From b9af61ca980a423a405788b41010fc053a77a630 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 24 Jul 2026 13:25:50 -0600 Subject: [PATCH 001/222] Fix Kappa Four zero-kappa formulas --- .../Distributions/Univariate/KappaFour.cs | 6 +- .../Univariate/Test_KappaFour.cs | 85 +++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/Numerics/Distributions/Univariate/KappaFour.cs b/Numerics/Distributions/Univariate/KappaFour.cs index 8b907c32..59b8a1b8 100644 --- a/Numerics/Distributions/Univariate/KappaFour.cs +++ b/Numerics/Distributions/Univariate/KappaFour.cs @@ -727,6 +727,10 @@ public override double PDF(double x) double F = CDF(x); double y = (x - Xi) / Alpha; + if (Kappa == 0d) + { + return Math.Exp(-y) / Alpha * Math.Pow(F, 1d - Hondo); + } double yy = 1 - Kappa * y; return (1 / Alpha) * Math.Pow(yy, 1 / Kappa - 1) * Math.Pow(F, 1 - Hondo); } @@ -781,7 +785,7 @@ public override double InverseCDF(double probability) } else if (Kappa == 0 && Hondo != 0) { - return Xi - Alpha * Math.Log(1 - Math.Pow(probability, Hondo) / Hondo); + return Xi - Alpha * Math.Log((1 - Math.Pow(probability, Hondo)) / Hondo); } else { diff --git a/Test_Numerics/Distributions/Univariate/Test_KappaFour.cs b/Test_Numerics/Distributions/Univariate/Test_KappaFour.cs index f48594a8..68acabe4 100644 --- a/Test_Numerics/Distributions/Univariate/Test_KappaFour.cs +++ b/Test_Numerics/Distributions/Univariate/Test_KappaFour.cs @@ -1,6 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Distributions; using Numerics.Mathematics; +using Numerics.Mathematics.Integration; namespace Distributions.Univariate { @@ -104,6 +105,90 @@ public void Test_K4_Dist() } + /// + /// Verifies the zero-kappa density against the analytical CDF derivative. + /// + [TestMethod] + public void Test_K4_ZeroKappa_PDFMatchesAnalyticalDerivative() + { + const double xi = 2.5d; + const double alpha = 1.75d; + const double x = 4.0d; + foreach (double hondo in new[] { -0.2d, 0d, 0.2d }) + { + var distribution = new KappaFour(xi, alpha, 0d, hondo); + double standardizedValue = (x - xi) / alpha; + double expected = Math.Exp(-standardizedValue) / alpha + * Math.Pow(distribution.CDF(x), 1d - hondo); + + Assert.AreEqual(expected, distribution.PDF(x), 1E-12d); + } + } + + /// + /// Verifies the zero-kappa inverse CDF algebra and CDF round trip. + /// + [TestMethod] + public void Test_K4_ZeroKappa_InverseCDFMatchesAnalyticalSolution() + { + const double xi = 2.5d; + const double alpha = 1.75d; + foreach (double hondo in new[] { -0.2d, 0.2d }) + { + var distribution = new KappaFour(xi, alpha, 0d, hondo); + foreach (double probability in new[] { 0.1d, 0.5d, 0.9d }) + { + double expected = xi - alpha + * Math.Log((1d - Math.Pow(probability, hondo)) / hondo); + double quantile = distribution.InverseCDF(probability); + + Assert.AreEqual(expected, quantile, 1E-12d); + Assert.AreEqual(probability, distribution.CDF(quantile), 1E-12d); + } + } + } + + /// + /// Verifies normalization and support for the positive-hondo zero-kappa case. + /// + [TestMethod] + public void Test_K4_ZeroKappa_PositiveHondoHasNormalizedSupportedDensity() + { + var distribution = new KappaFour(2.5d, 1.75d, 0d, 0.2d); + const double lowerProbability = 1E-9d; + const double upperProbability = 1d - lowerProbability; + double lower = distribution.InverseCDF(lowerProbability); + double upper = distribution.InverseCDF(upperProbability); + var integrator = new AdaptiveGaussKronrod(distribution.PDF, lower, upper); + + integrator.Integrate(); + + Assert.AreEqual(2.5d + 1.75d * Math.Log(0.2d), distribution.Minimum, 1E-12d); + Assert.AreEqual(0d, distribution.PDF(distribution.Minimum - 1E-6d)); + Assert.AreEqual(upperProbability - lowerProbability, integrator.Result, 1E-8d); + } + + /// + /// Verifies continuity of the general Kappa Four formulas as kappa approaches zero. + /// + [TestMethod] + public void Test_K4_GeneralFormulaIsContinuousAtZeroKappa() + { + const double xi = 2.5d; + const double alpha = 1.75d; + const double hondo = 0.2d; + const double x = 4.0d; + const double probability = 0.7d; + var limit = new KappaFour(xi, alpha, 0d, hondo); + + foreach (double kappa in new[] { -1E-7d, 1E-7d }) + { + var nearby = new KappaFour(xi, alpha, kappa, hondo); + Assert.AreEqual(limit.CDF(x), nearby.CDF(x), 1E-7d); + Assert.AreEqual(limit.PDF(x), nearby.PDF(x), 1E-7d); + Assert.AreEqual(limit.InverseCDF(probability), nearby.InverseCDF(probability), 1E-6d); + } + } /// /// Verifies Kappa Four partial derivative calculations. /// From d0bab1cb56f2bde012de715f37e878149b958b9b Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 24 Jul 2026 13:59:11 -0600 Subject: [PATCH 002/222] Fix parameter-adjusted RMSE residual sum --- Numerics/Data/Statistics/GoodnessOfFit.cs | 18 ++++++---- .../Data/Statistics/Test_GoodnessOfFit.cs | 36 +++++++++++++++++-- 2 files changed, 45 insertions(+), 9 deletions(-) diff --git a/Numerics/Data/Statistics/GoodnessOfFit.cs b/Numerics/Data/Statistics/GoodnessOfFit.cs index c07b799f..d5264789 100644 --- a/Numerics/Data/Statistics/GoodnessOfFit.cs +++ b/Numerics/Data/Statistics/GoodnessOfFit.cs @@ -153,10 +153,12 @@ public static double[] AICWeights(IList aicValues) /// The list of modeled values to compare against the observed values. /// Number of model parameters. Default = 0. /// The RMSE of the model. + /// Thrown when the lists have different lengths or when is negative or is not less than the observation count. /// - /// RMSE is the square root of the average of squared differences between observed and modeled values. - /// It is sensitive to large errors due to the squaring operation and is expressed in the same units - /// as the observed data. Lower values indicate better model performance. + /// The numerator contains the squared residual from every observation. When parameters were estimated, + /// the denominator is adjusted to the residual degrees of freedom, n - k. The metric is sensitive + /// to large errors due to the squaring operation and is expressed in the same units as the observed data. + /// Lower values indicate better model performance. /// public static double RMSE(IList observedValues, IList modeledValues, int k = 0) { @@ -164,11 +166,15 @@ public static double RMSE(IList observedValues, IList modeledVal if (observedValues.Count != modeledValues.Count) throw new ArgumentOutOfRangeException(nameof(observedValues), "The number of observed values must equal the number of modeled values."); - int n = observedValues.Count - k; + int observationCount = observedValues.Count; + if (k < 0 || k >= observationCount) + throw new ArgumentOutOfRangeException(nameof(k), "The number of model parameters must be nonnegative and less than the number of observations."); + + int degreesOfFreedom = observationCount - k; double sse = 0d; - for (int i = 0; i < n; i++) + for (int i = 0; i < observationCount; i++) sse += Tools.Sqr(modeledValues[i] - observedValues[i]); - return Math.Sqrt(sse / n); + return Math.Sqrt(sse / degreesOfFreedom); } /// diff --git a/Test_Numerics/Data/Statistics/Test_GoodnessOfFit.cs b/Test_Numerics/Data/Statistics/Test_GoodnessOfFit.cs index 9715104d..997892ee 100644 --- a/Test_Numerics/Data/Statistics/Test_GoodnessOfFit.cs +++ b/Test_Numerics/Data/Statistics/Test_GoodnessOfFit.cs @@ -130,7 +130,7 @@ public void Test_RMSE1() { var observed = new double[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 }; double RMSE = GoodnessOfFit.RMSE(observed, data, 2); - double trueRMSE = 83.8037180707237; + double trueRMSE = 87.6252835187426; Assert.AreEqual(trueRMSE, RMSE, 1E-6); } @@ -144,7 +144,7 @@ public void Test_RMSE2() { var observed = new double[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 }; double RMSE = GoodnessOfFit.RMSE(observed, norm); - double trueRMSE = 83.8037180707237; + double trueRMSE = 87.6252835187426; Assert.AreEqual(trueRMSE, RMSE, 1E-6); } @@ -160,11 +160,41 @@ public void Test_RMSE3() var observed = new double[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 }; var pp = PlottingPositions.Weibull(observed.Length); double RMSE = GoodnessOfFit.RMSE(observed, pp, norm); - double trueRMSE = 83.8037180707237; + double trueRMSE = 87.6252835187426; Assert.AreEqual(trueRMSE, RMSE, 1E-6); } + /// + /// Verifies that parameter adjustment changes only the denominator and that paired row order does not affect RMSE. + /// + [TestMethod] + public void Test_ParameterAdjustedRMSEUsesAllResiduals() + { + double[] observed = [0d, 0d, 0d, 0d]; + double[] modeled = [1d, 2d, 3d, 4d]; + double[] permutedModeled = [4d, 1d, 2d, 3d]; + double expected = Math.Sqrt((1d + 4d + 9d + 16d) / 3d); + + double actual = GoodnessOfFit.RMSE(observed, modeled, 1); + double permutedActual = GoodnessOfFit.RMSE(observed, permutedModeled, 1); + + Assert.AreEqual(expected, actual, 1E-12); + Assert.AreEqual(expected, permutedActual, 1E-12); + } + + /// + /// Verifies that RMSE rejects parameter counts that do not leave positive residual degrees of freedom. + /// + [TestMethod] + public void Test_ParameterAdjustedRMSERejectsInvalidParameterCount() + { + double[] values = [1d, 2d]; + + Assert.Throws(() => GoodnessOfFit.RMSE(values, values, -1)); + Assert.Throws(() => GoodnessOfFit.RMSE(values, values, values.Length)); + } + /// /// Test the method for weighting RMSE values. Validation values were derived directly from the formula /// for inverse-MSE weighting (the method used by this function). From 48c99a2695df7f93137153cdae5b0fab80f3e058 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 24 Jul 2026 14:12:17 -0600 Subject: [PATCH 003/222] Add Kappa Four finite-shape regression --- .../Univariate/Test_KappaFour.cs | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/Test_Numerics/Distributions/Univariate/Test_KappaFour.cs b/Test_Numerics/Distributions/Univariate/Test_KappaFour.cs index 68acabe4..cdd39df0 100644 --- a/Test_Numerics/Distributions/Univariate/Test_KappaFour.cs +++ b/Test_Numerics/Distributions/Univariate/Test_KappaFour.cs @@ -1,4 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics; using Numerics.Distributions; using Numerics.Mathematics; using Numerics.Mathematics.Integration; @@ -189,6 +190,53 @@ public void Test_K4_GeneralFormulaIsContinuousAtZeroKappa() Assert.AreEqual(limit.InverseCDF(probability), nearby.InverseCDF(probability), 1E-6d); } } + + /// + /// Verifies that representative finite shape pairs are valid and define internally consistent supports. + /// + [TestMethod] + public void Test_K4_FiniteShapePairsHaveConsistentSupport() + { + (double Kappa, double Hondo)[] shapePairs = + [ + (-0.25d, -0.5d), + (-0.25d, 0.5d), + (0.25d, -0.5d), + (0.25d, 0.5d) + ]; + double[] probabilities = [1E-6d, 0.1d, 0.5d, 0.9d, 1d - 1E-6d]; + + foreach ((double kappa, double hondo) in shapePairs) + { + var distribution = new KappaFour(2.5d, 1.75d, kappa, hondo); + Assert.IsTrue(distribution.ParametersValid, $"Finite shape pair (kappa={kappa}, h={hondo}) must be admissible."); + + double previousQuantile = double.NegativeInfinity; + foreach (double probability in probabilities) + { + double quantile = distribution.InverseCDF(probability); + Assert.IsTrue(Tools.IsFinite(quantile)); + Assert.IsGreaterThan(previousQuantile, quantile); + Assert.AreEqual(probability, distribution.CDF(quantile), 1E-10d); + Assert.IsTrue(quantile >= distribution.Minimum && quantile <= distribution.Maximum); + previousQuantile = quantile; + } + + double medianDensity = distribution.PDF(distribution.InverseCDF(0.5d)); + Assert.IsTrue(Tools.IsFinite(medianDensity) && medianDensity > 0d); + if (Tools.IsFinite(distribution.Minimum)) + { + Assert.AreEqual(0d, distribution.CDF(distribution.Minimum)); + Assert.AreEqual(0d, distribution.PDF(distribution.Minimum - 1d)); + } + if (Tools.IsFinite(distribution.Maximum)) + { + Assert.AreEqual(1d, distribution.CDF(distribution.Maximum)); + Assert.AreEqual(0d, distribution.PDF(distribution.Maximum + 1d)); + } + } + } + /// /// Verifies Kappa Four partial derivative calculations. /// From 600e83a6b8bc65c96b812611feafbe8ac1c96fe2 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 25 Jul 2026 14:00:28 -0600 Subject: [PATCH 004/222] Add univariate function serialization and the function factory New append-only UnivariateFunctionType enum and UnivariateFunctionFactory (CreateFunction by type, CreateFromXElement dispatching on the element local name, and GetFunctionType for live instances), mirroring the LinkFunctionFactory and UnivariateDistributionFactory patterns. Concrete ToXElement instance methods with static FromXElement counterparts land on LinearFunction, PowerFunction, and TabularFunction - deliberately NOT on IUnivariateFunction: external implementors of the interface exist and net481 has no default interface members, so an interface member would be a breaking change. G17 invariant-culture doubles; ConfidenceLevel is runtime sampling state and never serializes; PowerFunction.Minimum derives from Xi and never serializes; TabularFunction embeds its UncertainOrderedPairedData via SaveToXElement. Round-trip and dispatch tests cover all three types and the factory guards. --- Numerics/Functions/LinearFunction.cs | 47 ++++++ Numerics/Functions/PowerFunction.cs | 51 ++++++ Numerics/Functions/TabularFunction.cs | 51 +++++- .../Functions/UnivariateFunctionFactory.cs | 95 +++++++++++ Numerics/Functions/UnivariateFunctionType.cs | 35 ++++ .../Test_UnivariateFunctionFactory.cs | 151 ++++++++++++++++++ 6 files changed, 429 insertions(+), 1 deletion(-) create mode 100644 Numerics/Functions/UnivariateFunctionFactory.cs create mode 100644 Numerics/Functions/UnivariateFunctionType.cs create mode 100644 Test_Numerics/Functions/Test_UnivariateFunctionFactory.cs diff --git a/Numerics/Functions/LinearFunction.cs b/Numerics/Functions/LinearFunction.cs index 3109a397..0b0a7a4e 100644 --- a/Numerics/Functions/LinearFunction.cs +++ b/Numerics/Functions/LinearFunction.cs @@ -1,6 +1,8 @@ using Numerics.Distributions; using System; using System.Collections.Generic; +using System.Globalization; +using System.Xml.Linq; namespace Numerics.Functions { @@ -193,5 +195,50 @@ public double InverseFunction(double y) return x; } + /// + /// Serializes the function's configuration to an XElement: the parameters α, β, and σ, + /// the deterministic flag, and the support bounds. is + /// runtime sampling state and is deliberately not serialized. + /// + /// An XElement representation of the linear function. + public XElement ToXElement() + { + var result = new XElement(nameof(LinearFunction)); + result.SetAttributeValue(nameof(Alpha), Alpha.ToString("G17", CultureInfo.InvariantCulture)); + result.SetAttributeValue(nameof(Beta), Beta.ToString("G17", CultureInfo.InvariantCulture)); + result.SetAttributeValue(nameof(Sigma), Sigma.ToString("G17", CultureInfo.InvariantCulture)); + result.SetAttributeValue(nameof(IsDeterministic), IsDeterministic.ToString()); + result.SetAttributeValue(nameof(Minimum), Minimum.ToString("G17", CultureInfo.InvariantCulture)); + result.SetAttributeValue(nameof(Maximum), Maximum.ToString("G17", CultureInfo.InvariantCulture)); + return result; + } + + /// + /// Deserializes a linear function from an XElement produced by . + /// Missing or unparseable attributes keep the default-constructed values. + /// + /// The XElement to deserialize. + /// A new . + /// Thrown when is null. + public static LinearFunction FromXElement(XElement xElement) + { + if (xElement == null) throw new ArgumentNullException(nameof(xElement)); + var function = new LinearFunction(); + // Set the deterministic flag first: parameter validation is gated on it. + if (bool.TryParse(xElement.Attribute(nameof(IsDeterministic))?.Value, out bool isDeterministic)) + function.IsDeterministic = isDeterministic; + if (double.TryParse(xElement.Attribute(nameof(Alpha))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double alpha)) + function.Alpha = alpha; + if (double.TryParse(xElement.Attribute(nameof(Beta))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double beta)) + function.Beta = beta; + if (double.TryParse(xElement.Attribute(nameof(Sigma))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double sigma)) + function.Sigma = sigma; + if (double.TryParse(xElement.Attribute(nameof(Minimum))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double minimum)) + function.Minimum = minimum; + if (double.TryParse(xElement.Attribute(nameof(Maximum))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double maximum)) + function.Maximum = maximum; + return function; + } + } } diff --git a/Numerics/Functions/PowerFunction.cs b/Numerics/Functions/PowerFunction.cs index 27492dbd..7f24fe87 100644 --- a/Numerics/Functions/PowerFunction.cs +++ b/Numerics/Functions/PowerFunction.cs @@ -1,6 +1,8 @@ using Numerics.Distributions; using System; using System.Collections.Generic; +using System.Globalization; +using System.Xml.Linq; namespace Numerics.Functions { @@ -248,5 +250,54 @@ public double InverseFunction(double y) return x; } + /// + /// Serializes the function's configuration to an XElement: the parameters α, β, ξ, and σ, + /// the deterministic and inverse flags, and the upper support bound. + /// is derived from ξ and is runtime + /// sampling state — neither is serialized. + /// + /// An XElement representation of the power function. + public XElement ToXElement() + { + var result = new XElement(nameof(PowerFunction)); + result.SetAttributeValue(nameof(Alpha), Alpha.ToString("G17", CultureInfo.InvariantCulture)); + result.SetAttributeValue(nameof(Beta), Beta.ToString("G17", CultureInfo.InvariantCulture)); + result.SetAttributeValue(nameof(Xi), Xi.ToString("G17", CultureInfo.InvariantCulture)); + result.SetAttributeValue(nameof(Sigma), Sigma.ToString("G17", CultureInfo.InvariantCulture)); + result.SetAttributeValue(nameof(IsDeterministic), IsDeterministic.ToString()); + result.SetAttributeValue(nameof(IsInverse), IsInverse.ToString()); + result.SetAttributeValue(nameof(Maximum), Maximum.ToString("G17", CultureInfo.InvariantCulture)); + return result; + } + + /// + /// Deserializes a power function from an XElement produced by . + /// Missing or unparseable attributes keep the default-constructed values. + /// + /// The XElement to deserialize. + /// A new . + /// Thrown when is null. + public static PowerFunction FromXElement(XElement xElement) + { + if (xElement == null) throw new ArgumentNullException(nameof(xElement)); + var function = new PowerFunction(); + // Set the deterministic flag first: parameter validation is gated on it. + if (bool.TryParse(xElement.Attribute(nameof(IsDeterministic))?.Value, out bool isDeterministic)) + function.IsDeterministic = isDeterministic; + if (bool.TryParse(xElement.Attribute(nameof(IsInverse))?.Value, out bool isInverse)) + function.IsInverse = isInverse; + if (double.TryParse(xElement.Attribute(nameof(Alpha))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double alpha)) + function.Alpha = alpha; + if (double.TryParse(xElement.Attribute(nameof(Beta))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double beta)) + function.Beta = beta; + if (double.TryParse(xElement.Attribute(nameof(Xi))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double xi)) + function.Xi = xi; + if (double.TryParse(xElement.Attribute(nameof(Sigma))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double sigma)) + function.Sigma = sigma; + if (double.TryParse(xElement.Attribute(nameof(Maximum))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double maximum)) + function.Maximum = maximum; + return function; + } + } } diff --git a/Numerics/Functions/TabularFunction.cs b/Numerics/Functions/TabularFunction.cs index 815c7623..5d4e6942 100644 --- a/Numerics/Functions/TabularFunction.cs +++ b/Numerics/Functions/TabularFunction.cs @@ -1,6 +1,8 @@ using Numerics.Data; using System; using System.Collections.Generic; +using System.Globalization; +using System.Xml.Linq; namespace Numerics.Functions { @@ -147,8 +149,55 @@ public double InverseFunction(double y) { // Validate parameters if (ParametersValid == false) ValidateParameters(new double[] { 0 }, true); - y = AllowNegativeYValues == false && (double.IsNaN(y) || y < 0) ? 0 : y; + y = AllowNegativeYValues == false && (double.IsNaN(y) || y < 0) ? 0 : y; return opd.GetXFromY(y, XTransform, YTransform); } + + /// + /// Serializes the function's configuration to an XElement: the embedded uncertain ordered + /// paired data plus the axis transforms, the negative-Y policy, and the support bounds. + /// is runtime sampling state and is deliberately not + /// serialized; derives from the table's distribution type. + /// + /// An XElement representation of the tabular function. + public XElement ToXElement() + { + var result = new XElement(nameof(TabularFunction)); + result.SetAttributeValue(nameof(XTransform), XTransform.ToString()); + result.SetAttributeValue(nameof(YTransform), YTransform.ToString()); + result.SetAttributeValue(nameof(AllowNegativeYValues), AllowNegativeYValues.ToString()); + result.SetAttributeValue(nameof(Minimum), Minimum.ToString("G17", CultureInfo.InvariantCulture)); + result.SetAttributeValue(nameof(Maximum), Maximum.ToString("G17", CultureInfo.InvariantCulture)); + result.Add(PairedData.SaveToXElement()); + return result; + } + + /// + /// Deserializes a tabular function from an XElement produced by . + /// + /// The XElement to deserialize. + /// A new . + /// Thrown when is null. + /// Thrown when the element carries no embedded uncertain ordered paired data. + public static TabularFunction FromXElement(XElement xElement) + { + if (xElement == null) throw new ArgumentNullException(nameof(xElement)); + var tableElement = xElement.Element("UncertainOrderedPairedData"); + if (tableElement == null) + throw new ArgumentException("The serialized tabular function is missing its embedded UncertainOrderedPairedData.", nameof(xElement)); + + var function = new TabularFunction(new UncertainOrderedPairedData(tableElement)); + if (Enum.TryParse(xElement.Attribute(nameof(XTransform))?.Value, out Transform xTransform)) + function.XTransform = xTransform; + if (Enum.TryParse(xElement.Attribute(nameof(YTransform))?.Value, out Transform yTransform)) + function.YTransform = yTransform; + if (bool.TryParse(xElement.Attribute(nameof(AllowNegativeYValues))?.Value, out bool allowNegative)) + function.AllowNegativeYValues = allowNegative; + if (double.TryParse(xElement.Attribute(nameof(Minimum))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double minimum)) + function.Minimum = minimum; + if (double.TryParse(xElement.Attribute(nameof(Maximum))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double maximum)) + function.Maximum = maximum; + return function; + } } } diff --git a/Numerics/Functions/UnivariateFunctionFactory.cs b/Numerics/Functions/UnivariateFunctionFactory.cs new file mode 100644 index 00000000..d5a69f27 --- /dev/null +++ b/Numerics/Functions/UnivariateFunctionFactory.cs @@ -0,0 +1,95 @@ +using System; +using System.Xml.Linq; + +namespace Numerics.Functions +{ + /// + /// Factory for creating instances from + /// enum values or from serialized + /// representations, and for mapping live instances back to their enum type. + /// + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + /// Mirrors the and + /// patterns: a closed + /// switch per surface, with serialization dispatch keyed on the element's local name (which + /// by contract is the concrete class name). The enum deliberately stays off + /// — external implementors of the interface exist, and the + /// net481 target has no default interface members, so the serialization surface lives on the + /// concrete classes (ToXElement() instance methods with static FromXElement + /// counterparts) and on this factory. + /// + /// + public static class UnivariateFunctionFactory + { + /// + /// Creates an instance of the specified type with its + /// default parameters. + /// + /// The univariate function type. + /// A new instance. + /// + /// Thrown for : a tabular function requires + /// uncertain ordered paired data and cannot be created without parameters. + /// + /// Thrown when is not a recognized function type. + public static IUnivariateFunction CreateFunction(UnivariateFunctionType type) + { + switch (type) + { + case UnivariateFunctionType.Linear: + return new LinearFunction(); + case UnivariateFunctionType.Power: + return new PowerFunction(); + case UnivariateFunctionType.Tabular: + throw new NotSupportedException("Function type " + type + " requires uncertain ordered paired data and cannot be created without parameters."); + default: + throw new ArgumentOutOfRangeException(nameof(type), type, "The function type is not defined."); + } + } + + /// + /// Creates an instance from a serialized + /// . The element name determines the function type. + /// + /// The XElement representing the function. The element name must match a known function class name. + /// A new instance. + /// Thrown when is null. + /// Thrown when the element name does not correspond to a known function type. + public static IUnivariateFunction CreateFromXElement(XElement xElement) + { + if (xElement == null) throw new ArgumentNullException(nameof(xElement)); + switch (xElement.Name.LocalName) + { + case nameof(LinearFunction): + return LinearFunction.FromXElement(xElement); + case nameof(PowerFunction): + return PowerFunction.FromXElement(xElement); + case nameof(TabularFunction): + return TabularFunction.FromXElement(xElement); + default: + throw new NotSupportedException("Unknown function type: '" + xElement.Name.LocalName + "'."); + } + } + + /// + /// Gets the of a live function instance. + /// + /// The function instance. + /// The function type. + /// Thrown when is null. + /// Thrown when the instance is not one of the library's concrete function types. + public static UnivariateFunctionType GetFunctionType(IUnivariateFunction function) + { + if (function == null) throw new ArgumentNullException(nameof(function)); + if (function is LinearFunction) return UnivariateFunctionType.Linear; + if (function is PowerFunction) return UnivariateFunctionType.Power; + if (function is TabularFunction) return UnivariateFunctionType.Tabular; + throw new NotSupportedException("The function type '" + function.GetType().Name + "' is not a library function type."); + } + } +} diff --git a/Numerics/Functions/UnivariateFunctionType.cs b/Numerics/Functions/UnivariateFunctionType.cs new file mode 100644 index 00000000..e78802b1 --- /dev/null +++ b/Numerics/Functions/UnivariateFunctionType.cs @@ -0,0 +1,35 @@ +namespace Numerics.Functions +{ + /// + /// The univariate function types. + /// + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + /// Members are append-only, like : + /// downstream serialization and factory dispatch key on these values, so existing members are + /// never renamed, reordered, or removed. + /// + /// + public enum UnivariateFunctionType + { + /// + /// The linear function Y = α + βX with optional additive Gaussian noise (). + /// + Linear, + + /// + /// The power function Y = α(X − ξ)^β with optional log-space Gaussian noise and an optional + /// inverse form (). + /// + Power, + + /// + /// The tabular (nonparametric) function over uncertain ordered paired data (). + /// + Tabular, + } +} diff --git a/Test_Numerics/Functions/Test_UnivariateFunctionFactory.cs b/Test_Numerics/Functions/Test_UnivariateFunctionFactory.cs new file mode 100644 index 00000000..6ac1ff8e --- /dev/null +++ b/Test_Numerics/Functions/Test_UnivariateFunctionFactory.cs @@ -0,0 +1,151 @@ +using System; +using System.Xml.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Data; +using Numerics.Distributions; +using Numerics.Functions; + +namespace Functions +{ + /// + /// Unit tests for the univariate function serialization surface: the + /// enum, the + /// dispatch, and the per-class ToXElement/FromXElement round-trips. + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + [TestClass] + public class Test_UnivariateFunctionFactory + { + /// + /// Test that the factory creates default instances by type, refuses the parameterless + /// tabular creation, and rejects undefined types. + /// + [TestMethod] + public void Test_CreateFunction_ByType() + { + var linear = UnivariateFunctionFactory.CreateFunction(UnivariateFunctionType.Linear); + Assert.IsInstanceOfType(linear, typeof(LinearFunction)); + var power = UnivariateFunctionFactory.CreateFunction(UnivariateFunctionType.Power); + Assert.IsInstanceOfType(power, typeof(PowerFunction)); + + Assert.Throws(() => UnivariateFunctionFactory.CreateFunction(UnivariateFunctionType.Tabular)); + Assert.Throws(() => UnivariateFunctionFactory.CreateFunction((UnivariateFunctionType)999)); + } + + /// + /// Test that the factory maps live instances back to their enum type and rejects + /// non-library implementations of the interface. + /// + [TestMethod] + public void Test_GetFunctionType() + { + Assert.AreEqual(UnivariateFunctionType.Linear, UnivariateFunctionFactory.GetFunctionType(new LinearFunction())); + Assert.AreEqual(UnivariateFunctionType.Power, UnivariateFunctionFactory.GetFunctionType(new PowerFunction())); + var table = new UncertainOrderedPairedData( + new[] { new UncertainOrdinate(0d, new Deterministic(0d)), new UncertainOrdinate(1d, new Deterministic(1d)) }, + true, SortOrder.Ascending, false, SortOrder.None, UnivariateDistributionType.Deterministic); + Assert.AreEqual(UnivariateFunctionType.Tabular, UnivariateFunctionFactory.GetFunctionType(new TabularFunction(table))); + Assert.Throws(() => UnivariateFunctionFactory.GetFunctionType(null)); + } + + /// + /// Test the linear function XElement round-trip: every serialized member restores, the + /// runtime confidence level is not serialized, and evaluation matches at a fixed + /// percentile. + /// + [TestMethod] + public void Test_LinearFunction_RoundTrip() + { + var original = new LinearFunction(-2, 5, 3) { Minimum = -100, Maximum = 250 }; + var element = original.ToXElement(); + Assert.IsNull(element.Attribute("ConfidenceLevel"), "ConfidenceLevel is runtime state and must not serialize."); + + var restored = (LinearFunction)UnivariateFunctionFactory.CreateFromXElement(element); + Assert.AreEqual(original.Alpha, restored.Alpha, 0); + Assert.AreEqual(original.Beta, restored.Beta, 0); + Assert.AreEqual(original.Sigma, restored.Sigma, 0); + Assert.AreEqual(original.IsDeterministic, restored.IsDeterministic); + Assert.AreEqual(original.Minimum, restored.Minimum, 0); + Assert.AreEqual(original.Maximum, restored.Maximum, 0); + + original.ConfidenceLevel = 0.75; + restored.ConfidenceLevel = 0.75; + Assert.AreEqual(original.Function(6), restored.Function(6), 1E-12); + } + + /// + /// Test the power function XElement round-trip, including the inverse flag and the + /// derived minimum (ξ is serialized; Minimum is not). + /// + [TestMethod] + public void Test_PowerFunction_RoundTrip() + { + var original = new PowerFunction(5, 2, 1, 0.1) { IsInverse = true, Maximum = 5000 }; + var element = original.ToXElement(); + Assert.IsNull(element.Attribute("ConfidenceLevel"), "ConfidenceLevel is runtime state and must not serialize."); + Assert.IsNull(element.Attribute("Minimum"), "Minimum derives from Xi and must not serialize."); + + var restored = (PowerFunction)UnivariateFunctionFactory.CreateFromXElement(element); + Assert.AreEqual(original.Alpha, restored.Alpha, 0); + Assert.AreEqual(original.Beta, restored.Beta, 0); + Assert.AreEqual(original.Xi, restored.Xi, 0); + Assert.AreEqual(original.Sigma, restored.Sigma, 0); + Assert.AreEqual(original.IsDeterministic, restored.IsDeterministic); + Assert.AreEqual(original.IsInverse, restored.IsInverse); + Assert.AreEqual(original.Minimum, restored.Minimum, 0); + Assert.AreEqual(original.Maximum, restored.Maximum, 0); + + original.ConfidenceLevel = 0.75; + restored.ConfidenceLevel = 0.75; + Assert.AreEqual(original.Function(6), restored.Function(6), 1E-12); + } + + /// + /// Test the tabular function XElement round-trip: the embedded uncertain paired data, + /// the axis transforms, and the negative-Y policy all restore, and a missing table + /// throws. + /// + [TestMethod] + public void Test_TabularFunction_RoundTrip() + { + var table = new UncertainOrderedPairedData( + new[] { new UncertainOrdinate(1d, new Normal(10, 2)), new UncertainOrdinate(100d, new Normal(20, 2)) }, + true, SortOrder.Ascending, false, SortOrder.None, UnivariateDistributionType.Normal); + var original = new TabularFunction(table) + { + // Both axes carry positive hazard-scale values in this fixture, so both use the + // logarithmic transform (Normal-Z is reserved for probability-valued axes). + XTransform = Transform.Logarithmic, + YTransform = Transform.Logarithmic, + AllowNegativeYValues = false, + }; + + var restored = (TabularFunction)UnivariateFunctionFactory.CreateFromXElement(original.ToXElement()); + Assert.AreEqual(original.XTransform, restored.XTransform); + Assert.AreEqual(original.YTransform, restored.YTransform); + Assert.AreEqual(original.AllowNegativeYValues, restored.AllowNegativeYValues); + Assert.AreEqual(original.PairedData.Count, restored.PairedData.Count); + Assert.AreEqual(original.PairedData[1].X, restored.PairedData[1].X, 0); + Assert.AreEqual(original.PairedData[1].Y.Mean, restored.PairedData[1].Y.Mean, 0); + + original.ConfidenceLevel = 0.9; + restored.ConfidenceLevel = 0.9; + Assert.AreEqual(original.Function(50), restored.Function(50), 1E-12); + + Assert.Throws(() => TabularFunction.FromXElement(new XElement(nameof(TabularFunction)))); + } + + /// + /// Test that the factory rejects null and unknown serialized forms. + /// + [TestMethod] + public void Test_CreateFromXElement_Guards() + { + Assert.Throws(() => UnivariateFunctionFactory.CreateFromXElement(null)); + Assert.Throws(() => UnivariateFunctionFactory.CreateFromXElement(new XElement("BogusFunction"))); + } + } +} From 251dd8b5599b14f5e9ddae149eb58951c5a92238 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 25 Jul 2026 14:09:33 -0600 Subject: [PATCH 005/222] Add the BaRatin addition-mode SegmentedPowerFunction Q(h) = sum over controls of 10^(log10 alpha_k) (h - h_k)^beta_k for h > h_k, zero at and below the main-channel cease-to-flow stage h1, with a log10-space Gaussian residual applied at the confidence level - the exact body and stochastic form of the RMC-BestFit rating-curve Predict. The parameter-vector layout is the BestFit layout [h1, log10a1, b1, ..., sigma] (3 segments + 1), so a fitted posterior ParameterSet applies directly through SetParameters; breakpoints must be strictly ordered and exponents non-negative (the monotone-rating constraint the numeric Brent inverse relies on). One segment degenerates to PowerFunction. Parity constants in the tests were evaluated independently from the closed form; serialization rides the function factory (Minimum derives from h1 and ConfidenceLevel is runtime state - neither serializes). --- Numerics/Functions/SegmentedPowerFunction.cs | 390 ++++++++++++++++++ .../Functions/UnivariateFunctionFactory.cs | 5 + Numerics/Functions/UnivariateFunctionType.cs | 7 + .../Functions/Test_SegmentedPowerFunction.cs | 153 +++++++ 4 files changed, 555 insertions(+) create mode 100644 Numerics/Functions/SegmentedPowerFunction.cs create mode 100644 Test_Numerics/Functions/Test_SegmentedPowerFunction.cs diff --git a/Numerics/Functions/SegmentedPowerFunction.cs b/Numerics/Functions/SegmentedPowerFunction.cs new file mode 100644 index 00000000..6f43c001 --- /dev/null +++ b/Numerics/Functions/SegmentedPowerFunction.cs @@ -0,0 +1,390 @@ +using Numerics.Distributions; +using Numerics.Mathematics.RootFinding; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Xml.Linq; + +namespace Numerics.Functions +{ + /// + /// A segmented power function in the BaRatin matrix-of-controls ADDITION mode: + /// Q(h) = Σₖ 10^(log₁₀αₖ) · (h − hₖ)^βₖ · 𝟙{h > hₖ}, with a log₁₀-space Gaussian residual σ. + /// + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + /// The parameter-vector layout is exactly the RMC-BestFit rating-curve layout + /// (RMC.BestFit/Models/RatingCurve/RatingCurve.cs), so a fitted posterior parameter + /// set applies directly through : + /// [h₁, log₁₀α₁, β₁, h₂, log₁₀α₂, β₂, …, σ] with length 3·segments + 1. Under + /// addition mode the BaRatin continuity derivation collapses each control's offset to its + /// activation stage (b_k = κ_k), the breakpoints must be strictly ordered + /// (h₁ < h₂ < …), and discharge is zero at and below the main-channel cease-to-flow + /// stage h₁. One segment degenerates to the plain power law + /// with α = 10^(log₁₀α₁), β = β₁, ξ = h₁. + /// + /// + /// The residual is log₁₀-space, matching the BestFit stochastic prediction: with a + /// confidence level u, Q(h) is multiplied by 10^(z) where z ~ N(0, σ) evaluated at u. The + /// numeric uses monotone bracketing with Brent's + /// method — the addition-mode sum is strictly increasing above h₁ for non-negative + /// exponents. + /// + /// + /// References: + /// Le Coz, J., Renard, B., Bonnifait, L., Branger, F., Le Boursicaud, R. (2014). Combining + /// hydraulic knowledge and uncertain gaugings in the estimation of hydrometric rating + /// curves: a Bayesian approach. J. Hydrol. 509:573–587. + /// + /// + [Serializable] + public class SegmentedPowerFunction : IUnivariateFunction + { + + /// + /// Construct a new deterministic single-segment power function with + /// h₁ = 0, log₁₀α₁ = 0, β₁ = 1.5, and σ = 0.1. + /// + public SegmentedPowerFunction() : this(1) + { + } + + /// + /// Construct a new deterministic segmented power function with default, strictly + /// ordered breakpoints (hₖ = k − 1), log₁₀αₖ = 0, βₖ = 1.5, and σ = 0.1. + /// + /// The number of segments (≥ 1). + /// Thrown when is less than one. + public SegmentedPowerFunction(int numberOfSegments) + { + if (numberOfSegments < 1) + throw new ArgumentOutOfRangeException(nameof(numberOfSegments), "The number of segments must be at least one."); + _numberOfSegments = numberOfSegments; + _parameters = new double[3 * numberOfSegments + 1]; + for (int k = 0; k < numberOfSegments; k++) + { + _parameters[3 * k] = k; + _parameters[3 * k + 1] = 0d; + _parameters[3 * k + 2] = 1.5d; + } + _parameters[_parameters.Length - 1] = 0.1d; + _normal.SetParameters(0d, _parameters[_parameters.Length - 1]); + IsDeterministic = true; + } + + /// + /// Construct a new segmented power function directly from a BestFit-layout parameter + /// vector. The segment count is inferred from the vector length (3·segments + 1), and + /// the function is stochastic (σ is the last entry). + /// + /// The parameter vector [h₁, log₁₀α₁, β₁, …, σ]. + /// Thrown when is null. + /// Thrown when the vector length is not 3·segments + 1 for a positive segment count. + public SegmentedPowerFunction(IList parameters) + { + if (parameters == null) throw new ArgumentNullException(nameof(parameters)); + if (parameters.Count < 4 || (parameters.Count - 1) % 3 != 0) + throw new ArgumentException("The parameter vector length must be 3·segments + 1.", nameof(parameters)); + _numberOfSegments = (parameters.Count - 1) / 3; + _parameters = new double[parameters.Count]; + IsDeterministic = false; + SetParameters(parameters); + } + + private bool _parametersValid = true; + private int _numberOfSegments; + private double[] _parameters; + private Normal _normal = new Normal(); + + /// + /// The number of power-law segments (controls). Fixed at construction so the parameter + /// vector length stays coherent. + /// + public int NumberOfSegments => _numberOfSegments; + + /// + /// The log₁₀-space standard error σ (the last parameter-vector entry). + /// + public double Sigma => _parameters[_parameters.Length - 1]; + + /// + public int NumberOfParameters => 3 * _numberOfSegments + 1; + + /// + public bool ParametersValid => _parametersValid; + + /// + public double Minimum + { + get { return _parameters[0]; } + set { throw new NotSupportedException("Minimum is derived from the first breakpoint h₁ and cannot be set directly."); } + } + + /// + public double Maximum { get; set; } = double.MaxValue; + + /// + public double[] MinimumOfParameters + { + get + { + var result = new double[NumberOfParameters]; + for (int k = 0; k < _numberOfSegments; k++) + { + result[3 * k] = double.MinValue; + result[3 * k + 1] = double.MinValue; + result[3 * k + 2] = 0d; + } + result[result.Length - 1] = 0d; + return result; + } + } + + /// + public double[] MaximumOfParameters + { + get + { + var result = new double[NumberOfParameters]; + for (int i = 0; i < result.Length; i++) result[i] = double.MaxValue; + return result; + } + } + + /// + public bool IsDeterministic { get; set; } + + /// + public double ConfidenceLevel { get; set; } = -1; + + /// + /// Gets the breakpoint (activation stage) hₖ of a one-based segment. + /// + /// The segment index (1..NumberOfSegments). + /// The breakpoint hₖ. + /// Thrown when the segment index is out of range. + public double GetBreakpoint(int segmentOneBased) + { + ValidateSegmentIndex(segmentOneBased); + return _parameters[3 * (segmentOneBased - 1)]; + } + + /// + /// Gets log₁₀(αₖ) of a one-based segment. + /// + /// The segment index (1..NumberOfSegments). + /// log₁₀(αₖ). + /// Thrown when the segment index is out of range. + public double GetLog10Alpha(int segmentOneBased) + { + ValidateSegmentIndex(segmentOneBased); + return _parameters[3 * (segmentOneBased - 1) + 1]; + } + + /// + /// Gets the exponent βₖ of a one-based segment. + /// + /// The segment index (1..NumberOfSegments). + /// βₖ. + /// Thrown when the segment index is out of range. + public double GetBeta(int segmentOneBased) + { + ValidateSegmentIndex(segmentOneBased); + return _parameters[3 * (segmentOneBased - 1) + 2]; + } + + /// + public void SetParameters(IList parameters) + { + // Validate parameters + _parametersValid = ValidateParameters(parameters, false) is null; + // Set parameters + for (int i = 0; i < _parameters.Length && i < parameters.Count; i++) + _parameters[i] = parameters[i]; + _normal.SetParameters(0d, _parameters[_parameters.Length - 1]); + } + + /// + public ArgumentOutOfRangeException? ValidateParameters(IList parameters, bool throwException) + { + if (parameters == null || parameters.Count != NumberOfParameters) + { + var error = new ArgumentOutOfRangeException(nameof(parameters), "The parameter vector length must be 3·segments + 1."); + if (throwException) throw error; + return error; + } + for (int i = 0; i < parameters.Count; i++) + { + if (!Tools.IsFinite(parameters[i])) + { + var error = new ArgumentOutOfRangeException(nameof(parameters), "All parameters must be finite."); + if (throwException) throw error; + return error; + } + } + for (int k = 0; k < (parameters.Count - 1) / 3; k++) + { + if (parameters[3 * k + 2] < 0) + { + var error = new ArgumentOutOfRangeException(nameof(parameters), "Exponents must be non-negative for a monotone rating."); + if (throwException) throw error; + return error; + } + if (k > 0 && parameters[3 * k] <= parameters[3 * (k - 1)]) + { + var error = new ArgumentOutOfRangeException(nameof(parameters), "Breakpoints must be strictly increasing (h₁ < h₂ < …)."); + if (throwException) throw error; + return error; + } + } + if (IsDeterministic == false && parameters[parameters.Count - 1] <= 0) + { + var error = new ArgumentOutOfRangeException(nameof(Sigma), "Standard error must be greater than zero."); + if (throwException) throw error; + return error; + } + return null!; + } + + /// + public double Function(double x) + { + // Validate parameters + if (_parametersValid == false) + ValidateParameters(_parameters, true); + + // Check support + if (x >= Maximum) x = Maximum; + double q = DeterministicFunction(x); + if (q <= 0d) return 0d; + + if (IsDeterministic == true || ConfidenceLevel < 0 || ConfidenceLevel > 1) + return q; + // Log₁₀-space residual, matching the BestFit stochastic prediction. + return q * Math.Pow(10d, _normal.InverseCDF(ConfidenceLevel)); + } + + /// + public double InverseFunction(double y) + { + // Validate parameters + if (_parametersValid == false) + ValidateParameters(_parameters, true); + + // Fold the residual out first: the stochastic curve is the deterministic curve + // scaled by 10^z, so the inverse divides before the monotone root find. + if (IsDeterministic == false && ConfidenceLevel >= 0 && ConfidenceLevel <= 1) + y /= Math.Pow(10d, _normal.InverseCDF(ConfidenceLevel)); + + double h1 = _parameters[0]; + if (y <= 0d) return h1; + + // Bracket the root: the addition-mode sum is strictly increasing above h₁, so + // expand the upper bound geometrically until it covers y (or the support cap). + double lower = h1; + double upper = _parameters[3 * (_numberOfSegments - 1)] + 1d; + if (upper <= lower) upper = lower + 1d; + while (DeterministicFunction(upper) < y) + { + if (upper >= Maximum) return Maximum; + double span = upper - h1; + upper = h1 + span * 2d; + if (upper > Maximum) upper = Maximum; + } + + double x = Brent.Solve(h => DeterministicFunction(h) - y, lower, upper); + if (x < h1) return h1; + if (x > Maximum) return Maximum; + return x; + } + + /// + /// Serializes the function's configuration to an XElement: the segment count, the + /// BestFit-layout parameter vector, the deterministic flag, and the upper support bound. + /// derives from h₁ and is runtime + /// sampling state — neither is serialized. + /// + /// An XElement representation of the segmented power function. + public XElement ToXElement() + { + var result = new XElement(nameof(SegmentedPowerFunction)); + result.SetAttributeValue(nameof(NumberOfSegments), NumberOfSegments.ToString(CultureInfo.InvariantCulture)); + var values = new string[_parameters.Length]; + for (int i = 0; i < _parameters.Length; i++) + values[i] = _parameters[i].ToString("G17", CultureInfo.InvariantCulture); + result.SetAttributeValue("Parameters", string.Join("|", values)); + result.SetAttributeValue(nameof(IsDeterministic), IsDeterministic.ToString()); + result.SetAttributeValue(nameof(Maximum), Maximum.ToString("G17", CultureInfo.InvariantCulture)); + return result; + } + + /// + /// Deserializes a segmented power function from an XElement produced by + /// . + /// + /// The XElement to deserialize. + /// A new . + /// Thrown when is null. + /// Thrown when the element carries no parseable parameter vector. + public static SegmentedPowerFunction FromXElement(XElement xElement) + { + if (xElement == null) throw new ArgumentNullException(nameof(xElement)); + string? text = xElement.Attribute("Parameters")?.Value; + if (string.IsNullOrEmpty(text)) + throw new ArgumentException("The serialized segmented power function is missing its parameter vector.", nameof(xElement)); + + string[] tokens = text!.Split('|'); + var parameters = new double[tokens.Length]; + for (int i = 0; i < tokens.Length; i++) + { + if (!double.TryParse(tokens[i], NumberStyles.Any, CultureInfo.InvariantCulture, out parameters[i])) + throw new ArgumentException("The serialized segmented power function carries an unparseable parameter value.", nameof(xElement)); + } + + var function = new SegmentedPowerFunction(parameters); + if (bool.TryParse(xElement.Attribute(nameof(IsDeterministic))?.Value, out bool isDeterministic)) + function.IsDeterministic = isDeterministic; + if (double.TryParse(xElement.Attribute(nameof(Maximum))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double maximum)) + function.Maximum = maximum; + return function; + } + + /// + /// The deterministic addition-mode discharge: the sum of the active controls' power + /// laws, zero at and below the main-channel cease-to-flow stage h₁ (the BestFit + /// Predict body). + /// + /// The stage. + /// The deterministic discharge. + private double DeterministicFunction(double x) + { + double depth1 = x - _parameters[0]; + if (depth1 <= 0d) return 0d; + + double q = Math.Pow(10d, _parameters[1]) * Math.Pow(depth1, _parameters[2]); + for (int k = 1; k < _numberOfSegments; k++) + { + double depth = x - _parameters[3 * k]; + if (depth > 0d) + q += Math.Pow(10d, _parameters[3 * k + 1]) * Math.Pow(depth, _parameters[3 * k + 2]); + } + return q; + } + + /// + /// Throws when a one-based segment index is out of range. + /// + /// The segment index. + /// Thrown when the segment index is out of range. + private void ValidateSegmentIndex(int segmentOneBased) + { + if (segmentOneBased < 1 || segmentOneBased > _numberOfSegments) + throw new ArgumentOutOfRangeException(nameof(segmentOneBased), "Segment index must be between 1 and " + _numberOfSegments + "."); + } + + } +} diff --git a/Numerics/Functions/UnivariateFunctionFactory.cs b/Numerics/Functions/UnivariateFunctionFactory.cs index d5a69f27..88ad5526 100644 --- a/Numerics/Functions/UnivariateFunctionFactory.cs +++ b/Numerics/Functions/UnivariateFunctionFactory.cs @@ -47,6 +47,8 @@ public static IUnivariateFunction CreateFunction(UnivariateFunctionType type) return new PowerFunction(); case UnivariateFunctionType.Tabular: throw new NotSupportedException("Function type " + type + " requires uncertain ordered paired data and cannot be created without parameters."); + case UnivariateFunctionType.SegmentedPower: + return new SegmentedPowerFunction(); default: throw new ArgumentOutOfRangeException(nameof(type), type, "The function type is not defined."); } @@ -71,6 +73,8 @@ public static IUnivariateFunction CreateFromXElement(XElement xElement) return PowerFunction.FromXElement(xElement); case nameof(TabularFunction): return TabularFunction.FromXElement(xElement); + case nameof(SegmentedPowerFunction): + return SegmentedPowerFunction.FromXElement(xElement); default: throw new NotSupportedException("Unknown function type: '" + xElement.Name.LocalName + "'."); } @@ -89,6 +93,7 @@ public static UnivariateFunctionType GetFunctionType(IUnivariateFunction functio if (function is LinearFunction) return UnivariateFunctionType.Linear; if (function is PowerFunction) return UnivariateFunctionType.Power; if (function is TabularFunction) return UnivariateFunctionType.Tabular; + if (function is SegmentedPowerFunction) return UnivariateFunctionType.SegmentedPower; throw new NotSupportedException("The function type '" + function.GetType().Name + "' is not a library function type."); } } diff --git a/Numerics/Functions/UnivariateFunctionType.cs b/Numerics/Functions/UnivariateFunctionType.cs index e78802b1..e01af730 100644 --- a/Numerics/Functions/UnivariateFunctionType.cs +++ b/Numerics/Functions/UnivariateFunctionType.cs @@ -31,5 +31,12 @@ public enum UnivariateFunctionType /// The tabular (nonparametric) function over uncertain ordered paired data (). /// Tabular, + + /// + /// The BaRatin addition-mode segmented power function + /// Q(h) = Σₖ αₖ(h − hₖ)^βₖ·𝟙{h > hₖ} with a log₁₀-space residual + /// (). + /// + SegmentedPower, } } diff --git a/Test_Numerics/Functions/Test_SegmentedPowerFunction.cs b/Test_Numerics/Functions/Test_SegmentedPowerFunction.cs new file mode 100644 index 00000000..53475a59 --- /dev/null +++ b/Test_Numerics/Functions/Test_SegmentedPowerFunction.cs @@ -0,0 +1,153 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Functions; + +namespace Functions +{ + /// + /// Unit tests for the BaRatin addition-mode : parity + /// with the RMC-BestFit rating-curve prediction, the single-segment degeneracy to + /// , the numeric inverse, the parameter-vector contract, and the + /// serialization round-trip. + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + [TestClass] + public class Test_SegmentedPowerFunction + { + /// + /// Test parity with the BestFit rating-curve prediction. The reference constants were + /// evaluated independently from the BaRatin addition-mode closed form + /// Q(h) = Σₖ 10^(log₁₀αₖ)(h − hₖ)^βₖ·𝟙{h > hₖ}, the exact body of + /// RMC.BestFit RatingCurve.Predict (Python evaluation, 2026-07-25). + /// + [TestMethod] + public void Test_BestFitParity() + { + // 1 segment: [h₁, log₁₀α₁, β₁, σ] = [1, 1.5, 2, 0.1] + var oneSegment = new SegmentedPowerFunction(new[] { 1d, 1.5d, 2d, 0.1d }) { IsDeterministic = true }; + Assert.AreEqual(0d, oneSegment.Function(0.5), 0d, "Discharge is zero at and below the cease-to-flow stage h₁."); + Assert.AreEqual(505.9644256269407, oneSegment.Function(5), 1E-10); + + // 2 segments: [1, 1.5, 2, 3, 1.2, 1.5, 0.1] — below and above the second activation. + var twoSegments = new SegmentedPowerFunction(new[] { 1d, 1.5d, 2d, 3d, 1.2d, 1.5d, 0.1d }) { IsDeterministic = true }; + Assert.AreEqual(71.15124735378853, twoSegments.Function(2.5), 1E-10, "Only the first control is active below h₂."); + Assert.AreEqual(550.7919745807667, twoSegments.Function(5), 1E-10, "Both controls add above h₂."); + + // 3 segments: [1, 1.5, 2, 3, 1.2, 1.5, 6, 0.8, 1.1, 0.15] + var threeSegments = new SegmentedPowerFunction(new[] { 1d, 1.5d, 2d, 3d, 1.2d, 1.5d, 6d, 0.8d, 1.1d, 0.15d }) { IsDeterministic = true }; + Assert.AreEqual(300.45392133976526, threeSegments.Function(4), 1E-10); + Assert.AreEqual(2277.915263028223, threeSegments.Function(9), 1E-9); + + // The log₁₀-space residual at a fixed confidence level multiplies the curve by + // 10^(z·σ): 550.79197… · 10^(0.674489750196…·0.1) = 643.3341101314571. + twoSegments.IsDeterministic = false; + twoSegments.ConfidenceLevel = 0.75; + Assert.AreEqual(643.3341101314571, twoSegments.Function(5), 1E-9); + } + + /// + /// Test that one segment degenerates to the plain power law: α = 10^(log₁₀α₁), + /// β = β₁, ξ = h₁. + /// + [TestMethod] + public void Test_OneSegment_DegeneratesToPowerFunction() + { + var segmented = new SegmentedPowerFunction(new[] { 2d, 0.7d, 1.8d, 0.1d }) { IsDeterministic = true }; + var power = new PowerFunction(Math.Pow(10d, 0.7d), 1.8d, 2d) { IsDeterministic = true }; + foreach (double stage in new[] { 2.5, 4.0, 10.0, 55.0 }) + { + Assert.AreEqual(power.Function(stage), segmented.Function(stage), 1E-10 * power.Function(stage)); + } + } + + /// + /// Test the numeric inverse: round-trips within and across segments, at a stochastic + /// confidence level, and the zero/edge policies. + /// + [TestMethod] + public void Test_InverseFunction_RoundTrip() + { + var function = new SegmentedPowerFunction(new[] { 1d, 1.5d, 2d, 3d, 1.2d, 1.5d, 0.1d }) { IsDeterministic = true }; + foreach (double stage in new[] { 1.5, 2.5, 3.5, 5.0, 12.0 }) + { + double q = function.Function(stage); + Assert.AreEqual(stage, function.InverseFunction(q), 1E-6, $"Deterministic inverse at stage {stage}."); + } + + // A stochastic confidence level folds out of the inverse exactly. + function.IsDeterministic = false; + function.ConfidenceLevel = 0.9; + double stochastic = function.Function(5.0); + Assert.AreEqual(5.0, function.InverseFunction(stochastic), 1E-6); + + // Non-positive discharge maps to the cease-to-flow stage. + Assert.AreEqual(1d, function.InverseFunction(0d), 0d); + Assert.AreEqual(1d, function.InverseFunction(-5d), 0d); + } + + /// + /// Test the parameter-vector contract: SetParameters applies a BestFit-layout posterior + /// draw directly, and the validation guards reject malformed vectors. + /// + [TestMethod] + public void Test_ParameterVector_Contract() + { + var function = new SegmentedPowerFunction(new[] { 1d, 1.5d, 2d, 3d, 1.2d, 1.5d, 0.1d }); + Assert.AreEqual(2, function.NumberOfSegments); + Assert.AreEqual(7, function.NumberOfParameters); + Assert.AreEqual(1d, function.GetBreakpoint(1), 0d); + Assert.AreEqual(1.2d, function.GetLog10Alpha(2), 0d); + Assert.AreEqual(1.5d, function.GetBeta(2), 0d); + Assert.AreEqual(0.1d, function.Sigma, 0d); + Assert.AreEqual(1d, function.Minimum, 0d, "Minimum derives from h₁."); + + // A posterior draw applies in place (same layout, same length). + function.SetParameters(new[] { 0.8d, 1.6d, 1.9d, 2.9d, 1.1d, 1.4d, 0.12d }); + Assert.IsTrue(function.ParametersValid); + Assert.AreEqual(0.8d, function.GetBreakpoint(1), 0d); + Assert.AreEqual(0.12d, function.Sigma, 0d); + + // Guards: wrong length, unordered breakpoints, negative exponents, non-positive σ. + Assert.IsNotNull(function.ValidateParameters(new[] { 1d, 1.5d, 2d }, false)); + Assert.IsNotNull(function.ValidateParameters(new[] { 3d, 1.5d, 2d, 1d, 1.2d, 1.5d, 0.1d }, false)); + Assert.IsNotNull(function.ValidateParameters(new[] { 1d, 1.5d, -2d, 3d, 1.2d, 1.5d, 0.1d }, false)); + Assert.IsNotNull(function.ValidateParameters(new[] { 1d, 1.5d, 2d, 3d, 1.2d, 1.5d, 0d }, false)); + Assert.Throws(() => new SegmentedPowerFunction(0)); + Assert.Throws(() => new SegmentedPowerFunction(new[] { 1d, 2d, 3d, 4d, 5d })); + } + + /// + /// Test the XElement round-trip through the factory, and the factory dispatch surfaces. + /// + [TestMethod] + public void Test_Serialization_RoundTrip() + { + var original = new SegmentedPowerFunction(new[] { 1d, 1.5d, 2d, 3d, 1.2d, 1.5d, 0.1d }) { Maximum = 500d }; + var element = original.ToXElement(); + Assert.IsNull(element.Attribute("ConfidenceLevel"), "ConfidenceLevel is runtime state and must not serialize."); + Assert.IsNull(element.Attribute("Minimum"), "Minimum derives from h₁ and must not serialize."); + + var restored = (SegmentedPowerFunction)UnivariateFunctionFactory.CreateFromXElement(element); + Assert.AreEqual(original.NumberOfSegments, restored.NumberOfSegments); + Assert.AreEqual(original.IsDeterministic, restored.IsDeterministic); + Assert.AreEqual(original.Maximum, restored.Maximum, 0d); + for (int k = 1; k <= original.NumberOfSegments; k++) + { + Assert.AreEqual(original.GetBreakpoint(k), restored.GetBreakpoint(k), 0d); + Assert.AreEqual(original.GetLog10Alpha(k), restored.GetLog10Alpha(k), 0d); + Assert.AreEqual(original.GetBeta(k), restored.GetBeta(k), 0d); + } + Assert.AreEqual(original.Sigma, restored.Sigma, 0d); + + original.ConfidenceLevel = 0.75; + restored.ConfidenceLevel = 0.75; + Assert.AreEqual(original.Function(5), restored.Function(5), 1E-12); + + Assert.AreEqual(UnivariateFunctionType.SegmentedPower, UnivariateFunctionFactory.GetFunctionType(original)); + Assert.IsInstanceOfType(UnivariateFunctionFactory.CreateFunction(UnivariateFunctionType.SegmentedPower), typeof(SegmentedPowerFunction)); + } + } +} From 32f3bb2ef8065d2843f5e05a5cdc18da61e7cd89 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 25 Jul 2026 14:14:58 -0600 Subject: [PATCH 006/222] Add the weighted CompositeFunction with average and mixture modes A weighted combination over IUnivariateFunction children: the pointwise weighted average, and a mixture whose single confidence-level draw selects a child by cumulative weight and re-scales the remainder as the child draw - deterministic composition sampling with no internal random source (the math behind composite risk input functions). Weights are the composite parameters (non-negative, sum to one); children own and re-validate their own parameters at evaluation, so the composite validity flag deliberately does not fold in the latent child-flag quirk where a deterministic two-argument LinearFunction reports invalid until first evaluation. Mean convention outside [0,1] evaluates the weighted average of child means in both modes; a multi-branch mixture reports nondeterministic even over deterministic children. Serialization embeds children through their own forms and reconstructs through the function factory (nesting supported). --- Numerics/Functions/CompositeFunction.cs | 438 ++++++++++++++++++ Numerics/Functions/CompositeFunctionMode.cs | 26 ++ .../Functions/UnivariateFunctionFactory.cs | 5 + Numerics/Functions/UnivariateFunctionType.cs | 6 + .../Functions/Test_CompositeFunction.cs | 158 +++++++ 5 files changed, 633 insertions(+) create mode 100644 Numerics/Functions/CompositeFunction.cs create mode 100644 Numerics/Functions/CompositeFunctionMode.cs create mode 100644 Test_Numerics/Functions/Test_CompositeFunction.cs diff --git a/Numerics/Functions/CompositeFunction.cs b/Numerics/Functions/CompositeFunction.cs new file mode 100644 index 00000000..5ca43fcd --- /dev/null +++ b/Numerics/Functions/CompositeFunction.cs @@ -0,0 +1,438 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Xml.Linq; + +namespace Numerics.Functions +{ + /// + /// A weighted combination of univariate functions with two modes: the pointwise weighted + /// average F(x) = Σ wᵢ·fᵢ(x), and a mixture where a single confidence-level draw selects a + /// child by cumulative weight and re-scales the remainder as the child's own draw. + /// + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + /// This is the shared math behind composite (e.g., day/night exposure) risk input + /// functions. Weights must be non-negative and sum to one. The mixture mode is driven by + /// one uniform: with confidence level u, the child i with cumulative weight bracketing u is + /// selected and evaluated at the re-scaled remainder (u − Σ w₍<i₎)/wᵢ — deterministic + /// composition sampling with no internal random source, so the same u always reproduces the + /// same curve. Outside [0, 1] (the mean convention shared by the other function types), + /// both modes evaluate the weighted average of the children's own mean evaluations. + /// + /// + /// The numeric assumes the composed function is + /// monotone in x (true when every child is monotone non-decreasing, the risk-function use); + /// the mixture inverse inverts the selected child directly. Serialization embeds each child + /// through its own serialized form and reconstructs through + /// , the + /// Mixture/CompetingRisks distribution idiom. + /// + /// + [Serializable] + public class CompositeFunction : IUnivariateFunction + { + + /// + /// Construct a new composite function over child functions with equal weights, in the + /// weighted-average mode. + /// + /// The child functions. + /// Thrown when is null. + /// Thrown when no child functions are supplied. + public CompositeFunction(IList functions) + { + if (functions == null) throw new ArgumentNullException(nameof(functions)); + if (functions.Count == 0) throw new ArgumentException("At least one child function is required.", nameof(functions)); + _functions = new IUnivariateFunction[functions.Count]; + _weights = new double[functions.Count]; + for (int i = 0; i < functions.Count; i++) + { + _functions[i] = functions[i]; + _weights[i] = 1d / functions.Count; + } + } + + /// + /// Construct a new composite function over child functions and weights, in the + /// weighted-average mode. + /// + /// The child functions. + /// The non-negative weights; must sum to one. + /// Thrown when either argument is null. + /// Thrown when the collections are empty or mismatched in length. + public CompositeFunction(IList functions, IList weights) + { + if (functions == null) throw new ArgumentNullException(nameof(functions)); + if (weights == null) throw new ArgumentNullException(nameof(weights)); + if (functions.Count == 0) throw new ArgumentException("At least one child function is required.", nameof(functions)); + if (functions.Count != weights.Count) throw new ArgumentException("The weight count must match the function count.", nameof(weights)); + _functions = new IUnivariateFunction[functions.Count]; + _weights = new double[functions.Count]; + for (int i = 0; i < functions.Count; i++) _functions[i] = functions[i]; + SetParameters(weights); + } + + private bool _parametersValid = true; + private IUnivariateFunction[] _functions; + private double[] _weights; + + /// + /// The combination mode. Default = weighted average. + /// + public CompositeFunctionMode Mode { get; set; } = CompositeFunctionMode.WeightedAverage; + + /// + /// The child functions. + /// + public IReadOnlyList Functions => _functions; + + /// + /// The child weights (non-negative, summing to one). + /// + public IReadOnlyList Weights => _weights; + + /// + /// The parameters are the child weights; children own their own parameters. + public int NumberOfParameters => _functions.Length; + + /// + /// + /// Reflects the composite's own parameters (the weights) only: children validate their + /// own parameters at evaluation time, exactly as they do standalone. + /// + public bool ParametersValid => _parametersValid; + + /// + /// Derived: the smallest child minimum. The setter is not supported. + public double Minimum + { + get + { + double minimum = double.MaxValue; + for (int i = 0; i < _functions.Length; i++) + { + if (_functions[i].Minimum < minimum) minimum = _functions[i].Minimum; + } + return minimum; + } + set { throw new NotSupportedException("Minimum is derived from the child functions and cannot be set directly."); } + } + + /// + /// Derived: the largest child maximum. The setter is not supported. + public double Maximum + { + get + { + double maximum = double.MinValue; + for (int i = 0; i < _functions.Length; i++) + { + if (_functions[i].Maximum > maximum) maximum = _functions[i].Maximum; + } + return maximum; + } + set { throw new NotSupportedException("Maximum is derived from the child functions and cannot be set directly."); } + } + + /// + public double[] MinimumOfParameters + { + get + { + var result = new double[_functions.Length]; + for (int i = 0; i < result.Length; i++) result[i] = 0d; + return result; + } + } + + /// + public double[] MaximumOfParameters + { + get + { + var result = new double[_functions.Length]; + for (int i = 0; i < result.Length; i++) result[i] = 1d; + return result; + } + } + + /// + /// + /// True only when every child is deterministic and, in the mixture mode, when at most + /// one child carries positive weight (a multi-branch mixture varies with the draw even + /// over deterministic children). The setter propagates the flag to every child. + /// + public bool IsDeterministic + { + get + { + for (int i = 0; i < _functions.Length; i++) + { + if (!_functions[i].IsDeterministic) return false; + } + if (Mode == CompositeFunctionMode.Mixture) + { + int reachable = 0; + for (int i = 0; i < _weights.Length; i++) + { + if (_weights[i] > 0d) reachable++; + } + if (reachable > 1) return false; + } + return true; + } + set + { + for (int i = 0; i < _functions.Length; i++) _functions[i].IsDeterministic = value; + } + } + + /// + public double ConfidenceLevel { get; set; } = -1; + + /// + /// The parameters are the child weights. + public void SetParameters(IList parameters) + { + // Validate parameters + _parametersValid = ValidateParameters(parameters, false) is null; + // Set parameters + for (int i = 0; i < _weights.Length && i < parameters.Count; i++) + _weights[i] = parameters[i]; + } + + /// + public ArgumentOutOfRangeException? ValidateParameters(IList parameters, bool throwException) + { + if (parameters == null || parameters.Count != _functions.Length) + { + var error = new ArgumentOutOfRangeException(nameof(parameters), "The weight count must match the child function count."); + if (throwException) throw error; + return error; + } + double sum = 0d; + for (int i = 0; i < parameters.Count; i++) + { + if (!Tools.IsFinite(parameters[i]) || parameters[i] < 0d) + { + var error = new ArgumentOutOfRangeException(nameof(parameters), "Weights must be non-negative and finite."); + if (throwException) throw error; + return error; + } + sum += parameters[i]; + } + if (Math.Abs(sum - 1d) > 1E-8) + { + var error = new ArgumentOutOfRangeException(nameof(parameters), "Weights must sum to one."); + if (throwException) throw error; + return error; + } + return null!; + } + + /// + public double Function(double x) + { + // Validate parameters + if (ParametersValid == false) + ValidateParameters(_weights, true); + + if (IsDeterministic == true || ConfidenceLevel < 0 || ConfidenceLevel > 1) + { + // The mean convention: the weighted average of the children's own mean + // evaluations (children left at their configured state). + double mean = 0d; + for (int i = 0; i < _functions.Length; i++) + mean += _weights[i] * _functions[i].Function(x); + return mean; + } + + if (Mode == CompositeFunctionMode.Mixture) + { + int index = SelectMixtureChild(ConfidenceLevel, out double remainder); + return EvaluateChildAt(index, remainder, x); + } + + // Weighted average: the composite's draw drives every child co-monotonically. + double result = 0d; + for (int i = 0; i < _functions.Length; i++) + result += _weights[i] * EvaluateChildAt(i, ConfidenceLevel, x); + return result; + } + + /// + public double InverseFunction(double y) + { + // Validate parameters + if (ParametersValid == false) + ValidateParameters(_weights, true); + + if (Mode == CompositeFunctionMode.Mixture && IsDeterministic == false && ConfidenceLevel >= 0 && ConfidenceLevel <= 1) + { + int index = SelectMixtureChild(ConfidenceLevel, out double remainder); + return InvertChildAt(index, remainder, y); + } + + // Weighted average (or the mean convention): numeric monotone bracketing over the + // composed forward evaluation. + double lower = Minimum; + double upper = Maximum; + if (double.IsInfinity(lower) || lower == double.MinValue) lower = -1d; + if (double.IsInfinity(upper) || upper == double.MaxValue) upper = 1d; + while (Function(lower) > y && Tools.IsFinite(lower)) lower = lower < 0 ? lower * 2d : (lower - 1d) * 2d; + while (Function(upper) < y && Tools.IsFinite(upper)) upper = upper > 0 ? upper * 2d : (upper + 1d) * 2d; + return Mathematics.RootFinding.Brent.Solve(h => Function(h) - y, lower, upper); + } + + /// + /// Serializes the composite's configuration to an XElement: the mode, the weights, and + /// each child's own serialized form. is runtime sampling + /// state and is deliberately not serialized. + /// + /// An XElement representation of the composite function. + public XElement ToXElement() + { + var result = new XElement(nameof(CompositeFunction)); + result.SetAttributeValue(nameof(Mode), Mode.ToString()); + var values = new string[_weights.Length]; + for (int i = 0; i < _weights.Length; i++) + values[i] = _weights[i].ToString("G17", CultureInfo.InvariantCulture); + result.SetAttributeValue(nameof(Weights), string.Join("|", values)); + var children = new XElement("Functions"); + for (int i = 0; i < _functions.Length; i++) + { + children.Add(SerializeChild(_functions[i])); + } + result.Add(children); + return result; + } + + /// + /// Deserializes a composite function from an XElement produced by + /// ; children reconstruct through the function factory. + /// + /// The XElement to deserialize. + /// A new . + /// Thrown when is null. + /// Thrown when the element carries no child functions or mismatched weights. + public static CompositeFunction FromXElement(XElement xElement) + { + if (xElement == null) throw new ArgumentNullException(nameof(xElement)); + var childrenElement = xElement.Element("Functions"); + if (childrenElement == null) + throw new ArgumentException("The serialized composite function is missing its child functions.", nameof(xElement)); + + var functions = new List(); + foreach (var child in childrenElement.Elements()) + { + functions.Add(UnivariateFunctionFactory.CreateFromXElement(child)); + } + if (functions.Count == 0) + throw new ArgumentException("The serialized composite function carries no child functions.", nameof(xElement)); + + var composite = new CompositeFunction(functions); + string? weightsText = xElement.Attribute(nameof(Weights))?.Value; + if (!string.IsNullOrEmpty(weightsText)) + { + string[] tokens = weightsText!.Split('|'); + if (tokens.Length != functions.Count) + throw new ArgumentException("The serialized composite function's weight count does not match its child count.", nameof(xElement)); + var weights = new double[tokens.Length]; + for (int i = 0; i < tokens.Length; i++) + { + if (!double.TryParse(tokens[i], NumberStyles.Any, CultureInfo.InvariantCulture, out weights[i])) + throw new ArgumentException("The serialized composite function carries an unparseable weight.", nameof(xElement)); + } + composite.SetParameters(weights); + } + if (Enum.TryParse(xElement.Attribute(nameof(Mode))?.Value, out CompositeFunctionMode mode)) + composite.Mode = mode; + return composite; + } + + /// + /// Selects the mixture child whose cumulative weight brackets the draw, and re-scales + /// the remainder as the child's own confidence level. + /// + /// The composite's uniform draw in [0, 1]. + /// The re-scaled child draw. + /// The selected child index. + private int SelectMixtureChild(double u, out double remainder) + { + double cumulative = 0d; + for (int i = 0; i < _weights.Length; i++) + { + if (_weights[i] <= 0d) continue; + if (u <= cumulative + _weights[i] || i == _weights.Length - 1) + { + remainder = (u - cumulative) / _weights[i]; + if (remainder < 0d) remainder = 0d; + if (remainder > 1d) remainder = 1d; + return i; + } + cumulative += _weights[i]; + } + // All-zero weights cannot validate; fall back to the last child defensively. + remainder = u; + return _weights.Length - 1; + } + + /// + /// Evaluates a child at a given confidence level without disturbing the child's + /// configured state (the level is restored after the call). + /// + /// The child index. + /// The confidence level to evaluate at. + /// The evaluation point. + /// The child's value. + private double EvaluateChildAt(int index, double confidenceLevel, double x) + { + var child = _functions[index]; + double restore = child.ConfidenceLevel; + child.ConfidenceLevel = confidenceLevel; + double value = child.Function(x); + child.ConfidenceLevel = restore; + return value; + } + + /// + /// Inverts a child at a given confidence level without disturbing the child's + /// configured state (the level is restored after the call). + /// + /// The child index. + /// The confidence level to invert at. + /// The value to invert. + /// The child's inverse. + private double InvertChildAt(int index, double confidenceLevel, double y) + { + var child = _functions[index]; + double restore = child.ConfidenceLevel; + child.ConfidenceLevel = confidenceLevel; + double value = child.InverseFunction(y); + child.ConfidenceLevel = restore; + return value; + } + + /// + /// Serializes a child through its concrete ToXElement method. + /// + /// The child function. + /// The child's serialized form. + /// Thrown when the child is not a library function type. + private static XElement SerializeChild(IUnivariateFunction function) + { + if (function is LinearFunction linear) return linear.ToXElement(); + if (function is PowerFunction power) return power.ToXElement(); + if (function is TabularFunction tabular) return tabular.ToXElement(); + if (function is SegmentedPowerFunction segmented) return segmented.ToXElement(); + if (function is CompositeFunction composite) return composite.ToXElement(); + throw new NotSupportedException("The child function type '" + function.GetType().Name + "' does not support serialization."); + } + + } +} diff --git a/Numerics/Functions/CompositeFunctionMode.cs b/Numerics/Functions/CompositeFunctionMode.cs new file mode 100644 index 00000000..e4185b3b --- /dev/null +++ b/Numerics/Functions/CompositeFunctionMode.cs @@ -0,0 +1,26 @@ +namespace Numerics.Functions +{ + /// + /// The combination modes of a . + /// + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + public enum CompositeFunctionMode + { + /// + /// The pointwise weighted average of the child functions: F(x) = Σ wᵢ·fᵢ(x). + /// + WeightedAverage, + + /// + /// A mixture: one confidence-level draw selects a child by cumulative weight and + /// re-scales the remainder as the child's own confidence level, so a single uniform + /// drives the whole composition (no internal random source). + /// + Mixture, + } +} diff --git a/Numerics/Functions/UnivariateFunctionFactory.cs b/Numerics/Functions/UnivariateFunctionFactory.cs index 88ad5526..16b43328 100644 --- a/Numerics/Functions/UnivariateFunctionFactory.cs +++ b/Numerics/Functions/UnivariateFunctionFactory.cs @@ -49,6 +49,8 @@ public static IUnivariateFunction CreateFunction(UnivariateFunctionType type) throw new NotSupportedException("Function type " + type + " requires uncertain ordered paired data and cannot be created without parameters."); case UnivariateFunctionType.SegmentedPower: return new SegmentedPowerFunction(); + case UnivariateFunctionType.Composite: + throw new NotSupportedException("Function type " + type + " requires child functions and cannot be created without parameters."); default: throw new ArgumentOutOfRangeException(nameof(type), type, "The function type is not defined."); } @@ -75,6 +77,8 @@ public static IUnivariateFunction CreateFromXElement(XElement xElement) return TabularFunction.FromXElement(xElement); case nameof(SegmentedPowerFunction): return SegmentedPowerFunction.FromXElement(xElement); + case nameof(CompositeFunction): + return CompositeFunction.FromXElement(xElement); default: throw new NotSupportedException("Unknown function type: '" + xElement.Name.LocalName + "'."); } @@ -94,6 +98,7 @@ public static UnivariateFunctionType GetFunctionType(IUnivariateFunction functio if (function is PowerFunction) return UnivariateFunctionType.Power; if (function is TabularFunction) return UnivariateFunctionType.Tabular; if (function is SegmentedPowerFunction) return UnivariateFunctionType.SegmentedPower; + if (function is CompositeFunction) return UnivariateFunctionType.Composite; throw new NotSupportedException("The function type '" + function.GetType().Name + "' is not a library function type."); } } diff --git a/Numerics/Functions/UnivariateFunctionType.cs b/Numerics/Functions/UnivariateFunctionType.cs index e01af730..02563177 100644 --- a/Numerics/Functions/UnivariateFunctionType.cs +++ b/Numerics/Functions/UnivariateFunctionType.cs @@ -38,5 +38,11 @@ public enum UnivariateFunctionType /// (). /// SegmentedPower, + + /// + /// A weighted combination of child functions with weighted-average and mixture modes + /// (). + /// + Composite, } } diff --git a/Test_Numerics/Functions/Test_CompositeFunction.cs b/Test_Numerics/Functions/Test_CompositeFunction.cs new file mode 100644 index 00000000..8eceecaa --- /dev/null +++ b/Test_Numerics/Functions/Test_CompositeFunction.cs @@ -0,0 +1,158 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Functions; + +namespace Functions +{ + /// + /// Unit tests for : the weighted-average and mixture modes, + /// the single-uniform mixture composition, the deterministic semantics, weight validation, + /// the numeric inverse, and the serialization round-trip (including nesting). + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + [TestClass] + public class Test_CompositeFunction + { + /// + /// Test the weighted-average mode over deterministic children: + /// 0.25·(2x) + 0.75·(4x + 10) = 3.5x + 7.5 exactly. + /// + [TestMethod] + public void Test_WeightedAverage_Deterministic() + { + var composite = new CompositeFunction( + new IUnivariateFunction[] { new LinearFunction(0, 2), new LinearFunction(10, 4) }, + new[] { 0.25d, 0.75d }); + + foreach (double x in new[] { -3d, 0d, 5d, 42d }) + { + Assert.AreEqual(3.5d * x + 7.5d, composite.Function(x), 1E-12); + } + + // The numeric inverse round-trips the monotone composed curve. + Assert.AreEqual(5d, composite.InverseFunction(composite.Function(5d)), 1E-8); + } + + /// + /// Test the mixture composition: one uniform selects the child by cumulative weight and + /// re-scales the remainder as the child's own draw; the mean convention (a confidence + /// level outside [0, 1]) is the weighted average of the child means. + /// + [TestMethod] + public void Test_Mixture_SingleUniformComposition() + { + var composite = new CompositeFunction( + new IUnivariateFunction[] { new LinearFunction(0, 1), new LinearFunction(100, 1) }, + new[] { 0.4d, 0.6d }) + { + Mode = CompositeFunctionMode.Mixture, + }; + + // Below the first cumulative weight the first child is selected; above it, the second. + composite.ConfidenceLevel = 0.2; + Assert.AreEqual(5d, composite.Function(5d), 1E-12); + composite.ConfidenceLevel = 0.4; + Assert.AreEqual(5d, composite.Function(5d), 1E-12, "The bracket boundary belongs to the lower child."); + composite.ConfidenceLevel = 0.8; + Assert.AreEqual(105d, composite.Function(5d), 1E-12); + + // The mixture inverse inverts the selected child. + Assert.AreEqual(5d, composite.InverseFunction(105d), 1E-12); + + // The re-scaled remainder drives the selected child's own draw: with equal-weight + // uncertain children, u = 0.75 selects child 2 at remainder 0.5 — its median. + var uncertain = new CompositeFunction( + new IUnivariateFunction[] { new LinearFunction(0, 1, 2), new LinearFunction(0, 1, 2) }, + new[] { 0.5d, 0.5d }) + { + Mode = CompositeFunctionMode.Mixture, + ConfidenceLevel = 0.75, + }; + Assert.AreEqual(5d, uncertain.Function(5d), 1E-12, "Remainder 0.5 evaluates the selected child at its median."); + + // The mean convention: the weighted average of the child means. + composite.ConfidenceLevel = -1; + Assert.AreEqual(65d, composite.Function(5d), 1E-12); + } + + /// + /// Test the deterministic semantics: averages of deterministic children are + /// deterministic; a multi-branch mixture is not, even over deterministic children; a + /// single-reachable-branch mixture is. + /// + [TestMethod] + public void Test_IsDeterministic_Semantics() + { + var children = new IUnivariateFunction[] { new LinearFunction(0, 1), new LinearFunction(100, 1) }; + var average = new CompositeFunction(children, new[] { 0.5d, 0.5d }); + Assert.IsTrue(average.IsDeterministic); + + var mixture = new CompositeFunction(children, new[] { 0.5d, 0.5d }) { Mode = CompositeFunctionMode.Mixture }; + Assert.IsFalse(mixture.IsDeterministic, "A multi-branch mixture varies with the draw."); + + var degenerate = new CompositeFunction(children, new[] { 1d, 0d }) { Mode = CompositeFunctionMode.Mixture }; + Assert.IsTrue(degenerate.IsDeterministic, "A single reachable branch cannot vary."); + + var stochasticChild = new CompositeFunction( + new IUnivariateFunction[] { new LinearFunction(0, 1, 2) }, new[] { 1d }); + Assert.IsFalse(stochasticChild.IsDeterministic); + } + + /// + /// Test the weight validation guards: count mismatch, negatives, and sums away from one. + /// + [TestMethod] + public void Test_Weights_Validation() + { + var children = new IUnivariateFunction[] { new LinearFunction(0, 1), new LinearFunction(100, 1) }; + var composite = new CompositeFunction(children); + Assert.IsTrue(composite.ParametersValid, "Equal default weights are valid."); + + Assert.IsNotNull(composite.ValidateParameters(new[] { 0.5d }, false)); + Assert.IsNotNull(composite.ValidateParameters(new[] { -0.1d, 1.1d }, false)); + Assert.IsNotNull(composite.ValidateParameters(new[] { 0.3d, 0.3d }, false)); + Assert.IsNull(composite.ValidateParameters(new[] { 0.3d, 0.7d }, false)); + + Assert.Throws(() => new CompositeFunction(children, new[] { 0.5d })); + Assert.Throws(() => new CompositeFunction(Array.Empty())); + } + + /// + /// Test the XElement round-trip through the factory, including a nested composite child + /// and the factory dispatch surfaces. + /// + [TestMethod] + public void Test_Serialization_RoundTrip() + { + var inner = new CompositeFunction( + new IUnivariateFunction[] { new LinearFunction(0, 2), new PowerFunction(5, 2, 0) }, + new[] { 0.3d, 0.7d }); + var original = new CompositeFunction( + new IUnivariateFunction[] { inner, new LinearFunction(10, 4) }, + new[] { 0.6d, 0.4d }) + { + Mode = CompositeFunctionMode.Mixture, + }; + + var element = original.ToXElement(); + Assert.IsNull(element.Attribute("ConfidenceLevel"), "ConfidenceLevel is runtime state and must not serialize."); + + var restored = (CompositeFunction)UnivariateFunctionFactory.CreateFromXElement(element); + Assert.AreEqual(original.Mode, restored.Mode); + Assert.HasCount(original.Functions.Count, restored.Functions); + Assert.AreEqual(original.Weights[0], restored.Weights[0], 0d); + Assert.IsInstanceOfType(restored.Functions[0], typeof(CompositeFunction)); + Assert.IsInstanceOfType(restored.Functions[1], typeof(LinearFunction)); + + original.ConfidenceLevel = 0.3; + restored.ConfidenceLevel = 0.3; + Assert.AreEqual(original.Function(4d), restored.Function(4d), 1E-12); + + Assert.AreEqual(UnivariateFunctionType.Composite, UnivariateFunctionFactory.GetFunctionType(original)); + Assert.Throws(() => UnivariateFunctionFactory.CreateFunction(UnivariateFunctionType.Composite)); + } + } +} From c339d13a683aa56df20b2d7c08a66b1c00f23f4c Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 25 Jul 2026 14:17:42 -0600 Subject: [PATCH 007/222] Add the posterior EnsembleFunction with pure clone sampling A template function plus posterior ParameterSet draws, sampled by index (range-checked) or percentile (min(floor(u*N), N-1)) as fresh configured clones - pure by construction: the template is captured as an immutable serialized snapshot at construction and every sample re-materializes through the function factory before applying its parameter set, so concurrent realizations share no mutable state (the thread-safe alternative to mutating ConfidenceLevel/SetParameters inside Parallel.For hot loops; pinned by a parallel no-shared-mutation test). This is the vehicle for imported fitted functions (BestFit rating curves) carrying knowledge uncertainty into a simulation engine. Serialization embeds the template form plus every parameter set. --- Numerics/Functions/EnsembleFunction.cs | 195 ++++++++++++++++++ .../Functions/Test_EnsembleFunction.cs | 134 ++++++++++++ 2 files changed, 329 insertions(+) create mode 100644 Numerics/Functions/EnsembleFunction.cs create mode 100644 Test_Numerics/Functions/Test_EnsembleFunction.cs diff --git a/Numerics/Functions/EnsembleFunction.cs b/Numerics/Functions/EnsembleFunction.cs new file mode 100644 index 00000000..74a41fc6 --- /dev/null +++ b/Numerics/Functions/EnsembleFunction.cs @@ -0,0 +1,195 @@ +using Numerics.Mathematics.Optimization; +using System; +using System.Collections.Generic; +using System.Xml.Linq; + +namespace Numerics.Functions +{ + /// + /// A posterior ensemble of a univariate function: a template function plus an array of + /// posterior parameter sets, sampled by index or percentile as fresh configured clones. + /// + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + /// This is how an imported fitted function (e.g., a BestFit rating curve) carries knowledge + /// uncertainty into a simulation engine. Both sampling surfaces are pure: every call + /// returns a brand-new clone of the template configured with the selected parameter set, so + /// concurrent realizations never share mutable state — the thread-safe alternative to + /// mutating one instance's ConfidenceLevel/SetParameters inside a + /// Parallel.For hot loop. The template is captured as an immutable serialized + /// snapshot at construction (each clone re-materializes through + /// ), so later edits to + /// the original instance never leak into samples. + /// + /// + /// Percentile sampling maps u ∈ [0, 1] onto the index ladder as + /// min(⌊u·N⌋, N − 1) — the standard posterior-lookup convention. + /// + /// + [Serializable] + public class EnsembleFunction + { + + /// + /// Construct a new ensemble function from a template and its posterior parameter sets. + /// + /// The template function (a library function type). + /// The posterior parameter sets; each must carry one value per template parameter. + /// Thrown when either argument is null. + /// Thrown when no parameter sets are supplied, or a set's length does not match the template. + /// Thrown when the template is not a serializable library function type. + public EnsembleFunction(IUnivariateFunction template, IList parameterSets) + { + if (template == null) throw new ArgumentNullException(nameof(template)); + if (parameterSets == null) throw new ArgumentNullException(nameof(parameterSets)); + if (parameterSets.Count == 0) throw new ArgumentException("At least one parameter set is required.", nameof(parameterSets)); + + int expectedLength = template.NumberOfParameters; + _parameterSets = new ParameterSet[parameterSets.Count]; + for (int i = 0; i < parameterSets.Count; i++) + { + if (parameterSets[i].Values == null || parameterSets[i].Values.Length != expectedLength) + throw new ArgumentException("Parameter set " + i + " must carry exactly " + expectedLength + " values (one per template parameter).", nameof(parameterSets)); + _parameterSets[i] = parameterSets[i]; + } + + // The immutable template snapshot: parsing a private string per sample guarantees + // clones never share XML tree state across threads. + _templateXml = SerializeTemplate(template).ToString(SaveOptions.DisableFormatting); + } + + /// + /// Construct a new ensemble function from a serialized template element and its + /// posterior parameter sets (the deserialization path). + /// + /// The template's serialized form. + /// The posterior parameter sets. + /// Thrown when either argument is null. + /// Thrown when no parameter sets are supplied. + private EnsembleFunction(XElement templateElement, IList parameterSets) + { + if (templateElement == null) throw new ArgumentNullException(nameof(templateElement)); + if (parameterSets == null) throw new ArgumentNullException(nameof(parameterSets)); + if (parameterSets.Count == 0) throw new ArgumentException("At least one parameter set is required.", nameof(parameterSets)); + _parameterSets = new ParameterSet[parameterSets.Count]; + for (int i = 0; i < parameterSets.Count; i++) _parameterSets[i] = parameterSets[i]; + _templateXml = templateElement.ToString(SaveOptions.DisableFormatting); + } + + private readonly string _templateXml; + private readonly ParameterSet[] _parameterSets; + + /// + /// The number of posterior parameter sets. + /// + public int Count => _parameterSets.Length; + + /// + /// The posterior parameter sets. + /// + public IReadOnlyList ParameterSets => _parameterSets; + + /// + /// Samples the posterior by index: a fresh template clone configured with the indexed + /// parameter set. + /// + /// The posterior index in [0, Count). + /// A new configured function instance. + /// Thrown when is outside [0, Count). + public IUnivariateFunction Sample(int index) + { + if (index < 0 || index >= _parameterSets.Length) + throw new ArgumentOutOfRangeException(nameof(index), "The posterior index must be within [0, Count)."); + var clone = UnivariateFunctionFactory.CreateFromXElement(XElement.Parse(_templateXml)); + clone.SetParameters(_parameterSets[index].Values); + return clone; + } + + /// + /// Samples the posterior by percentile: u maps onto the index ladder as + /// min(⌊u·N⌋, N − 1). + /// + /// The percentile u in [0, 1]. + /// A new configured function instance. + /// Thrown when is outside [0, 1]. + public IUnivariateFunction Sample(double percentile) + { + if (percentile < 0d || percentile > 1d) + throw new ArgumentOutOfRangeException(nameof(percentile), "The percentile must be between 0 and 1."); + int index = (int)Math.Floor(percentile * _parameterSets.Length); + if (index > _parameterSets.Length - 1) index = _parameterSets.Length - 1; + return Sample(index); + } + + /// + /// Serializes the ensemble to an XElement: the template's serialized form plus every + /// posterior parameter set. + /// + /// An XElement representation of the ensemble. + public XElement ToXElement() + { + var result = new XElement(nameof(EnsembleFunction)); + var template = new XElement("Template"); + template.Add(XElement.Parse(_templateXml)); + result.Add(template); + var sets = new XElement("ParameterSets"); + for (int i = 0; i < _parameterSets.Length; i++) + sets.Add(_parameterSets[i].ToXElement()); + result.Add(sets); + return result; + } + + /// + /// Deserializes an ensemble from an XElement produced by . + /// + /// The XElement to deserialize. + /// A new . + /// Thrown when is null. + /// Thrown when the template or parameter sets are missing. + public static EnsembleFunction FromXElement(XElement xElement) + { + if (xElement == null) throw new ArgumentNullException(nameof(xElement)); + var templateElement = xElement.Element("Template"); + XElement? template = null; + if (templateElement != null) + { + foreach (var child in templateElement.Elements()) { template = child; break; } + } + if (template == null) + throw new ArgumentException("The serialized ensemble function is missing its template.", nameof(xElement)); + + var sets = new List(); + var setsElement = xElement.Element("ParameterSets"); + if (setsElement != null) + { + foreach (var child in setsElement.Elements()) + sets.Add(new ParameterSet(child)); + } + if (sets.Count == 0) + throw new ArgumentException("The serialized ensemble function carries no parameter sets.", nameof(xElement)); + + return new EnsembleFunction(template, sets); + } + + /// + /// Serializes a template through its concrete ToXElement method. + /// + /// The template function. + /// The template's serialized form. + /// Thrown when the template is not a library function type. + private static XElement SerializeTemplate(IUnivariateFunction function) + { + if (function is LinearFunction linear) return linear.ToXElement(); + if (function is PowerFunction power) return power.ToXElement(); + if (function is TabularFunction tabular) return tabular.ToXElement(); + if (function is SegmentedPowerFunction segmented) return segmented.ToXElement(); + if (function is CompositeFunction composite) return composite.ToXElement(); + throw new NotSupportedException("The template function type '" + function.GetType().Name + "' does not support serialization."); + } + + } +} diff --git a/Test_Numerics/Functions/Test_EnsembleFunction.cs b/Test_Numerics/Functions/Test_EnsembleFunction.cs new file mode 100644 index 00000000..f90c5396 --- /dev/null +++ b/Test_Numerics/Functions/Test_EnsembleFunction.cs @@ -0,0 +1,134 @@ +using System; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Functions; +using Numerics.Mathematics.Optimization; + +namespace Functions +{ + /// + /// Unit tests for : pure index and percentile sampling of + /// configured clones, clone independence from the template and each other, parallel + /// no-shared-mutation behavior, the guards, and the serialization round-trip. + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + [TestClass] + public class Test_EnsembleFunction + { + /// Builds a three-draw posterior over a segmented power template. + private static EnsembleFunction BuildEnsemble() + { + var template = new SegmentedPowerFunction(new[] { 1d, 1.5d, 2d, 0.1d }); + var sets = new[] + { + new ParameterSet(new[] { 1.0d, 1.5d, 2.0d, 0.10d }, 0), + new ParameterSet(new[] { 0.9d, 1.6d, 1.9d, 0.12d }, 0), + new ParameterSet(new[] { 1.1d, 1.4d, 2.1d, 0.08d }, 0), + }; + return new EnsembleFunction(template, sets); + } + + /// + /// Test index sampling: each draw returns a fresh clone configured with the indexed + /// parameter set, range violations throw, and clones are independent of each other and + /// of later samples. + /// + [TestMethod] + public void Test_Sample_ByIndex() + { + var ensemble = BuildEnsemble(); + Assert.AreEqual(3, ensemble.Count); + + var first = (SegmentedPowerFunction)ensemble.Sample(0); + var second = (SegmentedPowerFunction)ensemble.Sample(1); + Assert.AreEqual(1.0d, first.GetBreakpoint(1), 0); + Assert.AreEqual(0.9d, second.GetBreakpoint(1), 0); + Assert.AreEqual(0.12d, second.Sigma, 0); + Assert.AreNotSame(first, second); + + // Mutating one clone never touches another draw of the same index. + second.SetParameters(new[] { 5d, 5d, 5d, 5d }); + var secondAgain = (SegmentedPowerFunction)ensemble.Sample(1); + Assert.AreEqual(0.9d, secondAgain.GetBreakpoint(1), 0, "Clones must be independent."); + + Assert.Throws(() => ensemble.Sample(-1)); + Assert.Throws(() => ensemble.Sample(3)); + } + + /// + /// Test percentile sampling: u maps onto the index ladder as min(⌊u·N⌋, N − 1), and + /// out-of-range percentiles throw. + /// + [TestMethod] + public void Test_Sample_ByPercentile() + { + var ensemble = BuildEnsemble(); + Assert.AreEqual(1.0d, ((SegmentedPowerFunction)ensemble.Sample(0.0)).GetBreakpoint(1), 0); + Assert.AreEqual(0.9d, ((SegmentedPowerFunction)ensemble.Sample(0.5)).GetBreakpoint(1), 0, "u = 0.5 with N = 3 selects index 1."); + Assert.AreEqual(1.1d, ((SegmentedPowerFunction)ensemble.Sample(1.0)).GetBreakpoint(1), 0, "u = 1 clamps to the last index."); + Assert.Throws(() => ensemble.Sample(-0.1)); + Assert.Throws(() => ensemble.Sample(1.1)); + } + + /// + /// Test the thread-safety contract: concurrent sampling shares no mutable state, so + /// every parallel draw evaluates exactly its own parameter set. + /// + [TestMethod] + public void Test_Parallel_NoSharedMutation() + { + var ensemble = BuildEnsemble(); + double[] expected = { 1.0d, 0.9d, 1.1d }; + var failures = 0; + Parallel.For(0, 3000, i => + { + int index = i % 3; + var clone = (SegmentedPowerFunction)ensemble.Sample(index); + if (Math.Abs(clone.GetBreakpoint(1) - expected[index]) > 0d) + System.Threading.Interlocked.Increment(ref failures); + }); + Assert.AreEqual(0, failures, "Every parallel draw must carry exactly its own parameter set."); + } + + /// + /// Test the construction guards: null arguments, empty posteriors, length-mismatched + /// parameter sets, and non-library templates. + /// + [TestMethod] + public void Test_Construction_Guards() + { + var template = new LinearFunction(0, 1, 1); + var goodSet = new[] { new ParameterSet(new[] { 0d, 1d, 1d }, 0) }; + Assert.Throws(() => new EnsembleFunction(null, goodSet)); + Assert.Throws(() => new EnsembleFunction(template, null)); + Assert.Throws(() => new EnsembleFunction(template, Array.Empty())); + Assert.Throws(() => new EnsembleFunction(template, new[] { new ParameterSet(new[] { 0d, 1d }, 0) })); + } + + /// + /// Test the XElement round-trip: the template and every posterior draw restore, and + /// restored samples evaluate identically. + /// + [TestMethod] + public void Test_Serialization_RoundTrip() + { + var original = BuildEnsemble(); + var restored = EnsembleFunction.FromXElement(original.ToXElement()); + + Assert.AreEqual(original.Count, restored.Count); + for (int i = 0; i < original.Count; i++) + { + var a = original.Sample(i); + var b = restored.Sample(i); + a.ConfidenceLevel = 0.75; + b.ConfidenceLevel = 0.75; + Assert.AreEqual(a.Function(5d), b.Function(5d), 1E-12, $"Draw {i} must evaluate identically after the round-trip."); + } + + Assert.Throws(() => EnsembleFunction.FromXElement(new System.Xml.Linq.XElement(nameof(EnsembleFunction)))); + } + } +} From 0bb1e8939091a582b09e05eb204a622fcfffd093 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 25 Jul 2026 14:23:25 -0600 Subject: [PATCH 008/222] Fix the EmpiricalDistribution and KernelDensity XElement round-trips Both types reported parameter property names without scalar parameter values, so the inherited scalar-only ToXElement could not even complete - the X/probability tables and the kernel sample were lost entirely. Each now overrides ToXElement (tables and sample data as G17 invariant-culture lists, interpolation transforms, the probability sort order for exceedance-convention ladders, kernel type, bandwidth, and the optional per-sample weights) with a static FromXElement counterpart, wired into UnivariateDistributionFactory.CreateDistribution alongside the existing Mixture/CompetingRisks/PertPercentile special cases. Round-trip tests cover ascending and descending probability conventions, evaluation equality, weighted kernels, and the loud missing-table guards. --- .../Base/UnivariateDistributionFactory.cs | 8 ++ .../Univariate/EmpiricalDistribution.cs | 78 +++++++++++++ .../Distributions/Univariate/KernelDensity.cs | 94 ++++++++++++++- .../Test_DistributionXElementRoundTrips.cs | 109 ++++++++++++++++++ 4 files changed, 288 insertions(+), 1 deletion(-) create mode 100644 Test_Numerics/Distributions/Univariate/Test_DistributionXElementRoundTrips.cs diff --git a/Numerics/Distributions/Univariate/Base/UnivariateDistributionFactory.cs b/Numerics/Distributions/Univariate/Base/UnivariateDistributionFactory.cs index e1c6685d..938ecc67 100644 --- a/Numerics/Distributions/Univariate/Base/UnivariateDistributionFactory.cs +++ b/Numerics/Distributions/Univariate/Base/UnivariateDistributionFactory.cs @@ -193,6 +193,14 @@ public static UnivariateDistributionBase CreateDistribution(XElement xElement) { return PertPercentileZ.FromXElement(xElement)!; } + else if (type == UnivariateDistributionType.Empirical) + { + return EmpiricalDistribution.FromXElement(xElement); + } + else if (type == UnivariateDistributionType.KernelDensity) + { + return KernelDensity.FromXElement(xElement); + } } var dist = CreateDistribution(type); diff --git a/Numerics/Distributions/Univariate/EmpiricalDistribution.cs b/Numerics/Distributions/Univariate/EmpiricalDistribution.cs index a29353ef..973d279b 100644 --- a/Numerics/Distributions/Univariate/EmpiricalDistribution.cs +++ b/Numerics/Distributions/Univariate/EmpiricalDistribution.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Globalization; using System.Linq; +using System.Xml.Linq; using Numerics.Data; using Numerics.Data.Statistics; using Numerics.Mathematics; @@ -538,6 +540,82 @@ public override UnivariateDistributionBase Clone() return new EmpiricalDistribution(XValues, ProbabilityValues) { XTransform = XTransform, ProbabilityTransform = ProbabilityTransform }; } + /// + /// Serializes the empirical distribution to an XElement, overriding the base scalar-only + /// form: the X and probability tables (round-trip-exact "G17"), the probability sort + /// order, and both interpolation transforms. The base implementation writes only scalar + /// parameters, which lost the tables entirely. + /// + /// An XElement representation of the empirical distribution. + public override XElement ToXElement() + { + var result = new XElement("Distribution"); + result.SetAttributeValue(nameof(Type), Type.ToString()); + result.SetAttributeValue(nameof(XTransform), XTransform.ToString()); + result.SetAttributeValue(nameof(ProbabilityTransform), ProbabilityTransform.ToString()); + + var xValues = new string[XValues.Count]; + var pValues = new string[ProbabilityValues.Count]; + for (int i = 0; i < XValues.Count; i++) + xValues[i] = XValues[i].ToString("G17", CultureInfo.InvariantCulture); + for (int i = 0; i < ProbabilityValues.Count; i++) + pValues[i] = ProbabilityValues[i].ToString("G17", CultureInfo.InvariantCulture); + result.SetAttributeValue(nameof(XValues), string.Join("|", xValues)); + result.SetAttributeValue(nameof(ProbabilityValues), string.Join("|", pValues)); + + // The stored probability ladder may run ascending (non-exceedance) or descending + // (exceedance); record the order so deserialization restores the same convention. + var order = SortOrder.Ascending; + if (ProbabilityValues.Count > 1 && ProbabilityValues[0] > ProbabilityValues[ProbabilityValues.Count - 1]) + order = SortOrder.Descending; + result.SetAttributeValue("ProbabilityOrder", order.ToString()); + return result; + } + + /// + /// Deserializes an empirical distribution from an XElement produced by + /// . + /// + /// The XElement to deserialize. + /// A new . + /// Thrown when is null. + /// Thrown when the element carries no parseable X/probability tables. + public static EmpiricalDistribution FromXElement(XElement xElement) + { + if (xElement == null) throw new ArgumentNullException(nameof(xElement)); + string? xText = xElement.Attribute(nameof(XValues))?.Value; + string? pText = xElement.Attribute(nameof(ProbabilityValues))?.Value; + if (string.IsNullOrEmpty(xText) || string.IsNullOrEmpty(pText)) + throw new ArgumentException("The serialized empirical distribution is missing its X or probability table.", nameof(xElement)); + + string[] xTokens = xText!.Split('|'); + string[] pTokens = pText!.Split('|'); + if (xTokens.Length != pTokens.Length) + throw new ArgumentException("The serialized empirical distribution's X and probability tables differ in length.", nameof(xElement)); + + var xValues = new double[xTokens.Length]; + var pValues = new double[pTokens.Length]; + for (int i = 0; i < xTokens.Length; i++) + { + if (!double.TryParse(xTokens[i], NumberStyles.Any, CultureInfo.InvariantCulture, out xValues[i]) + || !double.TryParse(pTokens[i], NumberStyles.Any, CultureInfo.InvariantCulture, out pValues[i])) + { + throw new ArgumentException("The serialized empirical distribution carries an unparseable table value.", nameof(xElement)); + } + } + + var order = SortOrder.Ascending; + var orderAttr = xElement.Attribute("ProbabilityOrder"); + if (orderAttr != null) Enum.TryParse(orderAttr.Value, out order); + + var distribution = new EmpiricalDistribution(xValues, pValues, SortOrder.Ascending, order); + if (Enum.TryParse(xElement.Attribute(nameof(XTransform))?.Value, out Transform xTransform)) + distribution.XTransform = xTransform; + if (Enum.TryParse(xElement.Attribute(nameof(ProbabilityTransform))?.Value, out Transform probabilityTransform)) + distribution.ProbabilityTransform = probabilityTransform; + return distribution; + } + /// /// Convolves two empirical distributions using FFT. diff --git a/Numerics/Distributions/Univariate/KernelDensity.cs b/Numerics/Distributions/Univariate/KernelDensity.cs index fe577c55..a722ce53 100644 --- a/Numerics/Distributions/Univariate/KernelDensity.cs +++ b/Numerics/Distributions/Univariate/KernelDensity.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Globalization; using System.Linq; using System.Threading.Tasks; +using System.Xml.Linq; using Numerics.Data; using Numerics.Data.Statistics; using Numerics.Mathematics.Optimization; @@ -660,7 +662,97 @@ public override UnivariateDistributionBase Clone() }; } } - + + /// + /// Serializes the kernel density to an XElement, overriding the base scalar-only form: + /// the sample data (round-trip-exact "G17"), the kernel type, the bandwidth, the + /// optional per-sample weights, and both interpolation transforms. The base + /// implementation writes only scalar parameters, which lost the sample entirely. + /// + /// An XElement representation of the kernel density. + public override XElement ToXElement() + { + var result = new XElement("Distribution"); + result.SetAttributeValue(nameof(Type), Type.ToString()); + result.SetAttributeValue(nameof(KernelDistribution), KernelDistribution.ToString()); + result.SetAttributeValue(nameof(Bandwidth), Bandwidth.ToString("G17", CultureInfo.InvariantCulture)); + result.SetAttributeValue(nameof(XTransform), XTransform.ToString()); + result.SetAttributeValue(nameof(ProbabilityTransform), ProbabilityTransform.ToString()); + result.SetAttributeValue(nameof(BoundedByData), BoundedByData.ToString()); + + var samples = new string[SampleData.Count]; + for (int i = 0; i < SampleData.Count; i++) + samples[i] = SampleData[i].ToString("G17", CultureInfo.InvariantCulture); + result.SetAttributeValue(nameof(SampleData), string.Join("|", samples)); + + if (_weights != null) + { + var weights = new string[_weights.Length]; + for (int i = 0; i < _weights.Length; i++) + weights[i] = _weights[i].ToString("G17", CultureInfo.InvariantCulture); + result.SetAttributeValue("Weights", string.Join("|", weights)); + } + return result; + } + + /// + /// Deserializes a kernel density from an XElement produced by . + /// + /// The XElement to deserialize. + /// A new . + /// Thrown when is null. + /// Thrown when the element carries no parseable sample data. + public static KernelDensity FromXElement(XElement xElement) + { + if (xElement == null) throw new ArgumentNullException(nameof(xElement)); + string? sampleText = xElement.Attribute(nameof(SampleData))?.Value; + if (string.IsNullOrEmpty(sampleText)) + throw new ArgumentException("The serialized kernel density is missing its sample data.", nameof(xElement)); + + string[] tokens = sampleText!.Split('|'); + var samples = new double[tokens.Length]; + for (int i = 0; i < tokens.Length; i++) + { + if (!double.TryParse(tokens[i], NumberStyles.Any, CultureInfo.InvariantCulture, out samples[i])) + throw new ArgumentException("The serialized kernel density carries an unparseable sample value.", nameof(xElement)); + } + + var kernel = KernelType.Gaussian; + var kernelAttr = xElement.Attribute(nameof(KernelDistribution)); + if (kernelAttr != null) Enum.TryParse(kernelAttr.Value, out kernel); + + double bandwidth = 0d; + bool hasBandwidth = double.TryParse(xElement.Attribute(nameof(Bandwidth))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out bandwidth); + + KernelDensity distribution; + string? weightsText = xElement.Attribute("Weights")?.Value; + if (!string.IsNullOrEmpty(weightsText)) + { + string[] weightTokens = weightsText!.Split('|'); + if (weightTokens.Length != samples.Length) + throw new ArgumentException("The serialized kernel density's weight count does not match its sample count.", nameof(xElement)); + var weights = new double[weightTokens.Length]; + for (int i = 0; i < weightTokens.Length; i++) + { + if (!double.TryParse(weightTokens[i], NumberStyles.Any, CultureInfo.InvariantCulture, out weights[i])) + throw new ArgumentException("The serialized kernel density carries an unparseable weight value.", nameof(xElement)); + } + distribution = hasBandwidth ? new KernelDensity(samples, weights, kernel, bandwidth) : new KernelDensity(samples, weights, kernel); + } + else + { + distribution = hasBandwidth ? new KernelDensity(samples, kernel, bandwidth) : new KernelDensity(samples, kernel); + } + + if (Enum.TryParse(xElement.Attribute(nameof(XTransform))?.Value, out Transform xTransform)) + distribution.XTransform = xTransform; + if (Enum.TryParse(xElement.Attribute(nameof(ProbabilityTransform))?.Value, out Transform probabilityTransform)) + distribution.ProbabilityTransform = probabilityTransform; + if (bool.TryParse(xElement.Attribute(nameof(BoundedByData))?.Value, out bool bounded)) + distribution.BoundedByData = bounded; + return distribution; + } + /// /// Create the empirical CDF. /// diff --git a/Test_Numerics/Distributions/Univariate/Test_DistributionXElementRoundTrips.cs b/Test_Numerics/Distributions/Univariate/Test_DistributionXElementRoundTrips.cs new file mode 100644 index 00000000..a4ee5696 --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Test_DistributionXElementRoundTrips.cs @@ -0,0 +1,109 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Data; +using Numerics.Distributions; + +namespace Distributions.Univariate +{ + /// + /// Unit tests for the data-bearing distribution XElement round-trips: the + /// and overrides that fix + /// the base scalar-only serialization (their X/probability and sample tables were lost — + /// the inherited form could not even complete, because both types report parameter names + /// without scalar parameter values). + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + [TestClass] + public class Test_DistributionXElementRoundTrips + { + /// + /// Test the empirical distribution round-trip: the X/probability tables, the + /// transforms, and evaluation restore exactly, through both the direct FromXElement and + /// the distribution factory. + /// + [TestMethod] + public void Test_EmpiricalDistribution_RoundTrip() + { + var original = new EmpiricalDistribution( + new[] { 100d, 250d, 600d, 2000d }, + new[] { 0.05d, 0.4d, 0.8d, 0.99d }) + { + XTransform = Transform.Logarithmic, + ProbabilityTransform = Transform.NormalZ, + }; + + var element = original.ToXElement(); + var restored = (EmpiricalDistribution)UnivariateDistributionFactory.CreateDistribution(element); + + Assert.HasCount(original.XValues.Count, restored.XValues); + for (int i = 0; i < original.XValues.Count; i++) + { + Assert.AreEqual(original.XValues[i], restored.XValues[i], 0d); + Assert.AreEqual(original.ProbabilityValues[i], restored.ProbabilityValues[i], 0d); + } + Assert.AreEqual(original.XTransform, restored.XTransform); + Assert.AreEqual(original.ProbabilityTransform, restored.ProbabilityTransform); + Assert.AreEqual(original.CDF(500d), restored.CDF(500d), 1E-12); + Assert.AreEqual(original.InverseCDF(0.5d), restored.InverseCDF(0.5d), 1E-12); + + // A missing table is loud, never a silent scalar-only form. + Assert.Throws(() => EmpiricalDistribution.FromXElement(new System.Xml.Linq.XElement("Distribution"))); + } + + /// + /// Test that a descending (exceedance-convention) probability ladder round-trips with + /// its order preserved. + /// + [TestMethod] + public void Test_EmpiricalDistribution_DescendingProbabilities_RoundTrip() + { + var original = new EmpiricalDistribution( + new[] { 1d, 10d, 100d }, + new[] { 0.999d, 0.5d, 0.001d }, + SortOrder.Ascending, + SortOrder.Descending); + + var restored = (EmpiricalDistribution)UnivariateDistributionFactory.CreateDistribution(original.ToXElement()); + for (int i = 0; i < original.ProbabilityValues.Count; i++) + { + Assert.AreEqual(original.ProbabilityValues[i], restored.ProbabilityValues[i], 0d, "The exceedance ladder must restore in its stored order."); + } + Assert.AreEqual(original.CDF(10d), restored.CDF(10d), 1E-12); + } + + /// + /// Test the kernel density round-trip: the sample, kernel type, bandwidth, transforms, + /// and the optional per-sample weights restore exactly, through both the direct + /// FromXElement and the distribution factory. + /// + [TestMethod] + public void Test_KernelDensity_RoundTrip() + { + var sample = new[] { 1.2d, 3.4d, 2.2d, 5.6d, 4.4d, 3.1d, 2.7d, 4.9d }; + var original = new KernelDensity(sample, KernelDensity.KernelType.Epanechnikov, 0.75d); + + var element = original.ToXElement(); + var restored = (KernelDensity)UnivariateDistributionFactory.CreateDistribution(element); + + Assert.HasCount(original.SampleData.Count, restored.SampleData); + for (int i = 0; i < original.SampleData.Count; i++) + Assert.AreEqual(original.SampleData[i], restored.SampleData[i], 0d); + Assert.AreEqual(original.KernelDistribution, restored.KernelDistribution); + Assert.AreEqual(original.Bandwidth, restored.Bandwidth, 0d); + Assert.AreEqual(original.PDF(3d), restored.PDF(3d), 1E-12); + Assert.AreEqual(original.CDF(3d), restored.CDF(3d), 1E-12); + + // The weighted variant restores its weights (weighted evaluation must match). + var weights = new[] { 1d, 2d, 1d, 3d, 1d, 2d, 1d, 1d }; + var weighted = new KernelDensity(sample, weights, KernelDensity.KernelType.Gaussian, 0.6d); + var weightedRestored = (KernelDensity)UnivariateDistributionFactory.CreateDistribution(weighted.ToXElement()); + Assert.AreEqual(weighted.PDF(3d), weightedRestored.PDF(3d), 1E-12, "Weighted kernel evaluation must survive the round-trip."); + Assert.AreEqual(weighted.Mean, weightedRestored.Mean, 1E-12); + + Assert.Throws(() => KernelDensity.FromXElement(new System.Xml.Linq.XElement("Distribution"))); + } + } +} From fe9a52d3f44945eb37bd642f61f2b7acb35537ff Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 25 Jul 2026 14:29:19 -0600 Subject: [PATCH 009/222] Add the acceptance-aware node recorder to AdaptiveGaussKronrod An optional Recorder callback (x, weight, f(x)) that fires only when an interval is ACCEPTED into the final composite rule - the design that makes a weight-aware adaptive quadrature work at all: a naive per-evaluation weight hand-off double-counts, because a rejected interval's 21 evaluations are superseded by its children's and must never carry measure. Each evaluation caches its interval's nodes and values (only while a recorder is attached; null leaves the integration path unchanged with zero overhead, pinned bit-for-bit), and acceptance flushes them with half-length-scaled Kronrod weights. Two structural identities follow and are pinned under forced deep subdivision: the recorded weights sum to exactly the integration domain's width (any double count would overshoot), and the weighted node sum reproduces the returned integral to floating-point reassociation - so a consumer can take loss-exceedance probability mass directly from the quadrature. Works identically through the whole-interval and stratified-bin forms; G10K21 nodes are strictly interior, so adjacent intervals never repeat an abscissa. --- .../Integration/AdaptiveGuassKronrod.cs | 103 ++++++++++++-- .../Test_AdaptiveGaussKronrodRecorder.cs | 133 ++++++++++++++++++ 2 files changed, 221 insertions(+), 15 deletions(-) create mode 100644 Test_Numerics/Mathematics/Integration/Test_AdaptiveGaussKronrodRecorder.cs diff --git a/Numerics/Mathematics/Integration/AdaptiveGuassKronrod.cs b/Numerics/Mathematics/Integration/AdaptiveGuassKronrod.cs index c34c5a89..eec43292 100644 --- a/Numerics/Mathematics/Integration/AdaptiveGuassKronrod.cs +++ b/Numerics/Mathematics/Integration/AdaptiveGuassKronrod.cs @@ -98,11 +98,51 @@ public AdaptiveGaussKronrod(Func function, double min, double ma 0.149445554002916905664936468389821 }; + // The unscaled Kronrod weight per capture slot: slot 0 is the center node (wKronrod[10]), + // then the symmetric pair for each abscissa index i occupies slots 2i+1 and 2i+2. + private static readonly double[] wCapture = BuildCaptureWeights(); + + /// + /// Builds the unscaled Kronrod weight layout matching the node-capture order. + /// + /// The 21 unscaled weights in capture order. + private static double[] BuildCaptureWeights() + { + var weights = new double[21]; + weights[0] = wKronrod[10]; + for (int i = 0; i < 10; i++) + { + weights[2 * i + 1] = wKronrod[i]; + weights[2 * i + 2] = wKronrod[i]; + } + return weights; + } + /// /// The unidimensional function to integrate. /// public Func Function { get; } + /// + /// An optional recorder invoked as (x, weight, f(x)) for every node of every ACCEPTED + /// interval — the intervals of the final composite rule. Null (the default) records + /// nothing and leaves the integration path unchanged with zero overhead. + /// + /// + /// A naive per-evaluation weight hand-off double-counts under adaptivity: a rejected + /// interval's 21 evaluations are superseded by its children's, so their weights must + /// never carry measure. The recorder therefore fires only when an interval is accepted + /// (tolerance reached, or the depth/evaluation caps force acceptance), with each node's + /// Kronrod weight scaled by the interval half-length. Two identities follow: the weights + /// of one accepted interval sum to exactly its width, so the recorded weights sum to the + /// integration domain's width (any double count would overshoot it); and Σ weight·f(x) + /// over all recorded nodes reproduces to floating-point + /// reassociation (the composite rule IS that sum). Recorded abscissas are strictly + /// interior to their interval (the G10K21 property), so adjacent intervals never repeat + /// a node. + /// + public Action? Recorder { get; set; } + /// /// The minimum value under which the integral must be computed. /// @@ -139,10 +179,10 @@ public override void Integrate() try { // Initial evaluation using Gauss-Kronrod rule on the whole interval - var (kronrodResult, gaussResult) = EvaluateGaussKronrod(a, b); + var (kronrodResult, gaussResult, nodes, values) = EvaluateGaussKronrod(a, b); // Recursively sub-divide - Result = AdaptiveGK(Function, a, b, MaxDepth, kronrodResult, gaussResult, a, b); + Result = AdaptiveGK(Function, a, b, MaxDepth, kronrodResult, gaussResult, a, b, nodes, values); // Standard error calculated after recursion completes StandardError = Math.Sqrt(_squaredError); @@ -182,10 +222,10 @@ public void Integrate(List bins) // Initial evaluation using Gauss-Kronrod rule on the bin interval double binA = bins[i].LowerBound; double binB = bins[i].UpperBound; - var (kronrodResult, gaussResult) = EvaluateGaussKronrod(binA, binB); + var (kronrodResult, gaussResult, nodes, values) = EvaluateGaussKronrod(binA, binB); // Recursively sub-divide - mu += AdaptiveGK(Function, binA, binB, MaxDepth, kronrodResult, gaussResult, binA, binB); + mu += AdaptiveGK(Function, binA, binB, MaxDepth, kronrodResult, gaussResult, binA, binB, nodes, values); } // Final result and standard error @@ -209,23 +249,34 @@ public void Integrate(List bins) } /// - /// Evaluates the Gauss-Kronrod G10K21 rule over the interval [a, b]. + /// Evaluates the Gauss-Kronrod G10K21 rule over the interval [a, b]. When a + /// is attached, the 21 node abscissas and function values are + /// also captured (in the fixed capture order matching the static weight layout) so the + /// interval can flush them if it is later accepted; with no recorder the capture is + /// skipped entirely and the evaluation path is unchanged. /// /// The lower bound of integration. /// The upper bound of integration. - /// A tuple containing (Kronrod estimate, Gauss estimate). - private (double kronrod, double gauss) EvaluateGaussKronrod(double a, double b) + /// A tuple containing (Kronrod estimate, Gauss estimate, captured nodes, captured values). + private (double kronrod, double gauss, double[]? nodes, double[]? values) EvaluateGaussKronrod(double a, double b) { double center = 0.5 * (a + b); double halfLength = 0.5 * (b - a); double resultGauss = 0.0; double resultKronrod = 0.0; + double[]? nodes = Recorder != null ? new double[21] : null; + double[]? values = Recorder != null ? new double[21] : null; // Evaluate at center point (x = 0) double f0 = Function(center); resultKronrod += wKronrod[10] * f0; FunctionEvaluations++; + if (nodes != null) + { + nodes[0] = center; + values![0] = f0; + } // Evaluate at symmetric pairs of points for (int i = 0; i < 10; i++) @@ -246,12 +297,19 @@ public void Integrate(List bins) } FunctionEvaluations += 2; + if (nodes != null) + { + nodes[2 * i + 1] = center - abscissa; + nodes[2 * i + 2] = center + abscissa; + values![2 * i + 1] = fval1; + values[2 * i + 2] = fval2; + } } resultGauss *= halfLength; resultKronrod *= halfLength; - return (resultKronrod, resultGauss); + return (resultKronrod, resultGauss, nodes, values); } /// @@ -265,13 +323,15 @@ public void Integrate(List bins) /// The Gauss evaluation on [a,b]. /// The original lower bound of the integral. /// The original upper bound of the integral. + /// The interval's captured node abscissas (null when no recorder is attached). + /// The interval's captured function values (null when no recorder is attached). /// - /// An evaluation of the integral using adaptive Gauss-Kronrod with error less than the specified tolerance. - /// This is accomplished by subdividing the interval until the error between the Gauss and Kronrod estimates + /// An evaluation of the integral using adaptive Gauss-Kronrod with error less than the specified tolerance. + /// This is accomplished by subdividing the interval until the error between the Gauss and Kronrod estimates /// is sufficiently small. /// private double AdaptiveGK(Func f, double a, double b, int depth, - double kronrodWhole, double gaussWhole, double a0, double b0) + double kronrodWhole, double gaussWhole, double a0, double b0, double[]? nodes, double[]? values) { // Error estimate: difference between Kronrod and Gauss results double error = Math.Abs(kronrodWhole - gaussWhole); @@ -289,6 +349,19 @@ private double AdaptiveGK(Func f, double a, double b, int depth, { // Convergence is reached _squaredError += error * error; // Accumulate squared errors + + // The interval is ACCEPTED: it is part of the final composite rule, so its + // nodes carry quadrature measure — flush them to the recorder with the + // half-length-scaled Kronrod weights. Rejected (subdivided) intervals never + // reach this point, so their superseded evaluations never carry weight. + if (Recorder != null && nodes != null && values != null) + { + double halfLength = 0.5 * (b - a); + for (int j = 0; j < 21; j++) + { + Recorder(nodes[j], wCapture[j] * halfLength, values[j]); + } + } return kronrodWhole; // Return the more accurate Kronrod estimate } else @@ -297,14 +370,14 @@ private double AdaptiveGK(Func f, double a, double b, int depth, double m = (a + b) / 2.0; // Evaluate Gauss-Kronrod on left half - var (kronrodLeft, gaussLeft) = EvaluateGaussKronrod(a, m); + var (kronrodLeft, gaussLeft, nodesLeft, valuesLeft) = EvaluateGaussKronrod(a, m); // Evaluate Gauss-Kronrod on right half - var (kronrodRight, gaussRight) = EvaluateGaussKronrod(m, b); + var (kronrodRight, gaussRight, nodesRight, valuesRight) = EvaluateGaussKronrod(m, b); // Recursively subdivide the intervals and accumulate results - var leftResult = AdaptiveGK(f, a, m, depth - 1, kronrodLeft, gaussLeft, a0, b0); - var rightResult = AdaptiveGK(f, m, b, depth - 1, kronrodRight, gaussRight, a0, b0); + var leftResult = AdaptiveGK(f, a, m, depth - 1, kronrodLeft, gaussLeft, a0, b0, nodesLeft, valuesLeft); + var rightResult = AdaptiveGK(f, m, b, depth - 1, kronrodRight, gaussRight, a0, b0, nodesRight, valuesRight); return leftResult + rightResult; } diff --git a/Test_Numerics/Mathematics/Integration/Test_AdaptiveGaussKronrodRecorder.cs b/Test_Numerics/Mathematics/Integration/Test_AdaptiveGaussKronrodRecorder.cs new file mode 100644 index 00000000..b783648e --- /dev/null +++ b/Test_Numerics/Mathematics/Integration/Test_AdaptiveGaussKronrodRecorder.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Mathematics; +using Numerics.Mathematics.Integration; +using Numerics.Sampling; + +namespace Mathematics.Integration +{ + /// + /// Unit tests for the — the acceptance-aware + /// node ledger: recorded weights must partition the integration domain exactly (a rejected + /// interval's superseded evaluations never carry measure, so any double count would + /// overshoot the domain width), the weighted node sum must reproduce the returned integral + /// (the composite rule IS that sum), and an unattached recorder must leave the integration + /// byte-identical. + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + [TestClass] + public class Test_AdaptiveGaussKronrodRecorder + { + /// A sharp peak that forces deep adaptive subdivision on [0, 1]. + private static double SharpPeak(double x) + { + double t = (x - 0.3) * 60d; + return Math.Exp(-t * t) + 0.1 * x; + } + + /// + /// Test that the recorded weights sum to the domain width and the weighted node sum + /// reproduces the returned integral under forced deep subdivision — the proof that + /// abandoned (subdivided) parents never surface: if a rejected parent's nodes carried + /// weight, the weight sum would overshoot the domain width by that parent's width. + /// + [TestMethod] + public void Test_Recorder_MassAndResultIdentities() + { + var records = new List<(double X, double Weight, double Value)>(); + var gk = new AdaptiveGaussKronrod(SharpPeak, 0d, 1d) + { + RelativeTolerance = 1E-10, + MinDepth = 2, + Recorder = (x, w, fx) => records.Add((x, w, fx)), + }; + gk.Integrate(); + Assert.AreEqual(IntegrationStatus.Success, gk.Status); + + // The ledger arrives 21 nodes per accepted interval, and subdivision must have + // happened for this peak at this tolerance. + Assert.AreEqual(0, records.Count % 21, "The ledger flushes whole 21-node intervals."); + int intervals = records.Count / 21; + Assert.IsGreaterThan(1, intervals, "The sharp peak must force subdivision."); + + double weightSum = 0d; + double weightedValueSum = 0d; + for (int i = 0; i < records.Count; i++) + { + Assert.IsGreaterThan(0d, records[i].X, "G10K21 nodes are strictly interior (lower end)."); + Assert.IsLessThan(1d, records[i].X, "G10K21 nodes are strictly interior (upper end)."); + Assert.IsGreaterThan(0d, records[i].Weight, "Kronrod weights are strictly positive."); + weightSum += records[i].Weight; + weightedValueSum += records[i].Weight * records[i].Value; + } + + Assert.AreEqual(1d, weightSum, 1E-12, "The accepted-interval weights partition the domain exactly."); + Assert.AreEqual(gk.Result, weightedValueSum, Math.Abs(gk.Result) * 1E-12, + "The weighted node sum reproduces the returned integral (floating-point reassociation only)."); + + // The function-value channel echoes the integrand exactly at each node. + Assert.AreEqual(SharpPeak(records[0].X), records[0].Value, 0d); + } + + /// + /// Test the stratified-bin form: the ledger covers the union of the bins and reproduces + /// the summed result, with per-bin acceptance. + /// + [TestMethod] + public void Test_Recorder_StratifiedBins() + { + var bins = new List + { + new StratificationBin(0d, 0.25d), + new StratificationBin(0.25d, 0.6d), + new StratificationBin(0.6d, 1d), + }; + var records = new List<(double X, double Weight, double Value)>(); + var gk = new AdaptiveGaussKronrod(SharpPeak, 0d, 1d) + { + RelativeTolerance = 1E-10, + Recorder = (x, w, fx) => records.Add((x, w, fx)), + }; + gk.Integrate(bins); + Assert.AreEqual(IntegrationStatus.Success, gk.Status); + + double weightSum = 0d; + double weightedValueSum = 0d; + for (int i = 0; i < records.Count; i++) + { + weightSum += records[i].Weight; + weightedValueSum += records[i].Weight * records[i].Value; + } + Assert.AreEqual(1d, weightSum, 1E-12, "The ledger covers the union of the stratification bins."); + Assert.AreEqual(gk.Result, weightedValueSum, Math.Abs(gk.Result) * 1E-12); + } + + /// + /// Test that attaching the recorder does not perturb the integration itself, and that + /// leaving it unattached reproduces the original behavior bit-for-bit (the evaluation + /// sequence is identical; capture only observes it). + /// + [TestMethod] + public void Test_Recorder_OffIsByteIdentical() + { + var plain = new AdaptiveGaussKronrod(SharpPeak, 0d, 1d) { RelativeTolerance = 1E-10 }; + plain.Integrate(); + + var recorded = new AdaptiveGaussKronrod(SharpPeak, 0d, 1d) + { + RelativeTolerance = 1E-10, + Recorder = (x, w, fx) => { }, + }; + recorded.Integrate(); + + Assert.AreEqual(BitConverter.DoubleToInt64Bits(plain.Result), BitConverter.DoubleToInt64Bits(recorded.Result), + "Recording must not perturb the integration result."); + Assert.AreEqual(plain.FunctionEvaluations, recorded.FunctionEvaluations, + "Recording must not change the evaluation count."); + } + } +} From 5637331890553cbe62c23f762040f56c9c48f62c Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 25 Jul 2026 14:32:50 -0600 Subject: [PATCH 010/222] Add atom-aware discrete convolution and a log-spaced output option ConvolveDiscrete convolves point-mass (atom) distributions EXACTLY on a shared uniform lattice - the form the continuous-PDF pipeline cannot represent (a zero-inflation atom of a defective risk curve is a CDF jump with no density): atoms deposit with a moment-preserving two-node split, the mass vectors convolve by FFT, ringing floors at zero, and the total renormalizes to the product of the input totals. Pinned against exact enumeration: total mass, the convolved mean = sum of input means (exact by the split), interior cumulative probes, and the joint zero atom of two defective curves. The new logSpacedOutput overload keeps the existing linear-grid FFT pipeline byte-identical (false delegates to it directly) and re-reads the convolved CDF on a log-spaced ladder for order-of-magnitude supports, with a loud guard on non-positive support. The existing five continuous Convolve tests pass unchanged. --- .../Univariate/EmpiricalDistribution.cs | 209 ++++++++++++++++++ .../Univariate/Test_ConvolveUpgrades.cs | 124 +++++++++++ 2 files changed, 333 insertions(+) create mode 100644 Test_Numerics/Distributions/Univariate/Test_ConvolveUpgrades.cs diff --git a/Numerics/Distributions/Univariate/EmpiricalDistribution.cs b/Numerics/Distributions/Univariate/EmpiricalDistribution.cs index 973d279b..2ea95fa8 100644 --- a/Numerics/Distributions/Univariate/EmpiricalDistribution.cs +++ b/Numerics/Distributions/Univariate/EmpiricalDistribution.cs @@ -540,6 +540,215 @@ public override UnivariateDistributionBase Clone() return new EmpiricalDistribution(XValues, ProbabilityValues) { XTransform = XTransform, ProbabilityTransform = ProbabilityTransform }; } + /// + /// Convolves two empirical distributions with a log-spaced output grid — the opt-in + /// alternative for order-of-magnitude (heavy-tail) supports that the linear output grid + /// under-resolves. The FFT pipeline is exactly the linear-grid + /// (unchanged, + /// byte-identical when this overload is not used); only the OUTPUT resampling differs: + /// the returned distribution's CDF is re-read on a log-spaced ladder between the summed + /// supports, keeping tail resolution across decades. + /// + /// The first empirical distribution. + /// The second empirical distribution. + /// The number of output points. Default = 1024. + /// True for the log-spaced output ladder; false delegates to the linear-grid method unchanged. + /// The convolved empirical distribution. + /// Thrown when a log-spaced output is requested over a non-positive summed support. + public static EmpiricalDistribution Convolve(EmpiricalDistribution dist1, EmpiricalDistribution dist2, int numberOfPoints, bool logSpacedOutput) + { + var linear = Convolve(dist1, dist2, numberOfPoints); + if (!logSpacedOutput) return linear; + + double minimum = linear.Minimum; + double maximum = linear.Maximum; + if (minimum <= 0d || maximum <= 0d) + throw new ArgumentException("A log-spaced output grid requires a strictly positive summed support.", nameof(logSpacedOutput)); + + double logMin = Math.Log10(minimum); + double logMax = Math.Log10(maximum); + var xValues = new double[numberOfPoints]; + var pValues = new double[numberOfPoints]; + double previous = double.NegativeInfinity; + int count = 0; + for (int i = 0; i < numberOfPoints; i++) + { + double x = Math.Pow(10d, logMin + (logMax - logMin) * i / (numberOfPoints - 1d)); + double p = linear.CDF(x); + // The CDF ladder must stay strictly increasing for the empirical constructor. + if (p > previous) + { + xValues[count] = x; + pValues[count] = p; + previous = p; + count++; + } + } + var trimmedX = new double[count]; + var trimmedP = new double[count]; + Array.Copy(xValues, trimmedX, count); + Array.Copy(pValues, trimmedP, count); + return new EmpiricalDistribution(trimmedX, trimmedP) { XTransform = dist1.XTransform, ProbabilityTransform = dist1.ProbabilityTransform }; + } + + /// + /// Convolves two discrete (atom-bearing) distributions EXACTLY on a shared uniform + /// lattice — the atom-aware form the continuous Convolve cannot represent: it + /// samples continuous PDFs, so a point mass (a CDF jump, e.g. the zero-inflation atom of + /// a defective risk curve) has no representation there. Each input's atoms deposit onto + /// the lattice with a moment-preserving two-node split (input means are preserved + /// exactly), the mass vectors convolve by FFT, and the result is the discrete mass + /// ladder on the summed lattice. + /// + /// The first distribution's atom values. + /// The first distribution's atom masses (non-negative; typically summing to one). + /// The second distribution's atom values. + /// The second distribution's atom masses. + /// The per-input lattice resolution (rounded up to a power of two). Default = 4096. + /// The convolved lattice values (uniform, ascending). + /// The convolved lattice masses (non-negative; FFT ringing is floored and the total mass renormalized to the product of the input totals). + /// Thrown when any input list is null. + /// Thrown when a values/masses pair is empty or mismatched in length, or a mass is negative or non-finite. + public static void ConvolveDiscrete(IList values1, IList masses1, IList values2, IList masses2, + int latticePoints, out double[] values, out double[] masses) + { + ValidateAtoms(values1, masses1, nameof(values1)); + ValidateAtoms(values2, masses2, nameof(values2)); + if (latticePoints < 8) latticePoints = 8; + int n = Tools.NextPowerOfTwo(latticePoints); + + // The shared lattice: each input is binned over its own span, but on the SAME step + // so the convolution lattice is uniform. The step spans the summed support. + double min1 = Min(values1), max1 = Max(values1); + double min2 = Min(values2), max2 = Max(values2); + double span = Math.Max((max1 - min1) + (max2 - min2), Tools.DoubleMachineEpsilon); + double step = span / (n - 1); + + var lattice1 = DepositAtoms(values1, masses1, min1, step, n); + var lattice2 = DepositAtoms(values2, masses2, min2, step, n); + + // FFT convolution of the mass vectors (zero-padded complex arrays). + int fftSize = Tools.NextPowerOfTwo(2 * n); + var fft1 = new double[2 * fftSize]; + var fft2 = new double[2 * fftSize]; + for (int i = 0; i < n; i++) + { + fft1[2 * i] = lattice1[i]; + fft2[2 * i] = lattice2[i]; + } + Mathematics.Fourier.FFT(fft1); + Mathematics.Fourier.FFT(fft2); + var product = new double[2 * fftSize]; + for (int i = 0; i < fftSize; i++) + { + double re1 = fft1[2 * i], im1 = fft1[2 * i + 1]; + double re2 = fft2[2 * i], im2 = fft2[2 * i + 1]; + product[2 * i] = re1 * re2 - im1 * im2; + product[2 * i + 1] = re1 * im2 + im1 * re2; + } + Mathematics.Fourier.FFT(product, inverse: true); + + // The result ladder: 2n − 1 meaningful nodes from min1 + min2, floored against FFT + // ringing and renormalized to the exact product of the input mass totals. + int resultCount = 2 * n - 1; + values = new double[resultCount]; + masses = new double[resultCount]; + double total = 0d; + for (int i = 0; i < resultCount; i++) + { + values[i] = (min1 + min2) + i * step; + double mass = product[2 * i] / fftSize; + masses[i] = mass > 0d ? mass : 0d; + total += masses[i]; + } + double expected = Sum(masses1) * Sum(masses2); + if (total > 0d && expected > 0d) + { + double scale = expected / total; + for (int i = 0; i < resultCount; i++) masses[i] *= scale; + } + } + + /// + /// Validates one atom list pair. + /// + /// The atom values. + /// The atom masses. + /// The reported parameter name. + private static void ValidateAtoms(IList values, IList masses, string parameterName) + { + if (values == null || masses == null) throw new ArgumentNullException(parameterName); + if (values.Count == 0 || values.Count != masses.Count) + throw new ArgumentException("Atom values and masses must be non-empty and equal in length.", parameterName); + for (int i = 0; i < masses.Count; i++) + { + if (!Tools.IsFinite(values[i]) || !Tools.IsFinite(masses[i]) || masses[i] < 0d) + throw new ArgumentException("Atom values must be finite and masses non-negative.", parameterName); + } + } + + /// + /// Deposits atoms onto a uniform lattice with the moment-preserving two-node split: an + /// atom between nodes splits its mass so the lattice mean reproduces the atom mean + /// exactly. + /// + /// The atom values. + /// The atom masses. + /// The lattice origin. + /// The lattice step. + /// The lattice node count. + /// The lattice mass vector. + private static double[] DepositAtoms(IList values, IList masses, double origin, double step, int n) + { + var lattice = new double[n]; + for (int i = 0; i < values.Count; i++) + { + double position = (values[i] - origin) / step; + int lower = (int)Math.Floor(position); + if (lower < 0) lower = 0; + if (lower > n - 1) lower = n - 1; + int upper = lower + 1; + if (upper > n - 1) + { + lattice[n - 1] += masses[i]; + continue; + } + double fraction = position - lower; + if (fraction < 0d) fraction = 0d; + if (fraction > 1d) fraction = 1d; + lattice[lower] += masses[i] * (1d - fraction); + lattice[upper] += masses[i] * fraction; + } + return lattice; + } + + /// Returns the smallest value of a list. + /// The list. + private static double Min(IList values) + { + double minimum = double.MaxValue; + for (int i = 0; i < values.Count; i++) if (values[i] < minimum) minimum = values[i]; + return minimum; + } + + /// Returns the largest value of a list. + /// The list. + private static double Max(IList values) + { + double maximum = double.MinValue; + for (int i = 0; i < values.Count; i++) if (values[i] > maximum) maximum = values[i]; + return maximum; + } + + /// Returns the sum of a list. + /// The list. + private static double Sum(IList values) + { + double sum = 0d; + for (int i = 0; i < values.Count; i++) sum += values[i]; + return sum; + } + /// /// Serializes the empirical distribution to an XElement, overriding the base scalar-only /// form: the X and probability tables (round-trip-exact "G17"), the probability sort diff --git a/Test_Numerics/Distributions/Univariate/Test_ConvolveUpgrades.cs b/Test_Numerics/Distributions/Univariate/Test_ConvolveUpgrades.cs new file mode 100644 index 00000000..3bba4afe --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Test_ConvolveUpgrades.cs @@ -0,0 +1,124 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Distributions; + +namespace Distributions.Univariate +{ + /// + /// Unit tests for the N8 convolution upgrades: the atom-aware exact-lattice + /// (point masses — e.g. the + /// zero-inflation atom of a defective risk curve — have no representation in the + /// continuous-PDF pipeline) and the opt-in log-spaced output grid. + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + [TestClass] + public class Test_ConvolveUpgrades + { + /// + /// Test the discrete convolution against exact enumeration: total mass is the product + /// of the input totals, the mean is the sum of the input means (the moment-preserving + /// two-node split makes this exact), and the cumulative mass at probes between the + /// enumerated sum atoms matches the enumerated CDF. + /// + [TestMethod] + public void Test_ConvolveDiscrete_ExactEnumeration() + { + double[] values1 = { 0d, 10d }; + double[] masses1 = { 0.6d, 0.4d }; + double[] values2 = { 0d, 5d, 20d }; + double[] masses2 = { 0.5d, 0.3d, 0.2d }; + + EmpiricalDistribution.ConvolveDiscrete(values1, masses1, values2, masses2, 4096, out var values, out var masses); + + double total = 0d, mean = 0d; + for (int i = 0; i < values.Length; i++) + { + total += masses[i]; + mean += masses[i] * values[i]; + } + Assert.AreEqual(1d, total, 1E-12, "Total mass is the product of the input totals."); + + // Exact input means: 4.0 and 5.5 → the convolved mean is 9.5 exactly (two-node + // splits preserve each input mean). + Assert.AreEqual(9.5d, mean, 1E-9, "The convolved mean is the sum of the input means."); + + // Enumerated sums: {0: 0.30, 5: 0.18, 10: 0.20, 15: 0.12, 20: 0.12, 30: 0.08}. + // Cumulative probes at least one lattice node away from every atom are exact up to + // the split smear. + Assert.AreEqual(0.48d, CumulativeAt(values, masses, 7.5d), 1E-9, "P(sum ≤ 7.5)."); + Assert.AreEqual(0.68d, CumulativeAt(values, masses, 12.5d), 1E-9, "P(sum ≤ 12.5)."); + Assert.AreEqual(0.92d, CumulativeAt(values, masses, 25d), 1E-9, "P(sum ≤ 25)."); + } + + /// + /// Test the motivating case: zero-inflation atoms of defective risk curves convolve + /// exactly — the joint zero atom is the product of the input zero masses, a point mass + /// the continuous-PDF pipeline cannot represent. + /// + [TestMethod] + public void Test_ConvolveDiscrete_ZeroAtoms() + { + double[] values1 = { 0d, 100d }; + double[] masses1 = { 0.7d, 0.3d }; + double[] values2 = { 0d, 50d }; + double[] masses2 = { 0.8d, 0.2d }; + + EmpiricalDistribution.ConvolveDiscrete(values1, masses1, values2, masses2, 4096, out var values, out var masses); + + // P(both zero) = 0.7·0.8 = 0.56 sits at the origin node (both inputs' atoms land + // exactly on lattice nodes here only at the ends; probe just above the origin). + Assert.AreEqual(0.56d, CumulativeAt(values, masses, 1d), 1E-9, "The joint zero atom is exact."); + Assert.AreEqual(0.56d + 0.7d * 0.2d + 0.3d * 0.8d, CumulativeAt(values, masses, 120d), 1E-9, "P(sum ≤ 120)."); + Assert.AreEqual(1d, CumulativeAt(values, masses, 151d), 1E-12, "The ladder is exhaustive."); + + // Guards: mismatched atom lists are loud. + Assert.Throws(() => + EmpiricalDistribution.ConvolveDiscrete(new[] { 0d }, new[] { 0.5d, 0.5d }, values2, masses2, 4096, out _, out _)); + Assert.Throws(() => + EmpiricalDistribution.ConvolveDiscrete(new[] { 0d }, new[] { -0.5d }, values2, masses2, 4096, out _, out _)); + } + + /// + /// Test the log-spaced output option: false delegates to the linear-grid method + /// unchanged (the same instance), true re-reads the same convolved CDF on a log-spaced + /// ladder (agreeing at probes) and requires a strictly positive summed support. + /// + [TestMethod] + public void Test_Convolve_LogSpacedOutput() + { + var dist1 = new EmpiricalDistribution(new[] { 10d, 40d, 100d }, new[] { 0.1d, 0.6d, 0.95d }); + var dist2 = new EmpiricalDistribution(new[] { 5d, 20d, 60d }, new[] { 0.15d, 0.5d, 0.9d }); + + var linear = EmpiricalDistribution.Convolve(dist1, dist2, 1024, logSpacedOutput: false); + var logSpaced = EmpiricalDistribution.Convolve(dist1, dist2, 1024, logSpacedOutput: true); + + // The log-spaced ladder reads the same convolved CDF. + foreach (double probe in new[] { 40d, 80d, 140d }) + { + Assert.AreEqual(linear.CDF(probe), logSpaced.CDF(probe), 5E-3, $"CDF agreement at {probe}."); + } + Assert.IsGreaterThan(0d, logSpaced.Minimum, "The log-spaced ladder lives on positive support."); + + // A non-positive summed support cannot be log-spaced. + var negative = new EmpiricalDistribution(new[] { -5d, 0d, 10d }, new[] { 0.1d, 0.5d, 0.9d }); + Assert.Throws(() => EmpiricalDistribution.Convolve(negative, dist2, 1024, logSpacedOutput: true)); + } + + /// + /// Accumulates the lattice mass at and below a probe value. + /// + /// The lattice values. + /// The lattice masses. + /// The probe value. + /// The cumulative mass. + private static double CumulativeAt(double[] values, double[] masses, double probe) + { + double sum = 0d; + for (int i = 0; i < values.Length && values[i] <= probe; i++) sum += masses[i]; + return sum; + } + } +} From 9249440b869b461ea2d414da412a367e73392a36 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 25 Jul 2026 14:34:41 -0600 Subject: [PATCH 011/222] Pin the Vegas power-transform Jacobian in the handed weight The upstream unit-test half of the engine-level gamma audit: a known heavy-upper-tail integrand (21(1-p)^20, integral exactly one) must stay unbiased at gamma in {1, 4, 10}, and the weights handed to the integrand must sum to the domain volume per evaluation batch at every gamma - a missing Jacobian would distort both by the measure change, an order-of-magnitude error at gamma = 10. Seeded MersenneTwister with the Sobol default off, so the runs are deterministic. --- .../Test_VegasTailFocusJacobian.cs | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 Test_Numerics/Mathematics/Integration/Test_VegasTailFocusJacobian.cs diff --git a/Test_Numerics/Mathematics/Integration/Test_VegasTailFocusJacobian.cs b/Test_Numerics/Mathematics/Integration/Test_VegasTailFocusJacobian.cs new file mode 100644 index 00000000..41d3fcae --- /dev/null +++ b/Test_Numerics/Mathematics/Integration/Test_VegasTailFocusJacobian.cs @@ -0,0 +1,87 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Mathematics; +using Numerics.Mathematics.Integration; +using Numerics.Sampling; + +namespace Mathematics.Integration +{ + /// + /// Unit tests for the Vegas power-transform tail focus (N9): the upstream half of the + /// engine-level empirical audit. The transform p' = 1 − (1 − p)^γ concentrates samples in + /// the upper tail, and its Jacobian must fold into the weight handed to the integrand — + /// otherwise every consumer treating that weight as probability measure is biased even when + /// the returned integral is correct. A known heavy-tail integrand must integrate to the + /// same value at γ ∈ {1, 4, 10}, and the weights must sum to the domain volume per + /// evaluation batch at every γ. + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + [TestClass] + public class Test_VegasTailFocusJacobian + { + /// + /// Runs Vegas over the heavy-upper-tail integrand f(p) = 21·(1 − p)^20 on [0, 1] + /// (∫ = 1 exactly) at a given tail-focus parameter, accumulating the weights handed to + /// the integrand. + /// + /// The power-transform tail-focus parameter γ. + /// The integral estimate, the total weight handed out, and the call count. + private static (double Result, double WeightSum, long Calls) Run(double gamma) + { + double weightSum = 0d; + long calls = 0; + var vegas = new Vegas((x, w) => + { + weightSum += w; + calls++; + return 21d * Math.Pow(1d - x[0], 20d); + }, 1, new[] { 0d }, new[] { 1d }) + { + UseSobolSequence = false, + Random = new MersenneTwister(12345), + TailFocusParameter = gamma, + IndependentEvaluations = 10, + FunctionCalls = 10000, + }; + vegas.Integrate(); + Assert.AreEqual(IntegrationStatus.Success, vegas.Status, $"γ = {gamma}: the integration must succeed."); + return (vegas.Result, weightSum, calls); + } + + /// + /// Test that the heavy-tail integral is unbiased at every tail-focus setting: the + /// power transform changes where samples land, and the folded Jacobian must exactly + /// compensate, so γ ∈ {1, 4, 10} all reproduce ∫ 21(1 − p)^20 dp = 1. + /// + [TestMethod] + public void Test_HeavyTailIntegral_SameValueAtEveryGamma() + { + foreach (double gamma in new[] { 1d, 4d, 10d }) + { + var run = Run(gamma); + Assert.AreEqual(1d, run.Result, 0.03d, $"γ = {gamma}: the tail-focused integral must stay unbiased."); + } + } + + /// + /// Test that the weight handed to the integrand carries the domain measure at every + /// tail-focus setting: the average weight per evaluation batch must reproduce the + /// domain volume (a missing Jacobian would distort this by the measure change, an + /// order-of-magnitude error at γ = 10). + /// + [TestMethod] + public void Test_WeightSum_EqualsDomainVolumeAtEveryGamma() + { + foreach (double gamma in new[] { 1d, 4d, 10d }) + { + var run = Run(gamma); + double volumePerBatch = run.WeightSum / (run.Calls / 10000d); + Assert.AreEqual(1d, volumePerBatch, 0.1d, + $"γ = {gamma}: the weights must sum to the domain volume per evaluation batch (Jacobian folded into the weight)."); + } + } + } +} From 46854e435404e2478a00082f8c53933f88b0e71d Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 25 Jul 2026 14:37:23 -0600 Subject: [PATCH 012/222] Add the pooled IndependentExclusive overload with a truncation flag The pooled combination-enumeration path allocated its output lists and every indicator row per call - a per-evaluation cost in joint-risk hot loops. The new overload takes caller-owned output lists, refills existing row arrays of matching length in place (steady-state zero output allocations, pinned by reference-identity tests across calls), trims stale tails from larger prior calls, and returns TRUE when the inclusion-exclusion expansion converged early - surfacing the previously silent truncation of the deepest combinations behind the closing pseudo-row. The allocating out-parameter overload now delegates to it, so outputs are identical by construction (pinned for full and truncated enumerations). The sibling exclusive kernels (PositivelyDependentExclusive/ExclusivePCM/ExclusiveMVN) follow the same recipe when their call sites go pooled. --- Numerics/Data/Statistics/Probability.cs | 83 +++++++- .../Test_ProbabilityPooledExclusive.cs | 177 ++++++++++++++++++ 2 files changed, 253 insertions(+), 7 deletions(-) create mode 100644 Test_Numerics/Data/Statistics/Test_ProbabilityPooledExclusive.cs diff --git a/Numerics/Data/Statistics/Probability.cs b/Numerics/Data/Statistics/Probability.cs index 6811c4ec..68708410 100644 --- a/Numerics/Data/Statistics/Probability.cs +++ b/Numerics/Data/Statistics/Probability.cs @@ -1061,6 +1061,43 @@ public static double[] IndependentExclusive(IList probabilities) /// The result is added to a list, and convergence is monitored using the specified tolerances. /// public static void IndependentExclusive(IList probabilities, int[] binomialCombinations, int[,] indicators, out List eventProbabilities, out List eventIndicators, double absoluteTolerance = 1E-4, double relativeTolerance = 1E-4) + { + eventProbabilities = new List(); + eventIndicators = new List(); + IndependentExclusive(probabilities, binomialCombinations, indicators, eventProbabilities, eventIndicators, absoluteTolerance, relativeTolerance); + } + + /// + /// The pooled-output form of + /// : + /// the caller supplies (and reuses) the output lists across calls, and existing + /// indicator row arrays of matching length are refilled in place — so a hot loop that + /// calls this per evaluation performs no per-call output allocations after the first. + /// The return value surfaces the inclusion-exclusion truncation that was previously + /// silent. + /// + /// An array of probabilities for each event. Each element represents the probability of an individual event occurring. + /// An array of binomial combinations that define the number of events to consider for each calculation. + /// The caller-owned output list of exclusive event probabilities; cleared and refilled. + /// The caller-owned output list of event indicator rows; existing rows of matching length are refilled in place, and the list is trimmed to the produced count. + /// A 2D array of indicators, where each row represents a combination of events, and 0 means the event did not occur, 1 means the event did occur. + /// The absolute tolerance for evaluation convergence of the inclusion-exclusion algorithm. Default = 1E-4. + /// The relative tolerance for evaluation convergence of the inclusion-exclusion algorithm. Default = 1E-4. + /// + /// True when the inclusion-exclusion expansion converged early and the deepest + /// combinations were TRUNCATED — the outputs then end with one closing pseudo-row + /// carrying half the remaining inclusion-exclusion gap; false when every combination + /// was enumerated. + /// + /// Thrown when either output list is null. + /// Thrown if the probabilities array is null, empty, or if the lengths of the probabilities and indicators arrays do not match. + /// + /// This method uses the inclusion-exclusion principle to compute the exclusive + /// probability of each event combination, exactly as the allocating overload does (which + /// now delegates here); only the output-buffer ownership and the truncation visibility + /// differ. + /// + public static bool IndependentExclusive(IList probabilities, int[] binomialCombinations, int[,] indicators, List eventProbabilities, List eventIndicators, double absoluteTolerance = 1E-4, double relativeTolerance = 1E-4) { // Validation Checks if (probabilities == null || probabilities.Count == 0) @@ -1069,9 +1106,38 @@ public static void IndependentExclusive(IList probabilities, int[] binom throw new ArgumentException("The indicators array must have at least one row.", nameof(indicators)); if (probabilities.Count != indicators.GetLength(1)) throw new ArgumentException("The probabilities array and the indicator array must have the same length.", nameof(probabilities)); + if (eventProbabilities == null) throw new ArgumentNullException(nameof(eventProbabilities)); + if (eventIndicators == null) throw new ArgumentNullException(nameof(eventIndicators)); - eventProbabilities = new List(); - eventIndicators = new List(); + int n = probabilities.Count; + int used = 0; // Output rows placed this call + eventProbabilities.Clear(); + + // Copies an indicator row into the pooled output slot, reusing an existing row + // array of matching length in place (the steady-state zero-allocation path). + int[] PlaceRow(int rowIndex) + { + int[] row; + if (used < eventIndicators.Count && eventIndicators[used] != null && eventIndicators[used].Length == n) + { + row = eventIndicators[used]; + } + else + { + row = new int[n]; + if (used < eventIndicators.Count) eventIndicators[used] = row; + else eventIndicators.Add(row); + } + for (int column = 0; column < n; column++) row[column] = indicators[rowIndex, column]; + used++; + return row; + } + + // Trims stale rows from a previous, larger call. + void TrimToUsed() + { + while (eventIndicators.Count > used) eventIndicators.RemoveAt(eventIndicators.Count - 1); + } double union = 0; double s = 1; // Sign for inclusion-exclusion @@ -1098,9 +1164,10 @@ public static void IndependentExclusive(IList probabilities, int[] binom double diff = Math.Abs(inc - exc); if (j > 0 && j < binomialCombinations.Length && diff <= absoluteTolerance && diff <= relativeTolerance * Math.Min(inc, exc)) { - eventIndicators.Add(indicators.GetRow(indicators.GetLength(0) - 1)); // Add last indicator row + PlaceRow(indicators.GetLength(0) - 1); // Add last indicator row eventProbabilities.Add(0.5 * diff); // Add the average of the difference to the event probabilities - return; // Exit early when convergence is reached + TrimToUsed(); + return true; // Exit early when convergence is reached — the deepest combinations are truncated } // Flip the sign for the next inclusion-exclusion term @@ -1114,10 +1181,10 @@ public static void IndependentExclusive(IList probabilities, int[] binom } // Record the current indicators - eventIndicators.Add(indicators.GetRow(i)); + var currentRow = PlaceRow(i); // Compute the exclusive event probability and add to the list - eventProbabilities.Add(IndependentExclusive(probabilities, eventIndicators.Last())); + eventProbabilities.Add(IndependentExclusive(probabilities, currentRow)); // Calculate the union of probabilities (inclusion-exclusion) if (i < probabilities.Count) @@ -1126,11 +1193,13 @@ public static void IndependentExclusive(IList probabilities, int[] binom } else { - union += s * IndependentJointProbability(probabilities, eventIndicators.Last()); + union += s * IndependentJointProbability(probabilities, currentRow); } } + TrimToUsed(); + return false; } #endregion diff --git a/Test_Numerics/Data/Statistics/Test_ProbabilityPooledExclusive.cs b/Test_Numerics/Data/Statistics/Test_ProbabilityPooledExclusive.cs new file mode 100644 index 00000000..c97f89a4 --- /dev/null +++ b/Test_Numerics/Data/Statistics/Test_ProbabilityPooledExclusive.cs @@ -0,0 +1,177 @@ +using System; +using System.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Data.Statistics; + +namespace Data.Statistics +{ + /// + /// Unit tests for the pooled + /// overload (N11): output parity with the allocating overload, steady-state row-array reuse + /// across calls, and the truncation flag that surfaces the previously silent + /// inclusion-exclusion early exit. + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + [TestClass] + public class Test_ProbabilityPooledExclusive + { + /// The three-event full enumeration: sizes 1, 2, 3 in combination order. + private static readonly int[,] ThreeEventIndicators = new int[,] + { + { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 }, + { 1, 1, 0 }, { 1, 0, 1 }, { 0, 1, 1 }, + { 1, 1, 1 }, + }; + + /// The binomial combination counts for three events: C(3,1), C(3,2), C(3,3). + private static readonly int[] ThreeEventCombinations = { 3, 3, 1 }; + + /// + /// Test that the pooled overload reproduces the allocating overload exactly (the + /// allocating form now delegates to it), and reports no truncation on a full + /// enumeration. + /// + [TestMethod] + public void Test_Pooled_MatchesAllocatingOverload() + { + double[] probabilities = { 0.3d, 0.2d, 0.1d }; + Probability.IndependentExclusive(probabilities, ThreeEventCombinations, ThreeEventIndicators, + out List expectedProbabilities, out List expectedIndicators); + + var pooledProbabilities = new List(); + var pooledIndicators = new List(); + bool truncated = Probability.IndependentExclusive(probabilities, ThreeEventCombinations, ThreeEventIndicators, + pooledProbabilities, pooledIndicators); + + Assert.IsFalse(truncated, "A full three-event enumeration never truncates."); + Assert.HasCount(expectedProbabilities.Count, pooledProbabilities); + Assert.HasCount(expectedIndicators.Count, pooledIndicators); + for (int i = 0; i < expectedProbabilities.Count; i++) + { + Assert.AreEqual(expectedProbabilities[i], pooledProbabilities[i], 0d, $"Row {i} probability parity."); + CollectionAssert.AreEqual(expectedIndicators[i], pooledIndicators[i], $"Row {i} indicator parity."); + } + } + + /// + /// Test the steady-state reuse contract: a second call with the same shape refills the + /// same row arrays in place (no per-call output allocations), and a smaller subsequent + /// call trims the stale tail. + /// + [TestMethod] + public void Test_Pooled_ReusesRowArraysAcrossCalls() + { + double[] first = { 0.3d, 0.2d, 0.1d }; + double[] second = { 0.05d, 0.4d, 0.25d }; + var pooledProbabilities = new List(); + var pooledIndicators = new List(); + + Probability.IndependentExclusive(first, ThreeEventCombinations, ThreeEventIndicators, pooledProbabilities, pooledIndicators); + var firstCallRow0 = pooledIndicators[0]; + var firstCallRow6 = pooledIndicators[6]; + + Probability.IndependentExclusive(second, ThreeEventCombinations, ThreeEventIndicators, pooledProbabilities, pooledIndicators); + Assert.AreSame(firstCallRow0, pooledIndicators[0], "Row arrays must be refilled in place across same-shape calls."); + Assert.AreSame(firstCallRow6, pooledIndicators[6], "Every pooled row must be reused."); + Assert.HasCount(7, pooledIndicators); + + // Parity against a fresh allocating call on the second inputs. + Probability.IndependentExclusive(second, ThreeEventCombinations, ThreeEventIndicators, + out List expectedProbabilities, out _); + for (int i = 0; i < expectedProbabilities.Count; i++) + { + Assert.AreEqual(expectedProbabilities[i], pooledProbabilities[i], 0d); + } + } + + /// + /// Test the truncation flag: many small probabilities converge the inclusion-exclusion + /// expansion early, so the deepest combinations are truncated behind one closing + /// pseudo-row — previously silent, now reported, with the allocating overload's outputs + /// unchanged. + /// + [TestMethod] + public void Test_Pooled_TruncationIsReported() + { + // Ten rare events: the union converges within the first few inclusion-exclusion + // terms at the default 1e-4 tolerances, so the expansion exits early. + int n = 10; + var probabilities = new double[n]; + for (int i = 0; i < n; i++) probabilities[i] = 0.01d; + var (combinations, indicators) = BuildFullEnumeration(n); + + var pooledProbabilities = new List(); + var pooledIndicators = new List(); + bool truncated = Probability.IndependentExclusive(probabilities, combinations, indicators, pooledProbabilities, pooledIndicators); + + Assert.IsTrue(truncated, "Rare-event expansions converge early and must report the truncation."); + Assert.IsLessThan(indicators.GetLength(0), pooledProbabilities.Count, "The deepest combinations were not enumerated."); + Assert.HasCount(pooledProbabilities.Count, pooledIndicators); + + // The allocating overload produces the same truncated outputs (it delegates). + Probability.IndependentExclusive(probabilities, combinations, indicators, + out List expectedProbabilities, out _); + Assert.HasCount(expectedProbabilities.Count, pooledProbabilities); + for (int i = 0; i < expectedProbabilities.Count; i++) + { + Assert.AreEqual(expectedProbabilities[i], pooledProbabilities[i], 0d); + } + } + + /// + /// Builds the full combination enumeration (sizes 1..n in combination order) for n + /// events. + /// + /// The event count. + /// The per-size combination counts and the indicator matrix. + private static (int[] Combinations, int[,] Indicators) BuildFullEnumeration(int n) + { + var rows = new List(); + var counts = new List(); + for (int size = 1; size <= n; size++) + { + int before = rows.Count; + foreach (var combo in EnumerateCombinations(size, n)) + { + var row = new int[n]; + for (int j = 0; j < combo.Length; j++) row[combo[j]] = 1; + rows.Add(row); + } + counts.Add(rows.Count - before); + } + var indicators = new int[rows.Count, n]; + for (int i = 0; i < rows.Count; i++) + { + for (int j = 0; j < n; j++) indicators[i, j] = rows[i][j]; + } + return (counts.ToArray(), indicators); + } + + /// + /// Enumerates all index combinations of a given size from n items, lexicographically. + /// + /// The combination size. + /// The item count. + /// The index combinations. + private static IEnumerable EnumerateCombinations(int size, int n) + { + var indexes = new int[size]; + for (int i = 0; i < size; i++) indexes[i] = i; + while (true) + { + var copy = new int[size]; + Array.Copy(indexes, copy, size); + yield return copy; + + int position = size - 1; + while (position >= 0 && indexes[position] == n - size + position) position--; + if (position < 0) yield break; + indexes[position]++; + for (int i = position + 1; i < size; i++) indexes[i] = indexes[i - 1] + 1; + } + } + } +} From 013c46d23ea88c5fdc1b57e09778baa16cdb02bd Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 25 Jul 2026 14:54:33 -0600 Subject: [PATCH 013/222] Document the univariate functions namespace --- docs/functions/index.md | 97 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 docs/functions/index.md diff --git a/docs/functions/index.md b/docs/functions/index.md new file mode 100644 index 00000000..edc84d31 --- /dev/null +++ b/docs/functions/index.md @@ -0,0 +1,97 @@ +# Univariate Functions + +[Back to Index](../index.md) + +The `Numerics.Functions` namespace provides the uncertain-function toolkit: univariate +functional forms with optional stochastic residuals, sampled by confidence level, plus +serialization, a factory, and posterior-ensemble sampling. These are the shared math behind +risk input functions (rating curves, transforms, damage functions) in the consuming +engineering applications. + +## The `IUnivariateFunction` contract + +Every function implements `IUnivariateFunction`: + +| Member | Meaning | +|--------|---------| +| `Function(x)` / `InverseFunction(y)` | Forward and inverse evaluation | +| `SetParameters(values)` / `ValidateParameters(...)` | The parameter-vector surface (fitted parameter sets apply directly) | +| `Minimum` / `Maximum` | The evaluation support (clamped); some types derive `Minimum` from a location parameter | +| `IsDeterministic` | Whether the stochastic residual is active | +| `ConfidenceLevel` | The runtime draw in [0, 1] driving the residual; values outside [0, 1] evaluate the mean/median form. Runtime state — never serialized | + +## Available functions + +| Function | Class | Form | Residual | +|----------|-------|------|----------| +| **Linear** | `LinearFunction` | $Y = \alpha + \beta X$ | Additive Gaussian, $\epsilon \sim N(0, \sigma)$ | +| **Power** | `PowerFunction` | $Y = \alpha (X - \xi)^\beta$, optional inverse form | Log-space Gaussian (multiplicative) | +| **Tabular** | `TabularFunction` | Interpolation over `UncertainOrderedPairedData` with axis transforms | Co-monotonic percentile of the ordinate distributions | +| **Segmented power** | `SegmentedPowerFunction` | BaRatin addition mode: $Q(h) = \sum_k 10^{\log_{10}\alpha_k} (h - h_k)^{\beta_k} \cdot \mathbb{1}\{h > h_k\}$ | Log₁₀-space Gaussian | +| **Composite** | `CompositeFunction` | Weighted average $\sum w_i f_i(x)$, or a mixture selected by one uniform | Inherited from the children | + +### Segmented power (BaRatin addition mode) + +`SegmentedPowerFunction` matches the RMC-BestFit rating-curve parameterization exactly. The +parameter vector is `[h₁, log₁₀α₁, β₁, h₂, log₁₀α₂, β₂, …, σ]` (length `3·segments + 1`), so +a fitted posterior `ParameterSet.Values` applies directly through `SetParameters`. Breakpoints +must be strictly ordered, exponents non-negative (the monotone-rating constraint behind the +numeric Brent inverse), discharge is zero at and below the cease-to-flow stage `h₁`, and one +segment degenerates to the plain `PowerFunction`. + +```cs +using Numerics.Functions; + +// Two controls: main channel from stage 1, overbank activating at stage 3. +var rating = new SegmentedPowerFunction(new[] { 1.0, 1.5, 2.0, 3.0, 1.2, 1.5, 0.1 }); +double q = rating.Function(5.0); // deterministic (mean) discharge +rating.IsDeterministic = false; +rating.ConfidenceLevel = 0.75; // multiplies by 10^(z·σ), the BestFit stochastic form +double q75 = rating.Function(5.0); +``` + +### Composite (weighted average and mixture) + +`CompositeFunction` combines child functions with non-negative weights summing to one. The +**weighted-average** mode evaluates $\sum w_i f_i(x)$ with the composite's confidence level +driving every child co-monotonically. The **mixture** mode composes with a single uniform: the +draw selects a child by cumulative weight and re-scales the remainder as the child's own draw +— deterministic composition sampling with no internal random source. Outside [0, 1] both modes +evaluate the weighted average of the child means. + +## Serialization and the factory + +Every concrete function serializes with an instance `ToXElement()` and a static +`FromXElement(XElement)`. `UnivariateFunctionFactory` dispatches on the +`UnivariateFunctionType` enum or on a serialized element's local name, and maps live instances +back to their enum type: + +```cs +var element = rating.ToXElement(); +IUnivariateFunction restored = UnivariateFunctionFactory.CreateFromXElement(element); +UnivariateFunctionType kind = UnivariateFunctionFactory.GetFunctionType(restored); // SegmentedPower +``` + +`ConfidenceLevel` is runtime sampling state and never serializes; `PowerFunction.Minimum` +derives from ξ and never serializes. The serialization surface lives on the concrete classes +(not the interface) so external `IUnivariateFunction` implementations remain source-compatible. + +## Posterior ensembles + +`EnsembleFunction` carries a template function plus an array of posterior `ParameterSet` +draws — the vehicle for imported fitted functions (e.g., a BestFit rating-curve posterior) +inside a simulation engine. Both sampling surfaces are **pure**: every call returns a fresh +clone of the template configured with the selected draw, so concurrent realizations share no +mutable state. + +```cs +var ensemble = new EnsembleFunction(rating, posteriorParameterSets); +IUnivariateFunction draw = ensemble.Sample(index); // posterior draw by index +IUnivariateFunction pDraw = ensemble.Sample(0.37); // min(⌊u·N⌋, N − 1) +``` + +## Link functions + +The sibling `Link Functions` family (`ILinkFunction`: identity, log, logit, probit, +complementary log-log, Fisher-z, Yeo-Johnson) serves regression and machine-learning +transformations and has its own factory, `LinkFunctionFactory`. From dfb1c6bd62369830d336d336ecdf1f18675d579e Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sun, 26 Jul 2026 16:55:28 -0600 Subject: [PATCH 014/222] Seed the multivariate normal quadrature deterministically MVNDST is a randomized lattice rule -- it draws from MultivariateNormal's own generator to randomize its quadrature, so the returned probability carries a small stochastic error and two calls with different generator states do not agree bit-for-bit. That generator defaulted to `new MersenneTwister()`, whose parameterless constructor seeds from DateTime.UtcNow.Ticks. Every CDF and Interval evaluation above two dimensions was therefore irreproducible across runs. It stayed hidden because the error sits near the requested tolerance, so statistical tests absorb it. RMC-TotalRisk found it downstream: dependent competing-risks incidence curves run through Interval once per unit per hazard level, and a results hash over that path changed on every run. MVNUNI now defaults to the fixed DefaultMVNUNISeed, and CompetingRisks gains a PRNGSeed applied when it builds its multivariate normal, so callers deriving seeds from model content can tie the result to the model instead of to a shared constant. Both are documented, including MVNUNI's thread-safety constraint (MVNDST advances it, so a shared instance needs a clone per thread, as JointProbabilitiesMVN already does). Gates: build 0 warnings; 1943/1943 on net10.0, net9.0, net8.0 and net481. --- .../Multivariate/MultivariateNormal.cs | 21 ++++++++++++++++++- .../Univariate/CompetingRisks.cs | 16 +++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/Numerics/Distributions/Multivariate/MultivariateNormal.cs b/Numerics/Distributions/Multivariate/MultivariateNormal.cs index b0d97929..f913bb4e 100644 --- a/Numerics/Distributions/Multivariate/MultivariateNormal.cs +++ b/Numerics/Distributions/Multivariate/MultivariateNormal.cs @@ -70,7 +70,7 @@ public MultivariateNormal(double[] mean, double[,] covariance) // variables required for the multivariate CDF private Matrix _correlation = null!; private double[] _correl = null!; - private Random _MVNUNI = new MersenneTwister(); + private Random _MVNUNI = new MersenneTwister(DefaultMVNUNISeed); private int _maxEvaluations = 100000; private double _absoluteError = 1E-4; private double _relativeError = 1E-4; @@ -80,9 +80,28 @@ public MultivariateNormal(double[] mean, double[,] covariance) private bool _correlationMatrixCreated = false; private bool _covSRTed = false; + /// + /// The default seed. Fixed, never clock-derived — see . + /// + public const int DefaultMVNUNISeed = 12345; + /// /// The uniform(0,1) random number generator required to compute the multivariate CDF for dimensions greater than 2. /// + /// + /// MVNDST is a RANDOMIZED lattice rule: it draws from this generator to randomize its + /// quadrature, so the returned probability carries a small stochastic error and two calls + /// with different generator states do not agree bit-for-bit. This defaulted to + /// new MersenneTwister(), which seeds from the wall clock, so every CDF and Interval + /// evaluation above two dimensions was irreproducible across runs — silently, because the + /// error sits near the requested tolerance and statistical tests absorb it. The default is + /// now the fixed . Callers that need results tied to their + /// own content-derived seed assign a seeded generator here. + /// + /// Not thread-safe: MVNDST advances this generator, so an instance shared across threads + /// must be cloned per thread (as Probability.JointProbabilitiesMVN does). + /// + /// public Random MVNUNI { get { return _MVNUNI; } diff --git a/Numerics/Distributions/Univariate/CompetingRisks.cs b/Numerics/Distributions/Univariate/CompetingRisks.cs index 9416feff..f5e60215 100644 --- a/Numerics/Distributions/Univariate/CompetingRisks.cs +++ b/Numerics/Distributions/Univariate/CompetingRisks.cs @@ -68,6 +68,20 @@ public CompetingRisks(IUnivariateDistribution[] distributions) /// public ReadOnlyCollection Distributions => new(_distributions); + /// + /// The seed for the multivariate normal's quadrature randomizer, used by the dependent + /// (perfectly negative and correlation-matrix) branches. + /// + /// + /// Those branches evaluate Genz's randomized-lattice rectangle integral, which draws from + /// , so the incidence and union results carry a small + /// stochastic error. Fixing the seed here makes them reproducible; callers deriving seeds + /// from model content assign their own so the results are tied to the model rather than to + /// a shared constant. Applied when the multivariate normal is built, so set it before the + /// first dependent evaluation. + /// + public int PRNGSeed { get; set; } = MultivariateNormal.DefaultMVNUNISeed; + /// /// Determines the interpolation transform for the X-values. /// @@ -1068,7 +1082,7 @@ private void CreateMultivariateNormal() sigma[i, j] = CorrelationMatrix[i, j]; } } - _mvn = new MultivariateNormal(mu, sigma); + _mvn = new MultivariateNormal(mu, sigma) { MVNUNI = new MersenneTwister(PRNGSeed) }; _mvnCreated = true; } From 0d650d53085e5a5c41e19d4ea4a29475d5a58d99 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sun, 26 Jul 2026 17:44:57 -0600 Subject: [PATCH 015/222] Make the bootstrap uncertainty reductions deterministic The reductions over bootstrap replications merged thread-local partials through Tools.ParallelAdd, whose partitioning follows the scheduler. Results therefore varied in their last bits between runs and between machines -- including Estimate's mean curve, which feeds a monotonic filter that can admit a different number of interpolation knots from a last-bit difference. Every such reduction now splits its work into a fixed number of chunks, each summed sequentially and merged in chunk order, so the association is independent of the thread count: ExpectedProbabilities (both overloads), UncertaintyAnalysisResults.ProcessMeanCurve, the P0 proportions in BiasCorrectedQuantileCI and BCaQuantileCI, AccelerationConstants, StandardError, and Statistics.JackKnifeStandardError. Parallelism is retained throughout. Defects found while reworking the class: - ExpectedProbabilities summed over the successfully fitted distributions but divided by the full replication count, biasing the expectation toward zero in proportion to the failure rate. Both now exclude failures. - Fit failures were silent. Distributions() records FailedReplications, so a caller can tell when results rest on fewer samples than requested. - The NaN guards in BiasCorrectedQuantileCI and BCaQuantileCI were written `x != double.NaN`, which is always true in IEEE-754, so the intended rejection never ran. Harmless, since the following comparison is false for NaN, but now correct. - Estimate's quantile ladder accumulated Log10(previous + shift) + delta, so rounding compounded across up to a thousand bins. Each ordinate is now computed from the origin, matching ProcessMeanCurve. - Quantiles() and Probabilities() could not accept an existing set of bootstrapped distributions, so a caller wanting both paid for the whole bootstrap twice. - ComputeMinMaxQuantiles took a lock per distribution; it now merges thread-local extremes once per partition. - The jackknife in AccelerationConstants, StandardError, JackKnifeSample and JackKnifeStandardError copied the whole sample into a fresh list per point. Each chunk now refills one leave-one-out buffer. Adds two pins: Estimate is bit-identical across calls at the same seed, and ExpectedProbabilities is bit-identical when one arm is constrained to a single worker thread. Gates: build 0 warnings; 1945/1945 on net10.0, net9.0, net8.0 and net481. --- Numerics/Data/Statistics/Statistics.cs | 60 ++- .../Multivariate/MultivariateNormal.cs | 17 +- .../Univariate/CompetingRisks.cs | 10 +- .../Uncertainty Analysis/BootstrapAnalysis.cs | 383 ++++++++++++------ .../UncertaintyAnalysisResults.cs | 49 ++- .../Univariate/Test_BootstrapAnalysis.cs | 67 +++ 6 files changed, 409 insertions(+), 177 deletions(-) diff --git a/Numerics/Data/Statistics/Statistics.cs b/Numerics/Data/Statistics/Statistics.cs index 90a4c035..8d4cb1b6 100644 --- a/Numerics/Data/Statistics/Statistics.cs +++ b/Numerics/Data/Statistics/Statistics.cs @@ -316,26 +316,47 @@ public static double Skewness(IList data) /// /// Sample of data, no sorting is assumed. /// The statistic for estimating standard error. + /// + /// Chunked so the sum merges in a fixed order, independent of the thread count. Each chunk + /// refills one leave-one-out buffer rather than copying the sample per point. + /// public static double JackKnifeStandardError(IList data, Func, double> statistic) { if (data == null) throw new ArgumentNullException(nameof(data)); if (data.Count == 0) return double.NaN; int N = data.Count; + if (N == 1) return 0d; double theta = statistic(data); - double I = 0; - Parallel.For(0, N, () => 0d, (i, loop, subI) => + + int chunks = Math.Min(JackKnifeChunks, N); + var chunkSums = new double[chunks]; + Parallel.For(0, chunks, c => { - // Remove data point - var jackSample = new List(data); - jackSample.RemoveAt(i); - // Compute statistic - subI += Tools.Sqr(statistic(jackSample) - theta); - return subI; - }, z => Tools.ParallelAdd(ref I, z)); + var jackSample = new double[N - 1]; + double sum = 0d; + int start = (int)((long)c * N / chunks); + int end = (int)((long)(c + 1) * N / chunks); + for (int i = start; i < end; i++) + { + for (int k = 0; k < i; k++) jackSample[k] = data[k]; + for (int k = i + 1; k < N; k++) jackSample[k - 1] = data[k]; + sum += Tools.Sqr(statistic(jackSample) - theta); + } + chunkSums[c] = sum; + }); + + double I = 0d; + for (int c = 0; c < chunks; c++) I += chunkSums[c]; return Math.Sqrt((N - 1) / (double)N * I); } + /// + /// The number of accumulation chunks used by the jackknife reductions — fixed, so the + /// floating-point association order does not vary with the machine or the thread count. + /// + private const int JackKnifeChunks = 64; + /// /// Returns a jackknifed sample. /// @@ -348,14 +369,21 @@ public static double JackKnifeStandardError(IList data, Func + if (N == 1) return thetaJack; + + // Perform Jackknife, reusing one leave-one-out buffer per chunk. + int chunks = Math.Min(JackKnifeChunks, N); + Parallel.For(0, chunks, c => { - // Remove data point - var jackSample = new List(data); - jackSample.RemoveAt(i); - // Compute statistic - thetaJack[i] = statistic(jackSample); + var jackSample = new double[N - 1]; + int start = (int)((long)c * N / chunks); + int end = (int)((long)(c + 1) * N / chunks); + for (int i = start; i < end; i++) + { + for (int k = 0; k < i; k++) jackSample[k] = data[k]; + for (int k = i + 1; k < N; k++) jackSample[k - 1] = data[k]; + thetaJack[i] = statistic(jackSample); + } }); return thetaJack; } diff --git a/Numerics/Distributions/Multivariate/MultivariateNormal.cs b/Numerics/Distributions/Multivariate/MultivariateNormal.cs index f913bb4e..cf5b65fa 100644 --- a/Numerics/Distributions/Multivariate/MultivariateNormal.cs +++ b/Numerics/Distributions/Multivariate/MultivariateNormal.cs @@ -89,18 +89,11 @@ public MultivariateNormal(double[] mean, double[,] covariance) /// The uniform(0,1) random number generator required to compute the multivariate CDF for dimensions greater than 2. /// /// - /// MVNDST is a RANDOMIZED lattice rule: it draws from this generator to randomize its - /// quadrature, so the returned probability carries a small stochastic error and two calls - /// with different generator states do not agree bit-for-bit. This defaulted to - /// new MersenneTwister(), which seeds from the wall clock, so every CDF and Interval - /// evaluation above two dimensions was irreproducible across runs — silently, because the - /// error sits near the requested tolerance and statistical tests absorb it. The default is - /// now the fixed . Callers that need results tied to their - /// own content-derived seed assign a seeded generator here. - /// - /// Not thread-safe: MVNDST advances this generator, so an instance shared across threads - /// must be cloned per thread (as Probability.JointProbabilitiesMVN does). - /// + /// MVNDST is a randomized lattice rule and draws from this generator, so the CDF above two + /// dimensions carries a small stochastic error and only reproduces when the generator is + /// seeded. The default is the fixed ; assign a seeded + /// generator to tie results to a caller's own seed. Not thread-safe — MVNDST advances the + /// generator, so an instance shared across threads must be cloned per thread. /// public Random MVNUNI { diff --git a/Numerics/Distributions/Univariate/CompetingRisks.cs b/Numerics/Distributions/Univariate/CompetingRisks.cs index f5e60215..5d563185 100644 --- a/Numerics/Distributions/Univariate/CompetingRisks.cs +++ b/Numerics/Distributions/Univariate/CompetingRisks.cs @@ -73,12 +73,10 @@ public CompetingRisks(IUnivariateDistribution[] distributions) /// (perfectly negative and correlation-matrix) branches. /// /// - /// Those branches evaluate Genz's randomized-lattice rectangle integral, which draws from - /// , so the incidence and union results carry a small - /// stochastic error. Fixing the seed here makes them reproducible; callers deriving seeds - /// from model content assign their own so the results are tied to the model rather than to - /// a shared constant. Applied when the multivariate normal is built, so set it before the - /// first dependent evaluation. + /// Those branches evaluate a randomized-lattice rectangle integral drawing from + /// , so their results reproduce only when the seed + /// is fixed. Applied when the multivariate normal is built — set it before the first + /// dependent evaluation. /// public int PRNGSeed { get; set; } = MultivariateNormal.DefaultMVNUNISeed; diff --git a/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs b/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs index 6094656f..b1041a32 100644 --- a/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs +++ b/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Numerics.Data; using Numerics.Data.Statistics; @@ -54,11 +55,26 @@ public BootstrapAnalysis(IUnivariateDistribution distribution, ParameterEstimati private int _retries = 20; + /// + /// The number of accumulation chunks used by the reductions over replications. Fixed, not + /// derived from the processor count, so the summation order — and therefore the result — + /// does not vary with the machine or the thread count. + /// + private const int ReductionChunks = 64; + /// /// The distribution parameter estimation method. /// public ParameterEstimationMethod EstimationMethod { get; private set; } + /// + /// The number of replications whose parameter fit failed on the most recent call to + /// or . Failed + /// replications are recorded as null and excluded from every summary, so a non-zero count + /// means the results rest on fewer samples than requested. + /// + public int FailedReplications { get; private set; } + /// /// The univariate distribution to bootstrap. /// @@ -91,6 +107,7 @@ public IUnivariateDistribution[] Distributions() var bootDistributions = new IUnivariateDistribution[Replications]; var r = new MersenneTwister(PRNGSeed); var seeds = r.NextIntegers(Replications); + int failures = 0; Parallel.For(0, Replications, idx => { bool failed = false; @@ -111,9 +128,14 @@ public IUnivariateDistribution[] Distributions() // MLE and certain L-moments methods can fail to find a solution // On fail, set to null - if (failed == true) bootDistributions[idx] = null!; + if (failed == true) + { + bootDistributions[idx] = null!; + Interlocked.Increment(ref failures); + } }); + FailedReplications = failures; return bootDistributions; } @@ -124,6 +146,7 @@ public IUnivariateDistribution[] Distributions() public IUnivariateDistribution[] Distributions(ParameterSet[] parameterSets) { var bootDistributions = new IUnivariateDistribution[parameterSets.Length]; + int failures = 0; Parallel.For(0, parameterSets.Length, idx => { bool failed = false; @@ -141,10 +164,15 @@ public IUnivariateDistribution[] Distributions(ParameterSet[] parameterSets) }; // On fail, set to null - if (failed == true) bootDistributions[idx] = null!; + if (failed == true) + { + bootDistributions[idx] = null!; + Interlocked.Increment(ref failures); + } }); + FailedReplications = failures; return bootDistributions; } @@ -155,8 +183,8 @@ public IUnivariateDistribution[] Distributions(ParameterSet[] parameterSets) public double[,] Parameters(IUnivariateDistribution[]? distributions = null) { var bootDistributions = distributions != null ? distributions : Distributions(); - var bootParameters = new double[bootDistributions.Count(), Distribution.NumberOfParameters]; - Parallel.For(0, bootDistributions.Count(), idx => + var bootParameters = new double[bootDistributions.Length, Distribution.NumberOfParameters]; + Parallel.For(0, bootDistributions.Length, idx => { if (bootDistributions[idx] != null) { @@ -180,8 +208,8 @@ public IUnivariateDistribution[] Distributions(ParameterSet[] parameterSets) public ParameterSet[] ParameterSets(IUnivariateDistribution[]? distributions = null) { var bootDistributions = distributions != null ? distributions : Distributions(); - var bootParameters = new ParameterSet[bootDistributions.Count()]; - Parallel.For(0, bootDistributions.Count(), idx => + var bootParameters = new ParameterSet[bootDistributions.Length]; + Parallel.For(0, bootDistributions.Length, idx => { if (bootDistributions[idx] != null) { @@ -235,12 +263,17 @@ public ParameterSet[] ParameterSets(IUnivariateDistribution[]? distributions = n /// Bootstrap a list of quantiles given the input non-exceedance probabilities. /// /// List of non-exceedance probabilities. - public double[,] Quantiles(IList probabilities) + /// Optional. Pass in an array of bootstrapped distributions. Default = null. + public double[,] Quantiles(IList probabilities, IUnivariateDistribution[]? distributions = null) { - var Output = new double[Replications, probabilities.Count]; - var bootDistributions = Distributions(); - for (int i = 0; i < probabilities.Count; i++) - Parallel.For(0, Replications, idx => { Output[idx, i] = bootDistributions[idx] != null ? bootDistributions[idx].InverseCDF(probabilities[i]) : double.NaN; }); + var bootDistributions = distributions != null ? distributions : Distributions(); + var Output = new double[bootDistributions.Length, probabilities.Count]; + Parallel.For(0, bootDistributions.Length, idx => + { + var distribution = bootDistributions[idx]; + for (int i = 0; i < probabilities.Count; i++) + Output[idx, i] = distribution != null ? distribution.InverseCDF(probabilities[i]) : double.NaN; + }); return Output; } @@ -248,12 +281,17 @@ public ParameterSet[] ParameterSets(IUnivariateDistribution[]? distributions = n /// Bootstrap a list of non-exceedance probabilities given the input quantile values. /// /// List quantile values. - public double[,] Probabilities(IList quantiles) + /// Optional. Pass in an array of bootstrapped distributions. Default = null. + public double[,] Probabilities(IList quantiles, IUnivariateDistribution[]? distributions = null) { - var Output = new double[Replications, quantiles.Count]; - var bootDistributions = Distributions(); - for (int i = 0; i < quantiles.Count; i++) - Parallel.For(0, Replications, idx => { Output[idx, i] = bootDistributions[idx] != null ? bootDistributions[idx].CDF(quantiles[i]) : double.NaN; }); + var bootDistributions = distributions != null ? distributions : Distributions(); + var Output = new double[bootDistributions.Length, quantiles.Count]; + Parallel.For(0, bootDistributions.Length, idx => + { + var distribution = bootDistributions[idx]; + for (int i = 0; i < quantiles.Count; i++) + Output[idx, i] = distribution != null ? distribution.CDF(quantiles[i]) : double.NaN; + }); return Output; } @@ -286,21 +324,21 @@ public UncertaintyAnalysisResults Estimate(IList probabilities, double a // create list of quantiles var minMax = ComputeMinMaxQuantiles(0.001, 1 - 1E-9, bootDistributions); - int bins = 200; - List quantiles = new List(); double shift = 0; if (minMax[0] <= 0) shift = Math.Abs(minMax[0]) + 1d; double min = minMax[0] + shift; double max = minMax[1] + shift; - int order = (int)Math.Floor(Math.Log10(max) - Math.Log10(min)); - bins = Math.Max(200, Math.Min(1000, 100 * order)); - double delta = (Math.Log10(max) - Math.Log10(min)) / (bins - 1); - double x = Math.Log10(min); - quantiles.Add(Math.Pow(10, x) - shift); - for (int i = 1; i <= bins - 1; i++) + double logMin = Math.Log10(min); + int order = (int)Math.Floor(Math.Log10(max) - logMin); + int bins = Math.Max(200, Math.Min(1000, 100 * order)); + double delta = (Math.Log10(max) - logMin) / (bins - 1); + + // Each ordinate is computed from the origin, not accumulated from its predecessor, so + // rounding does not compound across the ladder. + var quantiles = new List(bins); + for (int i = 0; i < bins; i++) { - x = Math.Log10(quantiles[i - 1] + shift) + delta; - quantiles.Add(Math.Pow(10, x) - shift); + quantiles.Add(Math.Pow(10, logMin + i * delta) - shift); } // get mean curve @@ -320,22 +358,8 @@ public double[] ExpectedProbabilities(IList quantiles, IList pro var quants = quantiles.ToArray(); var probs = probabilities.ToArray(); Array.Sort(quants); - var expected = new double[quantiles.Count]; - var output = new double[probabilities.Count]; var bootDistributions = distributions != null ? distributions : Distributions(); - for (int i = 0; i < quantiles.Count; i++) - { - double total = 0d; - Parallel.For(0, bootDistributions.Count(), () => 0d, (j, loop, sum) => - { - if (bootDistributions[j] != null) - { - sum += bootDistributions[j].CDF(quants[i]); - } - return sum; - }, z => Tools.ParallelAdd(ref total, z)); - expected[i] = total / bootDistributions.Count(); - } + var expected = MeanCDFs(quants, bootDistributions); double minY = double.MaxValue; double maxY = double.MinValue; @@ -358,8 +382,62 @@ public double[] ExpectedProbabilities(IList quantiles, IList pro useLogTransform = true; Linear linint = new Linear(xVals, yVals) { XTransform = Transform.NormalZ, YTransform = useLogTransform ? Transform.Logarithmic : Transform.None }; - output = linint.Interpolate(probs); - return output; + return linint.Interpolate(probs); + } + + /// + /// The mean CDF across the bootstrapped distributions at each quantile. + /// + /// The quantile values to evaluate, ascending. + /// The bootstrapped distributions; null entries are failed fits. + /// The expected non-exceedance probability at each quantile. + /// + /// Replications are split into chunks, each summed + /// sequentially and merged in chunk order, so the result does not depend on the thread + /// count. Failed fits are excluded from both the sum and the divisor. + /// + private static double[] MeanCDFs(double[] quantiles, IUnivariateDistribution[] distributions) + { + int replications = distributions.Length; + int quantileCount = quantiles.Length; + var expected = new double[quantileCount]; + if (replications == 0 || quantileCount == 0) return expected; + + int chunks = Math.Min(ReductionChunks, replications); + var chunkSums = new double[chunks][]; + var chunkValid = new int[chunks]; + for (int c = 0; c < chunks; c++) chunkSums[c] = new double[quantileCount]; + + Parallel.For(0, chunks, c => + { + var accumulator = chunkSums[c]; + int start = (int)((long)c * replications / chunks); + int end = (int)((long)(c + 1) * replications / chunks); + int valid = 0; + for (int j = start; j < end; j++) + { + var distribution = distributions[j]; + if (distribution == null) continue; + valid++; + for (int i = 0; i < quantileCount; i++) + { + accumulator[i] += distribution.CDF(quantiles[i]); + } + } + chunkValid[c] = valid; + }); + + int validCount = 0; + for (int c = 0; c < chunks; c++) validCount += chunkValid[c]; + if (validCount == 0) return expected; + + for (int i = 0; i < quantileCount; i++) + { + double total = 0d; + for (int c = 0; c < chunks; c++) total += chunkSums[c][i]; + expected[i] = total / validCount; + } + return expected; } /// @@ -371,22 +449,8 @@ public double[] ExpectedProbabilities(IList quantiles, IUnivariateDistri { var quants = quantiles.ToArray(); Array.Sort(quants); - var expected = new double[quantiles.Count]; var bootDistributions = distributions != null ? distributions : Distributions(); - for (int i = 0; i < quantiles.Count; i++) - { - double total = 0d; - Parallel.For(0, bootDistributions.Count(), () => 0d, (j, loop, sum) => - { - if (bootDistributions[j] != null) - { - sum += bootDistributions[j].CDF(quants[i]); - } - return sum; - }, z => Tools.ParallelAdd(ref total, z)); - expected[i] = total / bootDistributions.Count(); - } - return expected; + return MeanCDFs(quants, bootDistributions); } /// @@ -397,19 +461,24 @@ public double[] ExpectedProbabilities(IList quantiles, IUnivariateDistri /// Optional. Pass in an array of bootstrapped distributions. Default = null. public double[] ComputeMinMaxQuantiles(double minProbability, double maxProbability, IUnivariateDistribution[] distributions) { + // Thread-local extremes merged once per partition rather than a lock per distribution. var output = new double[] { double.MaxValue, double.MinValue }; object lockObject = new object(); - Parallel.For(0, distributions.Count(), j => + int count = distributions.Length; + Parallel.For(0, count, () => (Min: double.MaxValue, Max: double.MinValue), (j, loop, local) => + { + var distribution = distributions[j]; + if (distribution == null) return local; + double minX = distribution.InverseCDF(minProbability); + double maxX = distribution.InverseCDF(maxProbability); + return (minX < local.Min ? minX : local.Min, maxX > local.Max ? maxX : local.Max); + }, + local => { - if (distributions[j] != null) + lock (lockObject) { - var minX = distributions[j].InverseCDF(minProbability); - var maxX = distributions[j].InverseCDF(maxProbability); - lock (lockObject) - { - if (minX < output[0]) output[0] = minX; - if (maxX > output[1]) output[1] = maxX; - } + if (local.Min < output[0]) output[0] = local.Min; + if (local.Max > output[1]) output[1] = local.Max; } }); return output; @@ -428,8 +497,8 @@ public double[] ComputeMinMaxQuantiles(double minProbability, double maxProbabil var bootDistributions = distributions != null ? distributions : Distributions(); for (int i = 0; i < probabilities.Count; i++) { - var XValues = new double[bootDistributions.Count()]; - Parallel.For(0, bootDistributions.Count(), idx => { XValues[idx] = bootDistributions[idx] != null ? bootDistributions[idx].InverseCDF(probabilities[i]) : double.NaN; }); + var XValues = new double[bootDistributions.Length]; + Parallel.For(0, bootDistributions.Length, idx => { XValues[idx] = bootDistributions[idx] != null ? bootDistributions[idx].InverseCDF(probabilities[i]) : double.NaN; }); // Filter valid values and sort int validCount = 0; @@ -469,19 +538,24 @@ public double[] ComputeMinMaxQuantiles(double minProbability, double maxProbabil var CIs = new double[] { alpha / 2d, 1d - alpha / 2d }; var Output = new double[probabilities.Count, 2]; var bootDistributions = distributions != null ? distributions : Distributions(); + int replications = bootDistributions.Length; for (int i = 0; i < probabilities.Count; i++) { - double P0 = 0d; // proportions of values less than population - var XValues = new double[bootDistributions.Count()]; - Parallel.For(0, bootDistributions.Count(), () => 0d, (idx, loop, subP0) => + var XValues = new double[replications]; + Parallel.For(0, replications, idx => { XValues[idx] = bootDistributions[idx] != null ? bootDistributions[idx].InverseCDF(probabilities[i]) : double.NaN; - if (XValues[idx] != double.NaN && XValues[idx] <= populationXValues[i]) subP0 += 1d; - return subP0; - }, z => Tools.ParallelAdd(ref P0, z)); + }); + + // Counted sequentially so the proportion does not depend on the thread count. + double P0 = 0d; // proportions of values less than population + for (int idx = 0; idx < replications; idx++) + { + if (!double.IsNaN(XValues[idx]) && XValues[idx] <= populationXValues[i]) P0 += 1d; + } // get proportion - P0 = P0 / (bootDistributions.Count() + 1); + P0 = P0 / (replications + 1); // Filter valid values and sort int validCount = 0; @@ -530,8 +604,8 @@ public double[] ComputeMinMaxQuantiles(double minProbability, double maxProbabil var bootDistributions = distributions != null ? distributions : Distributions(); for (int i = 0; i < probabilities.Count; i++) { - var XValues = new double[bootDistributions.Count()]; - Parallel.For(0, bootDistributions.Count(), idx => { XValues[idx] = bootDistributions[idx] != null ? Math.Pow(bootDistributions[idx].InverseCDF(probabilities[i]), 1d / 3d) : double.NaN; }); + var XValues = new double[bootDistributions.Length]; + Parallel.For(0, bootDistributions.Length, idx => { XValues[idx] = bootDistributions[idx] != null ? Math.Pow(bootDistributions[idx].InverseCDF(probabilities[i]), 1d / 3d) : double.NaN; }); // Filter valid values int validCount = 0; @@ -589,14 +663,18 @@ public double[] ComputeMinMaxQuantiles(double minProbability, double maxProbabil var bootDistributions = Distributions(); for (int i = 0; i < probabilities.Count; i++) { - double P0 = 0d; // proportions of values less than population var XValues = new double[Replications]; - Parallel.For(0, Replications, () => 0d, (idx, loop, subP0) => + Parallel.For(0, Replications, idx => { XValues[idx] = bootDistributions[idx] != null ? bootDistributions[idx].InverseCDF(probabilities[i]) : double.NaN; - if (XValues[idx] != double.NaN && XValues[idx] <= populationXValues[i]) subP0 += 1d; - return subP0; - }, z => Tools.ParallelAdd(ref P0, z)); + }); + + // Counted sequentially so the proportion does not depend on the thread count. + double P0 = 0d; // proportions of values less than population + for (int idx = 0; idx < Replications; idx++) + { + if (!double.IsNaN(XValues[idx]) && XValues[idx] <= populationXValues[i]) P0 += 1d; + } // get proportion P0 = (P0 + 1) / (Replications + 1); @@ -636,44 +714,68 @@ public double[] ComputeMinMaxQuantiles(double minProbability, double maxProbabil /// Sample of data. /// List of non-exceedance probabilities. /// The list of best-estimate quantiles. + /// + /// Chunked so the moment sums merge in a fixed order, independent of the thread count. + /// Each chunk refills one leave-one-out buffer rather than copying the sample per point. + /// private double[] AccelerationConstants(IList sampleData, IList probabilities, IList thetaHats) { var N = sampleData.Count; - var I2 = new double[probabilities.Count]; - var I3 = new double[probabilities.Count]; - var a = new double[probabilities.Count]; - - // Perform Jackknife - Parallel.For(0, N, idx => + int probabilityCount = probabilities.Count; + var a = new double[probabilityCount]; + if (N == 0) return a; + + int chunks = Math.Min(ReductionChunks, N); + var chunkI2 = new double[chunks][]; + var chunkI3 = new double[chunks][]; + for (int c = 0; c < chunks; c++) { - // Remove data point - var jackSample = new List(sampleData); - jackSample.RemoveAt(idx); - - // Estimate distribution - var newDistribution = ((UnivariateDistributionBase)Distribution).Clone(); + chunkI2[c] = new double[probabilityCount]; + chunkI3[c] = new double[probabilityCount]; + } - try + Parallel.For(0, chunks, c => + { + var i2 = chunkI2[c]; + var i3 = chunkI3[c]; + var jackSample = new double[N - 1]; + int start = (int)((long)c * N / chunks); + int end = (int)((long)(c + 1) * N / chunks); + for (int idx = start; idx < end; idx++) { - ((IEstimation)newDistribution).Estimate(jackSample, EstimationMethod); - // Get quantiles from new distribution - var thetaJack = new double[probabilities.Count]; - for (int i = 0; i < probabilities.Count; i++) + for (int k = 0; k < idx; k++) jackSample[k] = sampleData[k]; + for (int k = idx + 1; k < N; k++) jackSample[k - 1] = sampleData[k]; + + // Cloned per point: a failed Estimate can leave the instance partially set. + var newDistribution = ((UnivariateDistributionBase)Distribution).Clone(); + try { - thetaJack[i] = newDistribution.InverseCDF(probabilities[i]); - Tools.ParallelAdd(ref I2[i], Math.Pow(thetaHats[i] - thetaJack[i], 2)); - Tools.ParallelAdd(ref I3[i], Math.Pow(thetaHats[i] - thetaJack[i], 3)); + ((IEstimation)newDistribution).Estimate(jackSample, EstimationMethod); + for (int i = 0; i < probabilityCount; i++) + { + double thetaJack = newDistribution.InverseCDF(probabilities[i]); + i2[i] += Math.Pow(thetaHats[i] - thetaJack, 2); + i3[i] += Math.Pow(thetaHats[i] - thetaJack, 3); + } } + catch (Exception) + { + // MLE and certain L-moments methods can fail to find a solution + }; } - catch (Exception) - { - // MLE and certain L-moments methods can fail to find a solution - }; - }); + // Get acceleration constant - for (int i = 0; i < probabilities.Count; i++) - a[i] = I3[i] / (Math.Pow(I2[i], 1.5) * 6); + for (int i = 0; i < probabilityCount; i++) + { + double I2 = 0d, I3 = 0d; + for (int c = 0; c < chunks; c++) + { + I2 += chunkI2[c][i]; + I3 += chunkI3[c][i]; + } + a[i] = I3 / (Math.Pow(I2, 1.5) * 6); + } return a; } @@ -829,41 +931,56 @@ private double[] BootstrapStandardError(UnivariateDistributionBase parentDist, I /// Sample of data. /// List of non-exceedance probabilities. /// The list of best-estimate quantiles. + /// + /// Chunked as . + /// private double[] StandardError(IList sampleData, IList probabilities, IList thetaHats) { var N = sampleData.Count; - var I2 = new double[probabilities.Count]; - var se = new double[probabilities.Count]; + int probabilityCount = probabilities.Count; + var se = new double[probabilityCount]; + if (N == 0) return se; + + int chunks = Math.Min(ReductionChunks, N); + var chunkI2 = new double[chunks][]; + for (int c = 0; c < chunks; c++) chunkI2[c] = new double[probabilityCount]; // Perform Jackknife - Parallel.For(0, N, idx => + Parallel.For(0, chunks, c => { - // Remove data point - var jackSample = new List(sampleData); - jackSample.RemoveAt(idx); - // Estimate distribution - var newDistribution = ((UnivariateDistributionBase)Distribution).Clone(); - - try + var i2 = chunkI2[c]; + var jackSample = new double[N - 1]; + int start = (int)((long)c * N / chunks); + int end = (int)((long)(c + 1) * N / chunks); + for (int idx = start; idx < end; idx++) { - ((IEstimation)newDistribution).Estimate(jackSample, EstimationMethod); - // Get quantiles from new distribution - var thetaJack = new double[probabilities.Count]; - for (int i = 0; i < probabilities.Count; i++) + for (int k = 0; k < idx; k++) jackSample[k] = sampleData[k]; + for (int k = idx + 1; k < N; k++) jackSample[k - 1] = sampleData[k]; + + var newDistribution = ((UnivariateDistributionBase)Distribution).Clone(); + try { - thetaJack[i] = Math.Pow(newDistribution.InverseCDF(probabilities[i]), 1d / 3d); - Tools.ParallelAdd(ref I2[i], Math.Pow(thetaHats[i] - thetaJack[i], 2)); + ((IEstimation)newDistribution).Estimate(jackSample, EstimationMethod); + for (int i = 0; i < probabilityCount; i++) + { + double thetaJack = Math.Pow(newDistribution.InverseCDF(probabilities[i]), 1d / 3d); + i2[i] += Math.Pow(thetaHats[i] - thetaJack, 2); + } } + catch (Exception) + { + // MLE and certain L-moments methods can fail to find a solution + }; } - catch (Exception) - { - // MLE and certain L-moments methods can fail to find a solution - }; - }); + // Get standard error - for (int i = 0; i < probabilities.Count; i++) - se[i] = Math.Sqrt((N - 1) / (double)N * I2[i]); + for (int i = 0; i < probabilityCount; i++) + { + double I2 = 0d; + for (int c = 0; c < chunks; c++) I2 += chunkI2[c][i]; + se[i] = Math.Sqrt((N - 1) / (double)N * I2); + } return se; } diff --git a/Numerics/Distributions/Univariate/Uncertainty Analysis/UncertaintyAnalysisResults.cs b/Numerics/Distributions/Univariate/Uncertainty Analysis/UncertaintyAnalysisResults.cs index b908cc7e..caac76a4 100644 --- a/Numerics/Distributions/Univariate/Uncertainty Analysis/UncertaintyAnalysisResults.cs +++ b/Numerics/Distributions/Univariate/Uncertainty Analysis/UncertaintyAnalysisResults.cs @@ -27,6 +27,12 @@ namespace Numerics.Distributions /// public class UncertaintyAnalysisResults { + /// + /// The number of accumulation chunks used by the reduction over sampled distributions. + /// Fixed, not derived from the processor count, so the summation order — and therefore the + /// mean curve — does not vary with the machine or the thread count. + /// + private const int ReductionChunks = 64; /// /// Construct an instance of the UncertaintyAnalysisResults class. @@ -452,20 +458,43 @@ public void ProcessMeanCurve(UnivariateDistributionBase[] sampledDistributions, quantiles[i] = Math.Pow(10, logX) - shift; } - // Compute expected probability for each quantile - var expected = new double[bins]; - for (int i = 0; i < bins; i++) + // Compute the expected probability at each quantile, summing over fixed chunks so the + // result is independent of the thread count. The monotonic filter below can turn a + // last-bit difference into a different number of interpolation knots. + int chunkCount = Math.Min(ReductionChunks, B); + var chunkSums = new double[chunkCount][]; + var chunkValid = new int[chunkCount]; + for (int c = 0; c < chunkCount; c++) chunkSums[c] = new double[bins]; + + Parallel.For(0, chunkCount, c => { - double total = 0d; - Parallel.For(0, B, () => 0d, (j, loop, sum) => + var accumulator = chunkSums[c]; + int start = (int)((long)c * B / chunkCount); + int end = (int)((long)(c + 1) * B / chunkCount); + int valid = 0; + for (int j = start; j < end; j++) { - if (sampledDistributions[j] is not null) + var distribution = sampledDistributions[j]; + if (distribution is null) continue; + valid++; + for (int i = 0; i < bins; i++) { - sum += sampledDistributions[j].CDF(quantiles[i]); + accumulator[i] += distribution.CDF(quantiles[i]); } - return sum; - }, z => Tools.ParallelAdd(ref total, z)); - expected[i] = total / B; + } + chunkValid[c] = valid; + }); + + int validDistributions = 0; + for (int c = 0; c < chunkCount; c++) validDistributions += chunkValid[c]; + if (validDistributions == 0) validDistributions = 1; + + var expected = new double[bins]; + for (int i = 0; i < bins; i++) + { + double total = 0d; + for (int c = 0; c < chunkCount; c++) total += chunkSums[c][i]; + expected[i] = total / validDistributions; } // Build monotonic interpolation points diff --git a/Test_Numerics/Distributions/Univariate/Test_BootstrapAnalysis.cs b/Test_Numerics/Distributions/Univariate/Test_BootstrapAnalysis.cs index 2cc43017..b0213ab7 100644 --- a/Test_Numerics/Distributions/Univariate/Test_BootstrapAnalysis.cs +++ b/Test_Numerics/Distributions/Univariate/Test_BootstrapAnalysis.cs @@ -3,9 +3,11 @@ using Numerics.Data.Statistics; using Numerics.Distributions; using Numerics.Sampling; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Threading; using static System.Reflection.Metadata.BlobBuilder; namespace Distributions.Univariate @@ -183,5 +185,70 @@ public void Test_BootstrapAnalysis_UncertaintyAnalysisResults_Equivalence() } } + /// + /// Verifies Estimate() is bit-reproducible across calls at the same seed. Compares raw + /// bits: a tolerance assert cannot detect a reduction-order difference. + /// + [TestMethod] + public void Test_Estimate_IsBitReproducible() + { + var probabilities = new double[] { 0.999, 0.99, 0.9, 0.5, 0.1, 0.01, 0.001 }; + var dist = new Normal(3.122599, 0.5573654); + + var first = new BootstrapAnalysis(dist, ParameterEstimationMethod.MethodOfMoments, 100, 1000).Estimate(probabilities); + var second = new BootstrapAnalysis(dist, ParameterEstimationMethod.MethodOfMoments, 100, 1000).Estimate(probabilities); + + Assert.HasCount(first.MeanCurve.Length, second.MeanCurve); + for (int i = 0; i < first.MeanCurve.Length; i++) + { + Assert.AreEqual(BitConverter.DoubleToInt64Bits(first.MeanCurve[i]), + BitConverter.DoubleToInt64Bits(second.MeanCurve[i]), $"MeanCurve differs at index {i}."); + } + for (int i = 0; i < first.ConfidenceIntervals.GetLength(0); i++) + { + for (int j = 0; j < 2; j++) + { + Assert.AreEqual(BitConverter.DoubleToInt64Bits(first.ConfidenceIntervals[i, j]), + BitConverter.DoubleToInt64Bits(second.ConfidenceIntervals[i, j]), $"ConfidenceIntervals differ at [{i},{j}]."); + } + } + } + + /// + /// Verifies the mean curve does not depend on how many threads compute it. Constraining + /// one arm to a single worker stands in for running on a machine with a different core + /// count. + /// + [TestMethod] + public void Test_ExpectedProbabilities_IsThreadCountIndependent() + { + var probabilities = new double[] { 0.99, 0.9, 0.5, 0.1, 0.01 }; + var quantiles = new double[] { 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0 }; + var dist = new Normal(3.122599, 0.5573654); + var boot = new BootstrapAnalysis(dist, ParameterEstimationMethod.MethodOfMoments, 100, 1000); + var distributions = boot.Distributions(); + Assert.AreEqual(0, boot.FailedReplications); + + var parallelResult = boot.ExpectedProbabilities(quantiles, probabilities, distributions); + + double[] serialResult; + ThreadPool.GetMinThreads(out int minWorker, out int minIO); + try + { + ThreadPool.SetMinThreads(1, minIO); + serialResult = boot.ExpectedProbabilities(quantiles, probabilities, distributions); + } + finally + { + ThreadPool.SetMinThreads(minWorker, minIO); + } + + for (int i = 0; i < parallelResult.Length; i++) + { + Assert.AreEqual(BitConverter.DoubleToInt64Bits(parallelResult[i]), + BitConverter.DoubleToInt64Bits(serialResult[i]), $"Expected probabilities differ at index {i}."); + } + } + } } From 1f4a01278cf9418601ba511b8adbbf4ea8e5514a Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sun, 26 Jul 2026 18:12:18 -0600 Subject: [PATCH 016/222] Enumerate exclusive combinations lazily The exclusive expansions read their combinations from a dense n-by-(2^n - 1) indicator matrix built by Factorial.AllCombinations. That matrix is the binding constraint on dimension long before the arithmetic is: it is 80 MB at twenty events and 3.3 GB at twenty-five, and it refuses to build at all past thirty. Callers therefore carried dimension caps that had nothing to do with the model. Factorial gains NextCombination and AllCombinationsLazy, which generate the same rows in the same order -- subset size ascending, then lexicographic -- allocating nothing per row and imposing no upper bound on n. Probability gains IndependentExclusiveLazy on top of them, with caller-owned output buffers and an ExclusiveEnumerationStatus distinguishing a completed expansion from one the inclusion-exclusion bracket closed early and one that stopped at a caller-supplied cap. Where the dense form runs, the lazy form emits bit-identical probabilities and identical indicator rows. On convergence it closes with the same half-gap row; at the cap it closes with the exact residual 1 - sum(emitted), which for independent events is the mass of everything not enumerated. The cap is a backstop, not the operating mechanism. The bracket cannot be tested before the third subset size, so the floor is n + C(n,2) + C(n,3), and at the failure probabilities a risk model carries it closes at or just after that: a forty-event expansion enumerates around 10^5 of its 10^12 combinations. Gates: build 0 warnings; 1951/1951 on net10.0, net9.0, net8.0 and net481. --- Numerics/Data/Statistics/Probability.cs | 146 ++++++++++++++ .../Special Functions/Factorial.cs | 51 +++++ .../Test_ProbabilityLazyExclusive.cs | 187 ++++++++++++++++++ 3 files changed, 384 insertions(+) create mode 100644 Test_Numerics/Data/Statistics/Test_ProbabilityLazyExclusive.cs diff --git a/Numerics/Data/Statistics/Probability.cs b/Numerics/Data/Statistics/Probability.cs index 68708410..8c5e0d84 100644 --- a/Numerics/Data/Statistics/Probability.cs +++ b/Numerics/Data/Statistics/Probability.cs @@ -1202,6 +1202,152 @@ void TrimToUsed() return false; } + /// + /// The outcome of a lazily enumerated exclusive-probability expansion. + /// + public enum ExclusiveEnumerationStatus + { + /// Every combination was enumerated. + Complete, + + /// The inclusion-exclusion expansion converged; the deepest combinations were not enumerated. + Converged, + + /// The emitted-combination cap was reached; the remaining combinations were not enumerated. + Capped, + } + + /// + /// The lazily enumerated form of + /// : + /// the combinations are generated in order + /// rather than read from a materialized n·(2^n − 1) matrix, so the caller never + /// allocates one. + /// + /// An array of probabilities for each event; n = Count. + /// The caller-owned output list of exclusive event probabilities; cleared and refilled. + /// The caller-owned output list of indicator rows; rows of matching length are refilled in place, and the list is trimmed to the produced count. + /// True to emit the all-zero (no-event) combination first, carrying Π(1 − pᵢ). Excluded from the inclusion-exclusion bracket. + /// The cap on emitted rows, excluding any closing row; non-positive means no cap. + /// The absolute tolerance for evaluation convergence of the inclusion-exclusion algorithm. Default = 1E-4. + /// The relative tolerance for evaluation convergence of the inclusion-exclusion algorithm. Default = 1E-4. + /// Whether the expansion completed, converged early, or hit the cap. + /// Thrown when either output list is null. + /// Thrown when the probabilities list is null or empty. + /// + /// Emits the same rows, in the same order, with the same probabilities as the dense + /// overload. On convergence it closes with the same half-gap pseudo-row; at the cap it + /// closes with the exact residual 1 − Σ(emitted), which for independent events is + /// the mass of everything not enumerated. Both closing rows are attributed to the all-ones + /// combination. + /// + public static ExclusiveEnumerationStatus IndependentExclusiveLazy(IList probabilities, + List eventProbabilities, List eventIndicators, bool includeNoEventRow = false, + long maxEmittedCombinations = 0, double absoluteTolerance = 1E-4, double relativeTolerance = 1E-4) + { + if (probabilities == null || probabilities.Count == 0) + throw new ArgumentException("The probabilities array must have a length greater than 0.", nameof(probabilities)); + if (eventProbabilities == null) throw new ArgumentNullException(nameof(eventProbabilities)); + if (eventIndicators == null) throw new ArgumentNullException(nameof(eventIndicators)); + + int n = probabilities.Count; + int used = 0; + eventProbabilities.Clear(); + + int[] Row() + { + int[] row; + if (used < eventIndicators.Count && eventIndicators[used] != null && eventIndicators[used].Length == n) + { + row = eventIndicators[used]; + Array.Clear(row, 0, n); + } + else + { + row = new int[n]; + if (used < eventIndicators.Count) eventIndicators[used] = row; + else eventIndicators.Add(row); + } + used++; + return row; + } + + void TrimToUsed() + { + while (eventIndicators.Count > used) eventIndicators.RemoveAt(eventIndicators.Count - 1); + } + + // Closes the expansion on an all-ones row carrying the supplied mass. + ExclusiveEnumerationStatus Close(double mass, ExclusiveEnumerationStatus status) + { + var row = Row(); + for (int column = 0; column < n; column++) row[column] = 1; + eventProbabilities.Add(mass); + TrimToUsed(); + return status; + } + + double emittedMass = 0d; + if (includeNoEventRow) + { + var noEvent = Row(); + double none = IndependentExclusive(probabilities, noEvent); + eventProbabilities.Add(none); + emittedMass += none; + } + + double union = 0; + double s = 1; + double inc = double.NaN; + double exc = double.NaN; + long emitted = 0; + + for (int k = 1; k <= n; k++) + { + // The size-block transition, mirroring the dense form: the bracket is read at the + // first row of each size from two upward, and the sign alternates per block. + if (k >= 2) + { + int block = k - 2; + if (block > 0) + { + if (s == 1) inc = union; + else if (s == -1) exc = union; + } + double diff = Math.Abs(inc - exc); + if (block > 0 && block < n && diff <= absoluteTolerance && diff <= relativeTolerance * Math.Min(inc, exc)) + { + return Close(0.5 * diff, ExclusiveEnumerationStatus.Converged); + } + s *= -1; + } + + var combination = new int[k]; + for (int t = 0; t < k; t++) combination[t] = t; + do + { + if (maxEmittedCombinations > 0 && emitted >= maxEmittedCombinations) + { + return Close(Math.Max(0d, 1d - emittedMass), ExclusiveEnumerationStatus.Capped); + } + + var row = Row(); + for (int t = 0; t < k; t++) row[combination[t]] = 1; + + double exclusive = IndependentExclusive(probabilities, row); + eventProbabilities.Add(exclusive); + emittedMass += exclusive; + emitted++; + + union += s * (k == 1 ? probabilities[combination[0]] : IndependentJointProbability(probabilities, row)); + } + while (Factorial.NextCombination(combination, n)); + } + + TrimToUsed(); + return ExclusiveEnumerationStatus.Complete; + } + #endregion #region Positively Dependent diff --git a/Numerics/Mathematics/Special Functions/Factorial.cs b/Numerics/Mathematics/Special Functions/Factorial.cs index 6726fdbe..5e4b39c5 100644 --- a/Numerics/Mathematics/Special Functions/Factorial.cs +++ b/Numerics/Mathematics/Special Functions/Factorial.cs @@ -183,5 +183,56 @@ public static IEnumerable FindCombinations(int m, int n) return output; } + /// + /// Advances a k-combination over [0, n) to its lexicographic successor, in place. + /// + /// The current strictly increasing index tuple; advanced in place. + /// The overall count. + /// False when the combination was the last of its size. + /// Thrown when the combination is null. + public static bool NextCombination(int[] combination, int n) + { + if (combination == null) throw new ArgumentNullException(nameof(combination)); + int k = combination.Length; + if (k == 0 || k > n) return false; + + int i = k - 1; + while (i >= 0 && combination[i] == n - k + i) i--; + if (i < 0) return false; + combination[i]++; + for (int j = i + 1; j < k; j++) combination[j] = combination[j - 1] + 1; + return true; + } + + /// + /// Enumerates every non-empty subset of n items as an index tuple, in the order + /// lays out its rows — subset size ascending, then + /// lexicographic — without materializing the n·(2^n − 1) matrix. + /// + /// The overall count. + /// The index tuples, in row order. + /// Thrown when n is negative. + /// + /// Yields a reused buffer, as does; copy it to + /// retain it. Unlike there is no upper bound on n, since + /// nothing is allocated per row — the caller decides how far to enumerate. + /// + public static IEnumerable AllCombinationsLazy(int n) + { + if (n < 0) + throw new ArgumentOutOfRangeException(nameof(n), "n must be non-negative."); + + for (int k = 1; k <= n; k++) + { + var combination = new int[k]; + for (int j = 0; j < k; j++) combination[j] = j; + do + { + yield return combination; + } + while (NextCombination(combination, n)); + } + } + } } \ No newline at end of file diff --git a/Test_Numerics/Data/Statistics/Test_ProbabilityLazyExclusive.cs b/Test_Numerics/Data/Statistics/Test_ProbabilityLazyExclusive.cs new file mode 100644 index 00000000..cb5e012b --- /dev/null +++ b/Test_Numerics/Data/Statistics/Test_ProbabilityLazyExclusive.cs @@ -0,0 +1,187 @@ +using System; +using System.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Data.Statistics; +using Numerics.Mathematics.SpecialFunctions; + +namespace Data.Statistics +{ + /// + /// Unit tests for the lazily enumerated exclusive expansion (N13): row-order equivalence with + /// , output parity with the dense overload, the + /// emitted-combination cap, and enumeration past the dense form's dimension ceiling. + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + [TestClass] + public class Test_ProbabilityLazyExclusive + { + /// Builds the binomial combination counts for n events. + private static int[] BinomialCounts(int n) + { + var counts = new int[n]; + for (int i = 1; i <= n; i++) counts[i - 1] = (int)Math.Round(Factorial.BinomialCoefficient(n, i)); + return counts; + } + + /// + /// Verifies the lazy generator reproduces the dense combination matrix row for row. + /// + [TestMethod] + public void Test_AllCombinationsLazy_MatchesDenseRowOrder() + { + for (int n = 1; n <= 12; n++) + { + var dense = Factorial.AllCombinations(n); + int row = 0; + foreach (var combination in Factorial.AllCombinationsLazy(n)) + { + var expanded = new int[n]; + for (int t = 0; t < combination.Length; t++) expanded[combination[t]] = 1; + for (int column = 0; column < n; column++) + { + Assert.AreEqual(dense[row, column], expanded[column], $"n={n}, row {row}, column {column}"); + } + row++; + } + Assert.AreEqual(dense.GetLength(0), row, $"n={n} row count"); + } + } + + /// + /// Verifies the lazy expansion emits the same rows and probabilities as the dense overload + /// when the expansion runs to completion. + /// + [TestMethod] + public void Test_LazyExclusive_MatchesDense_WhenComplete() + { + // Probabilities small enough that the bracket cannot converge before the last size. + var probabilities = new double[] { 0.4d, 0.35d, 0.3d, 0.25d, 0.2d }; + int n = probabilities.Length; + + var denseProbabilities = new List(); + var denseIndicators = new List(); + bool truncated = Probability.IndependentExclusive(probabilities, BinomialCounts(n), + Factorial.AllCombinations(n), denseProbabilities, denseIndicators, 0d, 0d); + Assert.IsFalse(truncated); + + var lazyProbabilities = new List(); + var lazyIndicators = new List(); + var status = Probability.IndependentExclusiveLazy(probabilities, lazyProbabilities, lazyIndicators, + includeNoEventRow: false, maxEmittedCombinations: 0, absoluteTolerance: 0d, relativeTolerance: 0d); + + Assert.AreEqual(Probability.ExclusiveEnumerationStatus.Complete, status); + Assert.HasCount(denseProbabilities.Count, lazyProbabilities); + for (int i = 0; i < denseProbabilities.Count; i++) + { + Assert.AreEqual(BitConverter.DoubleToInt64Bits(denseProbabilities[i]), + BitConverter.DoubleToInt64Bits(lazyProbabilities[i]), $"probability at row {i}"); + CollectionAssert.AreEqual(denseIndicators[i], lazyIndicators[i], $"indicators at row {i}"); + } + } + + /// + /// Verifies the exclusive probabilities sum to one over a complete expansion, with the + /// no-event row included. + /// + [TestMethod] + public void Test_LazyExclusive_WithNoEventRow_SumsToOne() + { + var probabilities = new double[] { 0.4d, 0.35d, 0.3d, 0.25d }; + var eventProbabilities = new List(); + var eventIndicators = new List(); + + var status = Probability.IndependentExclusiveLazy(probabilities, eventProbabilities, eventIndicators, + includeNoEventRow: true, maxEmittedCombinations: 0, absoluteTolerance: 0d, relativeTolerance: 0d); + + Assert.AreEqual(Probability.ExclusiveEnumerationStatus.Complete, status); + Assert.HasCount(1 << probabilities.Length, eventProbabilities); + double total = 0d; + for (int i = 0; i < eventProbabilities.Count; i++) total += eventProbabilities[i]; + Assert.AreEqual(1d, total, 1e-12); + } + + /// + /// Verifies the cap stops the expansion and closes with the exact unenumerated mass. + /// + [TestMethod] + public void Test_LazyExclusive_Capped_ClosesWithResidualMass() + { + var probabilities = new double[] { 0.4d, 0.35d, 0.3d, 0.25d, 0.2d, 0.15d }; + var eventProbabilities = new List(); + var eventIndicators = new List(); + + var status = Probability.IndependentExclusiveLazy(probabilities, eventProbabilities, eventIndicators, + includeNoEventRow: true, maxEmittedCombinations: 10, absoluteTolerance: 0d, relativeTolerance: 0d); + + Assert.AreEqual(Probability.ExclusiveEnumerationStatus.Capped, status); + + // Ten enumerated combinations, plus the no-event row, plus the closing row. + Assert.HasCount(12, eventProbabilities); + double total = 0d; + for (int i = 0; i < eventProbabilities.Count; i++) total += eventProbabilities[i]; + Assert.AreEqual(1d, total, 1e-12, "The closing row must carry exactly the unenumerated mass."); + + // The residual is attributed to the all-ones combination. + var closing = eventIndicators[eventIndicators.Count - 1]; + for (int i = 0; i < closing.Length; i++) Assert.AreEqual(1, closing[i]); + } + + /// + /// Verifies the expansion runs beyond the dimension at which the dense matrix cannot be + /// built, converging on realistic failure probabilities rather than enumerating 2^n rows. + /// + [TestMethod] + public void Test_LazyExclusive_BeyondDenseDimensionLimit() + { + // The dense form throws above thirty events; nothing here allocates per row. + const int n = 40; + Assert.Throws(() => Factorial.AllCombinations(n)); + + // Failure probabilities of the order a risk model carries, where the bracket closes + // after the triples. + var probabilities = new double[n]; + for (int i = 0; i < n; i++) probabilities[i] = 5e-4d + i * 1e-5d; + + var eventProbabilities = new List(); + var eventIndicators = new List(); + var status = Probability.IndependentExclusiveLazy(probabilities, eventProbabilities, eventIndicators, + includeNoEventRow: true, maxEmittedCombinations: 1_000_000); + + // Converged on the bracket rather than stopping at the cap, having enumerated a + // vanishing fraction of the 2^40 combinations the dense form would have needed. + Assert.AreEqual(Probability.ExclusiveEnumerationStatus.Converged, status); + double enumeratedFraction = eventProbabilities.Count / Math.Pow(2d, n); + Assert.IsLessThanOrEqualTo(1e-5, enumeratedFraction, + $"Emitted {eventProbabilities.Count} of 2^{n} combinations."); + } + + /// + /// Verifies indicator row arrays are reused across calls, so a repeated caller allocates + /// no output rows after the first call. + /// + [TestMethod] + public void Test_LazyExclusive_ReusesIndicatorRows() + { + var probabilities = new double[] { 0.3d, 0.25d, 0.2d }; + var eventProbabilities = new List(); + var eventIndicators = new List(); + + Probability.IndependentExclusiveLazy(probabilities, eventProbabilities, eventIndicators, + absoluteTolerance: 0d, relativeTolerance: 0d); + var first = new int[eventIndicators.Count][]; + eventIndicators.CopyTo(first); + + Probability.IndependentExclusiveLazy(probabilities, eventProbabilities, eventIndicators, + absoluteTolerance: 0d, relativeTolerance: 0d); + + Assert.HasCount(first.Length, eventIndicators); + for (int i = 0; i < first.Length; i++) + { + Assert.AreSame(first[i], eventIndicators[i], $"Row {i} was reallocated."); + } + } + } +} From 1346baf0bc4ad5c18ceb7bf0ff9e6a98c3acb6fb Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sun, 26 Jul 2026 19:04:21 -0600 Subject: [PATCH 017/222] Raise the Vegas dimension guard from 20 to 50 The constructor refused more than twenty dimensions. Nothing structural required that: every internal array sizes from Dimensions, and the Sobol sequence supports 21,201. It was also stricter than the reference implementations -- GSL, Cuba and Lepage's vegas impose no dimension cap at all, and Lepage's largest documented example is itself twenty-dimensional. The algorithm already degrades the way those implementations rely on. The importance-sampling grid is separable, so it holds NumberOfBins x D bins rather than bins^D, and the stratification self-limits: strata per axis are (calls/2)^(1/D), which reaches one around fifteen dimensions, after which the run is pure adaptive importance sampling. At twenty dimensions with ten thousand calls it is already in that regime, so raising the guard changes nothing about how it behaves there. Kept as a guard rather than removed, so a runaway input still fails fast. The new test integrates the mean of 2*x_i over thirty dimensions. A product of the same factors would concentrate its mass in one corner and is hopeless at that dimension for any sample budget -- the curse of dimensionality rather than a property of the integrator -- which is worth knowing before reaching for high dimensions. Gates: build 0 warnings; 1955/1955 on net10.0, net9.0, net8.0 and net481. --- Numerics/Mathematics/Integration/Vegas.cs | 14 ++++++- .../Mathematics/Integration/Test_Vegas.cs | 37 ++++++++++++++++++- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/Numerics/Mathematics/Integration/Vegas.cs b/Numerics/Mathematics/Integration/Vegas.cs index afac4193..dd1870f0 100644 --- a/Numerics/Mathematics/Integration/Vegas.cs +++ b/Numerics/Mathematics/Integration/Vegas.cs @@ -59,7 +59,7 @@ public Vegas(Func function, int dimensions, IList MaxDimensions) - throw new ArgumentOutOfRangeException(nameof(dimensions), "The maximum number of dimensions is 20."); + throw new ArgumentOutOfRangeException(nameof(dimensions), $"The maximum number of dimensions is {MaxDimensions}."); // Check if the minimum values are less than the maximum values for (int i = 0; i < min.Count; i++) @@ -89,7 +89,17 @@ public Vegas(Func function, int dimensions, IList + /// The largest supported spatial dimension. A guard against runaway inputs rather than an + /// algorithmic bound: the importance-sampling grid is separable, so it holds + /// × D bins rather than bins^D, and the stratification + /// self-limits — strata per axis are (calls/2)^(1/D), which reaches one around fifteen + /// dimensions, at which point the algorithm runs as pure adaptive importance sampling. + /// That is the same graceful degradation GSL and Lepage's reference implementation rely + /// on; neither imposes a dimension cap. + /// + private const int MaxDimensions = 50; private const double TinyValue = 1.0e-30; // Algorithm Variables diff --git a/Test_Numerics/Mathematics/Integration/Test_Vegas.cs b/Test_Numerics/Mathematics/Integration/Test_Vegas.cs index cd7a55e7..f81bd77f 100644 --- a/Test_Numerics/Mathematics/Integration/Test_Vegas.cs +++ b/Test_Numerics/Mathematics/Integration/Test_Vegas.cs @@ -291,6 +291,41 @@ public void Test_PowerTransform_ProbabilityRange() } - } + + /// + /// Integrates a separable product above twenty dimensions, where the stratification + /// self-limits to one stratum per axis and the algorithm runs as pure adaptive importance + /// sampling. + /// + [TestMethod()] + public void Test_HighDimension() + { + const int dimensions = 30; + var min = new double[dimensions]; + var max = new double[dimensions]; + for (int i = 0; i < dimensions; i++) { min[i] = 0d; max[i] = 1d; } + + // Mean of 2*x_i over the axes; the exact integral over the unit cube is one. A + // PRODUCT of the same factors would concentrate its mass in a single corner and is + // hopeless at this dimension for any sample budget -- the curse of dimensionality, + // not a property of the integrator. + var vegas = new Vegas((x, w) => + { + double sum = 0d; + for (int i = 0; i < dimensions; i++) sum += 2d * x[i]; + return sum / dimensions; + }, dimensions, min, max) + { + UseSobolSequence = false, + Random = new Numerics.Sampling.MersenneTwister(12345), + FunctionCalls = 20000, + MaxIterations = 10, + }; + + vegas.Integrate(); + + Assert.AreEqual(1d, vegas.Result, 0.01d); + } +} } From a076538f1e93c906be78187068259abfd9710741 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sun, 26 Jul 2026 19:20:58 -0600 Subject: [PATCH 018/222] Pool the Gauss-Kronrod recorder capture buffers With a recorder attached, every interval allocated two 21-element buffers to hold its nodes and values -- including the rejected intervals, which are the majority under adaptive refinement. That is a per-evaluation allocation introduced purely by observing the integration. The buffers now come from a per-slot pool grown on demand. Depth-first recursion means the only intervals alive at once are the two halves of the current interval and their ancestors, so a slot index of 2*level+1 and 2*level+2 is unique among live intervals and every subtree reuses the same slots after its sibling finishes. Nothing is allocated when no recorder is attached. Gates: build 0 warnings; 1955/1955 on all four TFMs, including the recorder invariants -- weights summing to the domain width and the weighted node sum reproducing the result -- under forced deep subdivision. --- .../Integration/AdaptiveGuassKronrod.cs | 57 +++++++++++++++---- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/Numerics/Mathematics/Integration/AdaptiveGuassKronrod.cs b/Numerics/Mathematics/Integration/AdaptiveGuassKronrod.cs index eec43292..b485d367 100644 --- a/Numerics/Mathematics/Integration/AdaptiveGuassKronrod.cs +++ b/Numerics/Mathematics/Integration/AdaptiveGuassKronrod.cs @@ -102,6 +102,34 @@ public AdaptiveGaussKronrod(Func function, double min, double ma // then the symmetric pair for each abscissa index i occupies slots 2i+1 and 2i+2. private static readonly double[] wCapture = BuildCaptureWeights(); + /// + /// Per-slot capture buffers for the recorder's node abscissas, grown on demand. Only + /// allocated when is set. + /// + private double[]?[] _nodePool = Array.Empty(); + + /// + /// Per-slot capture buffers for the recorder's node values. + /// + private double[]?[] _valuePool = Array.Empty(); + + /// + /// Returns the capture buffer for a recursion slot, growing the pool as needed. + /// + /// The pool to rent from. + /// The slot index; distinct for every interval alive at once. + /// The buffer. + private static double[] RentCapture(ref double[]?[] pool, int slot) + { + if (slot >= pool.Length) + { + var grown = new double[]?[Math.Max(slot + 1, pool.Length * 2 + 8)]; + Array.Copy(pool, grown, pool.Length); + pool = grown; + } + return pool[slot] ??= new double[21]; + } + /// /// Builds the unscaled Kronrod weight layout matching the node-capture order. /// @@ -179,10 +207,10 @@ public override void Integrate() try { // Initial evaluation using Gauss-Kronrod rule on the whole interval - var (kronrodResult, gaussResult, nodes, values) = EvaluateGaussKronrod(a, b); + var (kronrodResult, gaussResult, nodes, values) = EvaluateGaussKronrod(a, b, 0); // Recursively sub-divide - Result = AdaptiveGK(Function, a, b, MaxDepth, kronrodResult, gaussResult, a, b, nodes, values); + Result = AdaptiveGK(Function, a, b, MaxDepth, kronrodResult, gaussResult, a, b, nodes, values, 0); // Standard error calculated after recursion completes StandardError = Math.Sqrt(_squaredError); @@ -222,10 +250,10 @@ public void Integrate(List bins) // Initial evaluation using Gauss-Kronrod rule on the bin interval double binA = bins[i].LowerBound; double binB = bins[i].UpperBound; - var (kronrodResult, gaussResult, nodes, values) = EvaluateGaussKronrod(binA, binB); + var (kronrodResult, gaussResult, nodes, values) = EvaluateGaussKronrod(binA, binB, 0); // Recursively sub-divide - mu += AdaptiveGK(Function, binA, binB, MaxDepth, kronrodResult, gaussResult, binA, binB, nodes, values); + mu += AdaptiveGK(Function, binA, binB, MaxDepth, kronrodResult, gaussResult, binA, binB, nodes, values, 0); } // Final result and standard error @@ -257,16 +285,17 @@ public void Integrate(List bins) /// /// The lower bound of integration. /// The upper bound of integration. + /// The capture-buffer slot; distinct for every interval alive at once. /// A tuple containing (Kronrod estimate, Gauss estimate, captured nodes, captured values). - private (double kronrod, double gauss, double[]? nodes, double[]? values) EvaluateGaussKronrod(double a, double b) + private (double kronrod, double gauss, double[]? nodes, double[]? values) EvaluateGaussKronrod(double a, double b, int slot) { double center = 0.5 * (a + b); double halfLength = 0.5 * (b - a); double resultGauss = 0.0; double resultKronrod = 0.0; - double[]? nodes = Recorder != null ? new double[21] : null; - double[]? values = Recorder != null ? new double[21] : null; + double[]? nodes = Recorder != null ? RentCapture(ref _nodePool, slot) : null; + double[]? values = Recorder != null ? RentCapture(ref _valuePool, slot) : null; // Evaluate at center point (x = 0) double f0 = Function(center); @@ -325,13 +354,14 @@ public void Integrate(List bins) /// The original upper bound of the integral. /// The interval's captured node abscissas (null when no recorder is attached). /// The interval's captured function values (null when no recorder is attached). + /// The recursion level, which selects this interval's children's capture slots. /// /// An evaluation of the integral using adaptive Gauss-Kronrod with error less than the specified tolerance. /// This is accomplished by subdividing the interval until the error between the Gauss and Kronrod estimates /// is sufficiently small. /// private double AdaptiveGK(Func f, double a, double b, int depth, - double kronrodWhole, double gaussWhole, double a0, double b0, double[]? nodes, double[]? values) + double kronrodWhole, double gaussWhole, double a0, double b0, double[]? nodes, double[]? values, int level) { // Error estimate: difference between Kronrod and Gauss results double error = Math.Abs(kronrodWhole - gaussWhole); @@ -370,14 +400,17 @@ private double AdaptiveGK(Func f, double a, double b, int depth, double m = (a + b) / 2.0; // Evaluate Gauss-Kronrod on left half - var (kronrodLeft, gaussLeft, nodesLeft, valuesLeft) = EvaluateGaussKronrod(a, m); + // Both halves are evaluated before either recurses, so they need distinct capture + // slots; their own children take the slots of the next level, which are free by + // the time each subtree runs. + var (kronrodLeft, gaussLeft, nodesLeft, valuesLeft) = EvaluateGaussKronrod(a, m, 2 * level + 1); // Evaluate Gauss-Kronrod on right half - var (kronrodRight, gaussRight, nodesRight, valuesRight) = EvaluateGaussKronrod(m, b); + var (kronrodRight, gaussRight, nodesRight, valuesRight) = EvaluateGaussKronrod(m, b, 2 * level + 2); // Recursively subdivide the intervals and accumulate results - var leftResult = AdaptiveGK(f, a, m, depth - 1, kronrodLeft, gaussLeft, a0, b0, nodesLeft, valuesLeft); - var rightResult = AdaptiveGK(f, m, b, depth - 1, kronrodRight, gaussRight, a0, b0, nodesRight, valuesRight); + var leftResult = AdaptiveGK(f, a, m, depth - 1, kronrodLeft, gaussLeft, a0, b0, nodesLeft, valuesLeft, level + 1); + var rightResult = AdaptiveGK(f, m, b, depth - 1, kronrodRight, gaussRight, a0, b0, nodesRight, valuesRight, level + 1); return leftResult + rightResult; } From 60580044b79f5c65a809ff9eebf2c35a7e19079e Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sun, 26 Jul 2026 20:50:21 -0600 Subject: [PATCH 019/222] Fill failed replications in the processed parameter sets ProcessParameterSets left a null entry wherever a sampled distribution had failed to fit, while BootstrapAnalysis.ParameterSets filled the same slot with a NaN-valued set. ParameterSets is a public array that consumers index directly, so the two paths differed in whether a failed replication throws or propagates. Both now fill NaN, taking the parameter count from the parent distribution. Also merges the mean curve's min/max extremes once per worker instead of once per distribution. Min and max are order-independent, so the result is unchanged however the loop partitions. --- .../UncertaintyAnalysisResults.cs | 52 +++++++++++++++---- .../Univariate/Test_BootstrapAnalysis.cs | 31 +++++++++++ 2 files changed, 73 insertions(+), 10 deletions(-) diff --git a/Numerics/Distributions/Univariate/Uncertainty Analysis/UncertaintyAnalysisResults.cs b/Numerics/Distributions/Univariate/Uncertainty Analysis/UncertaintyAnalysisResults.cs index caac76a4..586a71c3 100644 --- a/Numerics/Distributions/Univariate/Uncertainty Analysis/UncertaintyAnalysisResults.cs +++ b/Numerics/Distributions/Univariate/Uncertainty Analysis/UncertaintyAnalysisResults.cs @@ -422,25 +422,31 @@ public void ProcessMeanCurve(UnivariateDistributionBase[] sampledDistributions, int B = sampledDistributions.Length; - // Compute min and max X values across all distributions + // Compute min and max X values across all distributions. The extremes merge once per + // worker rather than once per distribution; min and max are order-independent, so the + // result is the same however the loop partitions. double minX = double.MaxValue; double maxX = double.MinValue; object lockObject = new object(); - Parallel.For(0, B, j => - { - if (sampledDistributions[j] is not null) + Parallel.For(0, B, + () => (Min: double.MaxValue, Max: double.MinValue), + (j, state, local) => { + if (sampledDistributions[j] is null) return local; var innerMin = sampledDistributions[j].InverseCDF(minProbability); var innerMax = sampledDistributions[j].InverseCDF(maxProbability); - + return (innerMin < local.Min ? innerMin : local.Min, + innerMax > local.Max ? innerMax : local.Max); + }, + local => + { lock (lockObject) { - if (innerMin < minX) minX = innerMin; - if (innerMax > maxX) maxX = innerMax; + if (local.Min < minX) minX = local.Min; + if (local.Max > maxX) maxX = local.Max; } - } - }); + }); // Create log-spaced quantiles for efficient coverage double shift = minX <= 0 ? Math.Abs(minX) + 1d : 0; @@ -527,15 +533,35 @@ public void ProcessMeanCurve(UnivariateDistributionBase[] sampledDistributions, } /// - /// Processes and stores the parameter sets from all sampled distributions. + /// Processes and stores the parameter sets from all sampled distributions. A sampled + /// distribution that failed to fit contributes a parameter set of NaN values rather than a + /// null entry, so a consumer indexing gets a value that + /// propagates as NaN instead of throwing. BootstrapAnalysis.ParameterSets fills + /// failures the same way. /// /// Array of sampled distributions to extract parameters from. + /// Thrown when the sampled distributions are null, empty, or all failed. public void ProcessParameterSets(UnivariateDistributionBase[] sampledDistributions) { if (sampledDistributions == null || sampledDistributions.Length == 0) throw new ArgumentException("Sampled distributions cannot be null or empty.", nameof(sampledDistributions)); int B = sampledDistributions.Length; + int numberOfParameters = ParentDistribution?.NumberOfParameters ?? 0; + if (numberOfParameters == 0) + { + for (int i = 0; i < B; i++) + { + if (sampledDistributions[i] is not null) + { + numberOfParameters = sampledDistributions[i].NumberOfParameters; + break; + } + } + } + if (numberOfParameters == 0) + throw new ArgumentException("Every sampled distribution is null; the parameter count is unknown.", nameof(sampledDistributions)); + ParameterSets = new ParameterSet[B]; Parallel.For(0, B, idx => @@ -544,6 +570,12 @@ public void ProcessParameterSets(UnivariateDistributionBase[] sampledDistributio { ParameterSets[idx] = new ParameterSet(sampledDistributions[idx].GetParameters, double.NaN); } + else + { + var parameters = new double[numberOfParameters]; + for (int i = 0; i < numberOfParameters; i++) parameters[i] = double.NaN; + ParameterSets[idx] = new ParameterSet(parameters, double.NaN); + } }); } diff --git a/Test_Numerics/Distributions/Univariate/Test_BootstrapAnalysis.cs b/Test_Numerics/Distributions/Univariate/Test_BootstrapAnalysis.cs index b0213ab7..7654d59f 100644 --- a/Test_Numerics/Distributions/Univariate/Test_BootstrapAnalysis.cs +++ b/Test_Numerics/Distributions/Univariate/Test_BootstrapAnalysis.cs @@ -250,5 +250,36 @@ public void Test_ExpectedProbabilities_IsThreadCountIndependent() } } + /// + /// A sampled distribution that failed to fit contributes a NaN parameter set rather than a + /// null entry, matching BootstrapAnalysis.ParameterSets. Consumers index the array directly. + /// + [TestMethod] + public void Test_ProcessParameterSets_FillsFailuresWithNaN() + { + var probabilities = new double[] { 0.99, 0.9, 0.5, 0.1, 0.01 }; + var dist = new Normal(3.122599, 0.5573654); + var boot = new BootstrapAnalysis(dist, ParameterEstimationMethod.MethodOfMoments, 100, 200); + var sampled = boot.Distributions().Cast().ToArray(); + sampled[7] = null; + + var results = new UncertaintyAnalysisResults(dist, sampled, probabilities, recordParameterSets: true); + var reference = boot.ParameterSets(sampled.Cast().ToArray()); + + Assert.HasCount(sampled.Length, results.ParameterSets); + for (int i = 0; i < sampled.Length; i++) + { + Assert.IsNotNull(results.ParameterSets[i], $"Parameter set {i} is null."); + Assert.HasCount(dist.NumberOfParameters, results.ParameterSets[i].Values); + for (int j = 0; j < dist.NumberOfParameters; j++) + { + Assert.AreEqual(BitConverter.DoubleToInt64Bits(reference[i].Values[j]), + BitConverter.DoubleToInt64Bits(results.ParameterSets[i].Values[j]), + $"Parameter set {i} value {j} differs from the BootstrapAnalysis form."); + } + } + Assert.IsTrue(double.IsNaN(results.ParameterSets[7].Values[0])); + } + } } From 6c551d13d368371d5b0b3cbfbeb18d3a78e25480 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 27 Jul 2026 04:46:33 -0600 Subject: [PATCH 020/222] Fix adaptive MCMC diagnostics and covariance tracking --- Numerics/Sampling/MCMC/ARWMH.cs | 48 ++-- Numerics/Sampling/MCMC/Base/MCMCSampler.cs | 24 +- Numerics/Sampling/MCMC/NUTS.cs | 233 ++++++++++++++- Numerics/Sampling/MCMC/Support/MCMCResults.cs | 56 +++- .../Sampling/MCMC/Test_MCMCSamplerFindings.cs | 268 ++++++++++++++++++ 5 files changed, 588 insertions(+), 41 deletions(-) create mode 100644 Test_Numerics/Sampling/MCMC/Test_MCMCSamplerFindings.cs diff --git a/Numerics/Sampling/MCMC/ARWMH.cs b/Numerics/Sampling/MCMC/ARWMH.cs index 492575f9..e8608d26 100644 --- a/Numerics/Sampling/MCMC/ARWMH.cs +++ b/Numerics/Sampling/MCMC/ARWMH.cs @@ -133,44 +133,34 @@ protected override ParameterSet ChainIteration(int index, ParameterSet state) // Get proposal vector var xp = mvn[index].InverseCDF(_chainPRNGs[index].NextDoubles(NumberOfParameters)); - // Check if the parameter is feasible (within the constraints) + // Determine whether the proposal is feasible before evaluating the target. + bool isFeasible = true; for (int i = 0; i < NumberOfParameters; i++) { if (xp[i] < PriorDistributions[i].Minimum || xp[i] > PriorDistributions[i].Maximum) { - // The proposed parameter vector was infeasible, so leave xi unchanged. - // Adapt Covariance Matrix after warmup - if (SampleCount[index] > ThinningInterval * WarmupIterations) - sigma[index].Push(state.Values); - return state; + isFeasible = false; + break; } } - // Evaluate fitness - var logLHp = LogLikelihoodFunction(xp); - var logLHi = state.Fitness; - - // Calculate the Metropolis ratio - var logRatio = logLHp - logLHi; - - // Accept the proposal with probability min(1,r) - // otherwise leave xi unchanged - var logU = Math.Log(_chainPRNGs[index].NextDouble()); - if (logU <= logRatio) - { - // The proposal is accepted - AcceptCount[index] += 1; - // Adapt Covariance Matrix - sigma[index].Push(xp); - return new ParameterSet(xp, logLHp); - } - else + ParameterSet retainedState = state; + if (isFeasible) { - // Adapt Covariance Matrix after warmup - if (SampleCount[index] > ThinningInterval * WarmupIterations) - sigma[index].Push(state.Values); - return state; + double proposedLogLikelihood = LogLikelihoodFunction(xp); + double logRatio = proposedLogLikelihood - state.Fitness; + double logUniform = Math.Log(_chainPRNGs[index].NextDouble()); + if (logUniform <= logRatio) + { + AcceptCount[index] += 1; + retainedState = new ParameterSet(xp, proposedLogLikelihood); + } } + + // Adaptive Metropolis covariance is based on the realized chain. Rejected + // and infeasible proposals therefore contribute the repeated retained state. + sigma[index].Push(retainedState.Values); + return retainedState; } } diff --git a/Numerics/Sampling/MCMC/Base/MCMCSampler.cs b/Numerics/Sampling/MCMC/Base/MCMCSampler.cs index 52d1f370..19628e38 100644 --- a/Numerics/Sampling/MCMC/Base/MCMCSampler.cs +++ b/Numerics/Sampling/MCMC/Base/MCMCSampler.cs @@ -295,15 +295,27 @@ public enum InitializationType /// /// The acceptance rate per chain. /// - public double[] AcceptanceRates + public double[] AcceptanceRates => ComputeAcceptanceRates(); + + /// + /// Computes the sampler-appropriate acceptance statistic for each chain. + /// + /// The acceptance statistic for each chain. + /// + /// Metropolis samplers use accepted proposal counts. Hamiltonian samplers may + /// override this hook when their meaningful statistic is a mean trajectory + /// acceptance probability rather than a binary retained-state count. + /// + protected virtual double[] ComputeAcceptanceRates() { - get + var acceptanceRates = new double[NumberOfChains]; + for (int i = 0; i < NumberOfChains; i++) { - var ar = new double[NumberOfChains]; - for (int i = 0; i < NumberOfChains; i++) - ar[i] = SampleCount[i] > 0 ? (double)AcceptCount[i] / (double)SampleCount[i] : 0d; - return ar; + acceptanceRates[i] = SampleCount[i] > 0 + ? (double)AcceptCount[i] / SampleCount[i] + : 0d; } + return acceptanceRates; } /// diff --git a/Numerics/Sampling/MCMC/NUTS.cs b/Numerics/Sampling/MCMC/NUTS.cs index 263cdc32..6ffc2d2d 100644 --- a/Numerics/Sampling/MCMC/NUTS.cs +++ b/Numerics/Sampling/MCMC/NUTS.cs @@ -131,6 +131,20 @@ public NUTS(List priorDistributions, LogLikelihood logL private double[] _chainMu = null!; private int[] _chainAdaptStep = null!; + // Per-chain post-warmup diagnostics. These are streaming accumulators so + // diagnostic collection adds no target/gradient evaluations or draw storage. + private double[] _hamiltonianAcceptanceSums = null!; + private int[] _diagnosticSampleCounts = null!; + private int[] _divergenceCounts = null!; + private int[] _maxTreeDepthHitCounts = null!; + private double[] _treeDepthSums = null!; + private double[] _leapfrogStepSums = null!; + private double[] _energyMeans = null!; + private double[] _energyM2 = null!; + private double[] _energySquaredDifferenceSums = null!; + private double[] _previousEnergy = null!; + private bool[] _hasPreviousEnergy = null!; + // Dual averaging hyperparameters (Hoffman & Gelman 2014, Section 3.2) private const double DELTA_TARGET = 0.80; private const double GAMMA = 0.05; @@ -172,6 +186,116 @@ public NUTS(List priorDistributions, LogLikelihood logL /// public double TargetAcceptanceRate => DELTA_TARGET; + /// + /// Gets the mean post-warmup Hamiltonian acceptance probability for each chain. + /// + /// + /// This is the mean tree acceptance statistic used by dual averaging, not the + /// fraction of NUTS iterations that returned a retained state. + /// + public double[] HamiltonianAcceptanceRates + { + get + { + var values = new double[NumberOfChains]; + for (int i = 0; i < NumberOfChains; i++) + { + values[i] = _diagnosticSampleCounts == null || _diagnosticSampleCounts[i] == 0 + ? 0d + : _hamiltonianAcceptanceSums[i] / _diagnosticSampleCounts[i]; + } + return values; + } + } + + /// + /// Gets the number of post-warmup transitions contributing diagnostics per chain. + /// + public int[] DiagnosticSampleCounts => _diagnosticSampleCounts == null + ? new int[NumberOfChains] + : (int[])_diagnosticSampleCounts.Clone(); + + /// + /// Gets the number of divergent post-warmup transitions per chain. + /// + public int[] DivergenceCounts => _divergenceCounts == null + ? new int[NumberOfChains] + : (int[])_divergenceCounts.Clone(); + + /// + /// Gets the number of post-warmup transitions that exhausted per chain. + /// + public int[] MaxTreeDepthHitCounts => _maxTreeDepthHitCounts == null + ? new int[NumberOfChains] + : (int[])_maxTreeDepthHitCounts.Clone(); + + /// + /// Gets the mean post-warmup tree depth for each chain. + /// + public double[] MeanTreeDepths => ComputeDiagnosticMeans(_treeDepthSums); + + /// + /// Gets the mean number of post-warmup leapfrog steps per transition for each chain. + /// + public double[] MeanLeapfrogSteps => ComputeDiagnosticMeans(_leapfrogStepSums); + + /// + /// Gets the current adapted leapfrog step size for each chain. + /// + public double[] StepSizes => _chainStepSizes == null + ? new double[NumberOfChains] + : (double[])_chainStepSizes.Clone(); + + /// + /// Gets the post-warmup energy Bayesian fraction of missing information for each chain. + /// + /// + /// E-BFMI is the mean squared successive Hamiltonian difference divided by + /// sample variance of the Hamiltonian, matching the Stan diagnostic definition. + /// + public double[] EnergyBayesianFractionOfMissingInformation + { + get + { + var values = new double[NumberOfChains]; + for (int i = 0; i < NumberOfChains; i++) + { + values[i] = ComputeEnergyBayesianFractionOfMissingInformation( + _diagnosticSampleCounts == null ? 0 : _diagnosticSampleCounts[i], + _energyM2 == null ? 0d : _energyM2[i], + _energySquaredDifferenceSums == null ? 0d : _energySquaredDifferenceSums[i]); + } + return values; + } + } + + /// + protected override double[] ComputeAcceptanceRates() + { + if (_diagnosticSampleCounts == null || _diagnosticSampleCounts.Length != NumberOfChains) + return base.ComputeAcceptanceRates(); + return HamiltonianAcceptanceRates; + } + + /// + /// Computes per-chain diagnostic means from streaming sums. + /// + /// Per-chain diagnostic sums. + /// The corresponding per-chain means. + private double[] ComputeDiagnosticMeans(double[] sums) + { + var values = new double[NumberOfChains]; + if (sums == null || _diagnosticSampleCounts == null) + return values; + + for (int i = 0; i < NumberOfChains; i++) + { + if (_diagnosticSampleCounts[i] > 0) + values[i] = sums[i] / _diagnosticSampleCounts[i]; + } + return values; + } + /// /// Gets or sets whether to adapt the diagonal mass matrix during warmup. /// When enabled, uses Stan-style windowed adaptation with Welford's online algorithm @@ -201,6 +325,19 @@ protected override void InitializeCustomSettings() _chainMu = new double[N]; _chainAdaptStep = new int[N]; + // Initialize post-warmup diagnostic accumulators. + _hamiltonianAcceptanceSums = new double[N]; + _diagnosticSampleCounts = new int[N]; + _divergenceCounts = new int[N]; + _maxTreeDepthHitCounts = new int[N]; + _treeDepthSums = new double[N]; + _leapfrogStepSums = new double[N]; + _energyMeans = new double[N]; + _energyM2 = new double[N]; + _energySquaredDifferenceSums = new double[N]; + _previousEnergy = new double[N]; + _hasPreviousEnergy = new bool[N]; + // Initialize diagonal mass matrix and Welford accumulators _welfordMean = new double[N][]; _welfordM2 = new double[N][]; @@ -435,6 +572,8 @@ protected override ParameterSet ChainIteration(int index, ParameterSet state) { // Update the sample count SampleCount[index] += 1; + int sampleNum = SampleCount[index]; + int warmupSteps = WarmupIterations * ThinningInterval; double eps = _chainStepSizes[index]; int D = NumberOfParameters; @@ -461,6 +600,9 @@ protected override ParameterSet ChainIteration(int index, ParameterSet state) int depth = 0; double sumAlpha = 0; int numAlpha = 0; + int leapfrogSteps = 0; + int trajectoryDepth = 0; + bool trajectoryDivergent = false; // Step 3: Build tree by doubling until U-turn or max depth while (depth < MaxTreeDepth) @@ -495,6 +637,9 @@ protected override ParameterSet ChainIteration(int index, ParameterSet state) logSumWeight = logSumWeightNew; } + leapfrogSteps += subtree.LeafCount; + trajectoryDepth = depth + 1; + trajectoryDivergent |= subtree.Divergent; // Accumulate adaptation statistics sumAlpha += subtree.SumAlpha; numAlpha += subtree.NumAlpha; @@ -510,15 +655,16 @@ protected override ParameterSet ChainIteration(int index, ParameterSet state) depth++; } + double averageAcceptanceProbability = numAlpha > 0 ? sumAlpha / numAlpha : 0d; // Step 4: Warmup adaptation (step size + mass matrix) - int warmupSteps = WarmupIterations * ThinningInterval; - int sampleNum = SampleCount[index]; - if (sampleNum <= warmupSteps) { // Always do dual averaging step size adaptation during warmup - double avgAlpha = numAlpha > 0 ? sumAlpha / numAlpha : DELTA_TARGET; - DualAveragingUpdate(index, avgAlpha); + // Preserve the original neutral fallback when no subtree contributed. + double adaptationAcceptanceProbability = numAlpha > 0 + ? averageAcceptanceProbability + : DELTA_TARGET; + DualAveragingUpdate(index, adaptationAcceptanceProbability); // Accumulate Welford statistics during mass matrix adaptation windows (Phase 2) if (AdaptMassMatrix && sampleNum > _initBuffer && sampleNum <= warmupSteps - _termBuffer) @@ -539,11 +685,83 @@ protected override ParameterSet ChainIteration(int index, ParameterSet state) _chainStepSizes[index] = Math.Exp(_chainLogEpsBar[index]); } + if (sampleNum > warmupSteps) + { + RecordDiagnostics( + index, + averageAcceptanceProbability, + trajectoryDivergent, + depth >= MaxTreeDepth, + trajectoryDepth, + leapfrogSteps, + H0); + } + // NUTS always accepts AcceptCount[index] += 1; return new ParameterSet(candidate.Array, candidateLogLH); } + /// + /// Records one post-warmup NUTS transition using constant-memory accumulators. + /// + /// Zero-based chain index. + /// Mean tree acceptance probability. + /// Whether the trajectory contained a divergence. + /// Whether tree construction exhausted the configured maximum depth. + /// Tree depth attempted by the transition. + /// Number of leapfrog steps built by the transition. + /// Initial Hamiltonian after momentum resampling. + private void RecordDiagnostics(int chainIndex, double acceptanceProbability, bool divergent, + bool hitMaximumDepth, int treeDepth, int leapfrogSteps, double energy) + { + int newCount = ++_diagnosticSampleCounts[chainIndex]; + _hamiltonianAcceptanceSums[chainIndex] += acceptanceProbability; + if (divergent) + _divergenceCounts[chainIndex]++; + if (hitMaximumDepth) + _maxTreeDepthHitCounts[chainIndex]++; + _treeDepthSums[chainIndex] += treeDepth; + _leapfrogStepSums[chainIndex] += leapfrogSteps; + + if (_hasPreviousEnergy[chainIndex]) + { + double energyDifference = energy - _previousEnergy[chainIndex]; + _energySquaredDifferenceSums[chainIndex] += energyDifference * energyDifference; + } + _previousEnergy[chainIndex] = energy; + _hasPreviousEnergy[chainIndex] = true; + + double delta = energy - _energyMeans[chainIndex]; + _energyMeans[chainIndex] += delta / newCount; + double centeredEnergy = energy - _energyMeans[chainIndex]; + _energyM2[chainIndex] += delta * centeredEnergy; + } + + /// + /// Computes E-BFMI from streaming energy accumulators. + /// + /// Number of energy observations. + /// Sum of squared deviations from the running mean. + /// Sum of squared successive energy differences. + /// E-BFMI, or when fewer than two energies or zero energy variance are available. + /// + /// The formula is mean(diff(E)^2) / var(E), with the same sample-variance + /// convention used by Stan interfaces. + /// + internal static double ComputeEnergyBayesianFractionOfMissingInformation( + int sampleCount, + double energyM2, + double squaredDifferenceSum) + { + if (sampleCount < 2 || !(energyM2 > 0d)) + return double.NaN; + + double meanSquaredDifference = squaredDifferenceSum / sampleCount; + double sampleVariance = energyM2 / (sampleCount - 1d); + return meanSquaredDifference / sampleVariance; + } + /// /// Accumulates sample statistics using Welford's online algorithm for computing /// the diagonal mass matrix during warmup. @@ -677,6 +895,7 @@ private TreeState BuildTree(Vector theta, Vector momentum, double epsilon, int d LogLikelihoodPrime = logLH, LeafCount = 1, Valid = !divergent, + Divergent = divergent, SumAlpha = alpha, NumAlpha = 1 }; @@ -713,6 +932,7 @@ private TreeState BuildTree(Vector theta, Vector momentum, double epsilon, int d tree.LogSumWeight = logSumWeightNew; tree.LeafCount += tree2.LeafCount; + tree.Divergent |= tree2.Divergent; tree.SumAlpha += tree2.SumAlpha; tree.NumAlpha += tree2.NumAlpha; @@ -745,6 +965,7 @@ private static TreeState InvalidTreeState(Vector theta, Vector momentum) LogLikelihoodPrime = double.NegativeInfinity, LeafCount = 1, Valid = false, + Divergent = true, SumAlpha = 0d, NumAlpha = 1 }; @@ -884,6 +1105,8 @@ private struct TreeState public int LeafCount; /// Whether the subtree is valid (no divergence, no U-turn). public bool Valid; + /// Whether the subtree contains a divergent or non-finite trajectory. + public bool Divergent; /// Sum of per-leaf Metropolis acceptance probabilities (for dual averaging). public double SumAlpha; /// Number of leaves contributing to SumAlpha. diff --git a/Numerics/Sampling/MCMC/Support/MCMCResults.cs b/Numerics/Sampling/MCMC/Support/MCMCResults.cs index dab56975..795bc255 100644 --- a/Numerics/Sampling/MCMC/Support/MCMCResults.cs +++ b/Numerics/Sampling/MCMC/Support/MCMCResults.cs @@ -41,6 +41,17 @@ public MCMCResults(MCMCSampler sampler, double alpha = 0.1) Output.AddRange(sampler.Output[i].ToList()); } AcceptanceRates = sampler.AcceptanceRates.ToArray(); + if (sampler is NUTS nuts) + { + NUTSDiagnosticSampleCounts = nuts.DiagnosticSampleCounts; + NUTSDivergenceCounts = nuts.DivergenceCounts; + NUTSMaxTreeDepthHitCounts = nuts.MaxTreeDepthHitCounts; + NUTSMeanTreeDepths = nuts.MeanTreeDepths; + NUTSMeanLeapfrogSteps = nuts.MeanLeapfrogSteps; + NUTSStepSizes = nuts.StepSizes; + NUTSEnergyBayesianFractionOfMissingInformation = + nuts.EnergyBayesianFractionOfMissingInformation; + } MeanLogLikelihood = sampler.MeanLogLikelihood.ToList(); MAP = sampler.MAP.Clone(); ProcessParameterResults(sampler, alpha); @@ -84,6 +95,48 @@ public MCMCResults(ParameterSet map, IList parameterSets, double a [JsonInclude] public double[] AcceptanceRates { get; private set; } = null!; + /// + /// Gets the number of post-warmup NUTS transitions contributing diagnostics per chain. + /// + [JsonInclude] + public int[]? NUTSDiagnosticSampleCounts { get; private set; } + + /// + /// Gets the number of divergent post-warmup NUTS transitions per chain. + /// + [JsonInclude] + public int[]? NUTSDivergenceCounts { get; private set; } + + /// + /// Gets the number of post-warmup NUTS transitions that exhausted maximum tree depth per chain. + /// + [JsonInclude] + public int[]? NUTSMaxTreeDepthHitCounts { get; private set; } + + /// + /// Gets the mean post-warmup NUTS tree depth per chain. + /// + [JsonInclude] + public double[]? NUTSMeanTreeDepths { get; private set; } + + /// + /// Gets the mean post-warmup NUTS leapfrog-step count per transition and chain. + /// + [JsonInclude] + public double[]? NUTSMeanLeapfrogSteps { get; private set; } + + /// + /// Gets the final adapted NUTS leapfrog step size per chain. + /// + [JsonInclude] + public double[]? NUTSStepSizes { get; private set; } + + /// + /// Gets the post-warmup NUTS energy Bayesian fraction of missing information per chain. + /// + [JsonInclude] + public double[]? NUTSEnergyBayesianFractionOfMissingInformation { get; private set; } + /// /// Parameter results using the output posterior parameter sets. /// @@ -153,7 +206,8 @@ private void ProcessParameterResults(double alpha = 0.1) /// /// Recompute parameter summary statistics at a new credible-interval level /// (alpha) without rerunning the chain. Preserves Rhat, ESS, autocorrelation, - /// MarkovChains, AcceptanceRates, MeanLogLikelihood, MAP, and Output. + /// MarkovChains, acceptance and sampler diagnostics, MeanLogLikelihood, MAP, + /// and Output. /// /// /// The new significance level (e.g., 0.05 for 95% credible intervals, diff --git a/Test_Numerics/Sampling/MCMC/Test_MCMCSamplerFindings.cs b/Test_Numerics/Sampling/MCMC/Test_MCMCSamplerFindings.cs new file mode 100644 index 00000000..570d6270 --- /dev/null +++ b/Test_Numerics/Sampling/MCMC/Test_MCMCSamplerFindings.cs @@ -0,0 +1,268 @@ +using System.Reflection; +using Numerics; +using Numerics.Data.Statistics; +using Numerics.Distributions; +using Numerics.Mathematics.LinearAlgebra; +using Numerics.Mathematics.Optimization; +using Numerics.Sampling.MCMC; + +namespace Sampling.MCMC +{ + /// + /// Characterizes the adaptive-random-walk and NUTS diagnostic findings used by + /// the RMC.BestFit verification program. + /// + /// + /// These tests intentionally pin the current public behavior before any sampler + /// correction. They use deterministic inline targets and do not require R or Python. + /// + [TestClass] + public class Test_MCMCSamplerFindings + { + /// + /// Confirms that rejected transitions before the warmup boundary advance the + /// running covariance sample count in the corrected ARWMH implementation. + /// + /// + /// Adaptive Metropolis covariance is defined from the realized chain history; + /// a rejected proposal therefore contributes the repeated retained state. + /// + [TestMethod] + public void ARWMH_RejectedWarmupTransitionsEnterCovariance() + { + InspectableARWMH sampler = CreateRejectingArwmh(warmupIterations: 50); + ParameterSet state = new ParameterSet(new[] { 0d, 0d }, 0d); + sampler.InitializeAt(state); + + for (int i = 0; i < 12; i++) + state = sampler.Iterate(state); + + Assert.AreEqual(12, sampler.SampleCount[0]); + Assert.AreEqual(0, sampler.AcceptCount[0]); + Assert.AreEqual( + 12, + GetArwmhCovarianceSampleCount(sampler), + "Every rejected warmup transition must contribute its repeated retained state."); + } + + /// + /// Confirms that ARWMH continues its original continual-adaptation schedule after + /// the configured warmup boundary. + /// + /// + /// Continued Adaptive Metropolis updating is consistent with the original + /// Haario-Saksman-Tamminen construction. This test separates that intended + /// behavior from the omitted repeated states before the boundary. + /// + [TestMethod] + public void ARWMH_RejectedPostWarmupTransitionsContinueEnteringCovariance() + { + InspectableARWMH sampler = CreateRejectingArwmh(warmupIterations: 5); + ParameterSet state = new ParameterSet(new[] { 0d, 0d }, 0d); + sampler.InitializeAt(state); + + for (int i = 0; i < 12; i++) + state = sampler.Iterate(state); + + Assert.AreEqual(12, sampler.SampleCount[0]); + Assert.AreEqual(0, sampler.AcceptCount[0]); + Assert.AreEqual( + 12, + GetArwmhCovarianceSampleCount(sampler), + "Continual adaptation must keep every realized transition before and after the boundary."); + } + + /// + /// Confirms that NUTS exposes post-warmup Hamiltonian acceptance and additive sampler diagnostics. + /// + [TestMethod] + public void NUTS_AcceptanceRatesAndDiagnosticsUsePostWarmupHamiltonianStatistics() + { + var priors = new List + { + new Uniform(-20d, 20d), + new Uniform(-20d, 20d) + }; + double LogTarget(double[] values) => -0.5d * (values[0] * values[0] + values[1] * values[1]); + Vector Gradient(IList values) => new Vector(new[] { -values[0], -values[1] }); + var sampler = new SeedableNUTS(priors, LogTarget, Gradient) + { + NumberOfChains = 2, + InitialIterations = 2, + WarmupIterations = 50, + Iterations = 100, + OutputLength = 100, + ThinningInterval = 1, + PRNGSeed = 8675309, + ParallelizeChains = false + }; + sampler.Seed(new ParameterSet(new[] { 0d, 0d }, 0d)); + + sampler.Sample(); + + for (int chainIndex = 0; chainIndex < sampler.NumberOfChains; chainIndex++) + { + Assert.AreEqual(sampler.SampleCount[chainIndex], sampler.AcceptCount[chainIndex]); + Assert.AreEqual( + sampler.SampleCount[chainIndex] - sampler.WarmupIterations, + sampler.DiagnosticSampleCounts[chainIndex]); + Assert.AreEqual( + sampler.HamiltonianAcceptanceRates[chainIndex], + sampler.AcceptanceRates[chainIndex], + 0d); + Assert.IsGreaterThan(0d, sampler.AcceptanceRates[chainIndex]); + Assert.IsLessThan(1d, sampler.AcceptanceRates[chainIndex]); + Assert.IsGreaterThanOrEqualTo(1d, sampler.MeanTreeDepths[chainIndex]); + Assert.IsLessThanOrEqualTo(4d, sampler.MeanTreeDepths[chainIndex]); + Assert.IsGreaterThanOrEqualTo(1d, sampler.MeanLeapfrogSteps[chainIndex]); + Assert.IsGreaterThan(0d, sampler.StepSizes[chainIndex]); + Assert.IsTrue(Tools.IsFinite(sampler.EnergyBayesianFractionOfMissingInformation[chainIndex])); + } + + var results = new MCMCResults(sampler); + CollectionAssert.AreEqual(sampler.AcceptanceRates, results.AcceptanceRates); + CollectionAssert.AreEqual(sampler.DiagnosticSampleCounts, results.NUTSDiagnosticSampleCounts); + CollectionAssert.AreEqual(sampler.DivergenceCounts, results.NUTSDivergenceCounts); + CollectionAssert.AreEqual(sampler.MaxTreeDepthHitCounts, results.NUTSMaxTreeDepthHitCounts); + CollectionAssert.AreEqual(sampler.MeanTreeDepths, results.NUTSMeanTreeDepths); + CollectionAssert.AreEqual(sampler.MeanLeapfrogSteps, results.NUTSMeanLeapfrogSteps); + CollectionAssert.AreEqual(sampler.StepSizes, results.NUTSStepSizes); + CollectionAssert.AreEqual( + sampler.EnergyBayesianFractionOfMissingInformation, + results.NUTSEnergyBayesianFractionOfMissingInformation); + + byte[] serialized = MCMCResults.ToByteArray(results); + MCMCResults restored = MCMCResults.FromByteArray(serialized); + Assert.IsNotNull(restored); + CollectionAssert.AreEqual(results.AcceptanceRates, restored.AcceptanceRates); + CollectionAssert.AreEqual(results.NUTSDiagnosticSampleCounts, restored.NUTSDiagnosticSampleCounts); + CollectionAssert.AreEqual(results.NUTSDivergenceCounts, restored.NUTSDivergenceCounts); + CollectionAssert.AreEqual(results.NUTSMaxTreeDepthHitCounts, restored.NUTSMaxTreeDepthHitCounts); + CollectionAssert.AreEqual(results.NUTSMeanTreeDepths, restored.NUTSMeanTreeDepths); + CollectionAssert.AreEqual(results.NUTSMeanLeapfrogSteps, restored.NUTSMeanLeapfrogSteps); + CollectionAssert.AreEqual(results.NUTSStepSizes, restored.NUTSStepSizes); + CollectionAssert.AreEqual( + results.NUTSEnergyBayesianFractionOfMissingInformation, + restored.NUTSEnergyBayesianFractionOfMissingInformation); + } + + /// + /// Confirms the streaming E-BFMI calculation matches Stan's + /// mean(diff(E)^2) / var(E) convention. + /// + [TestMethod] + public void NUTS_EnergyBayesianFractionOfMissingInformationMatchesStanFormula() + { + double actual = NUTS.ComputeEnergyBayesianFractionOfMissingInformation( + sampleCount: 4, + energyM2: 5d, + squaredDifferenceSum: 6d); + + Assert.AreEqual(0.9d, actual, 1e-15); + Assert.IsTrue(double.IsNaN( + NUTS.ComputeEnergyBayesianFractionOfMissingInformation(1, 0d, 0d))); + } + + /// + /// Creates a deterministic ARWMH sampler whose only finite-density state is the origin. + /// + /// Configured warmup boundary in raw transitions. + /// The configured inspectable sampler. + private static InspectableARWMH CreateRejectingArwmh(int warmupIterations) + { + var priors = new List + { + new Uniform(-10d, 10d), + new Uniform(-10d, 10d) + }; + double SpikeTarget(double[] values) => + values[0] == 0d && values[1] == 0d ? 0d : double.NegativeInfinity; + return new InspectableARWMH(priors, SpikeTarget) + { + NumberOfChains = 1, + WarmupIterations = warmupIterations, + ThinningInterval = 1, + PRNGSeed = 314159, + Beta = 1d + }; + } + + /// + /// Reads the current running-covariance observation count for the first ARWMH chain. + /// + /// Sampler to inspect. + /// The number of states pushed into the running covariance. + private static int GetArwmhCovarianceSampleCount(ARWMH sampler) + { + FieldInfo field = typeof(ARWMH).GetField("sigma", BindingFlags.Instance | BindingFlags.NonPublic); + Assert.IsNotNull(field, "The ARWMH running covariance field must be available for characterization."); + var covariance = field.GetValue(sampler) as RunningCovarianceMatrix[]; + Assert.IsNotNull(covariance); + return covariance[0].N; + } + + + /// + /// Exposes deterministic initialization and one ARWMH transition for focused tests. + /// + private sealed class InspectableARWMH : ARWMH + { + /// + /// Initializes a test sampler. + /// + /// Parameter prior bounds. + /// Log target. + public InspectableARWMH(List priors, LogLikelihood target) + : base(priors, target) + { + } + + /// + /// Initializes sampler-specific state at a deterministic chain state. + /// + /// Initial state. + internal void InitializeAt(ParameterSet state) + { + Reset(); + _chainStates = new[] { state.Clone() }; + InitializeCustomSettings(); + } + + /// + /// Executes one raw transition. + /// + /// Current state. + /// The retained state. + internal ParameterSet Iterate(ParameterSet state) => ChainIteration(0, state); + } + + /// + /// Allows a deterministic user-defined starting state for a focused NUTS run. + /// + private sealed class SeedableNUTS : NUTS + { + /// + /// Initializes a test sampler with an analytic gradient. + /// + /// Parameter prior bounds. + /// Log target. + /// Analytic log-target gradient. + public SeedableNUTS(List priors, LogLikelihood target, HMC.Gradient gradient) + : base(priors, target, maxTreeDepth: 4, gradientFunction: gradient) + { + } + + /// + /// Seeds every chain for user-defined initialization. + /// + /// Initial state. + internal void Seed(ParameterSet state) + { + Reset(); + for (int chainIndex = 0; chainIndex < NumberOfChains; chainIndex++) + MarkovChains[chainIndex].Add(state.Clone()); + Initialize = InitializationType.UserDefined; + } + } + } +} From cd9bf451d73e31ebec3aea2d9425259f5213a455 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 27 Jul 2026 07:23:22 -0600 Subject: [PATCH 021/222] Fix numerical validation and edge cases --- Numerics/Data/Statistics/Probability.cs | 93 ++- Numerics/Data/Statistics/Statistics.cs | 37 +- .../Multivariate/MultivariateNormal.cs | 4 +- .../Base/UnivariateDistributionFactory.cs | 98 +-- .../Univariate/CompetingRisks.cs | 226 +++-- .../Univariate/EmpiricalDistribution.cs | 219 +++-- .../Distributions/Univariate/KernelDensity.cs | 117 ++- .../Uncertainty Analysis/BootstrapAnalysis.cs | 787 ++++++++---------- Numerics/Functions/CompositeFunction.cs | 144 +++- Numerics/Functions/EnsembleFunction.cs | 109 ++- Numerics/Functions/SegmentedPowerFunction.cs | 100 ++- .../Integration/AdaptiveGuassKronrod.cs | 66 +- Numerics/Mathematics/Integration/Vegas.cs | 56 +- .../Special Functions/Factorial.cs | 36 +- Numerics/Sampling/MCMC/NUTS.cs | 29 +- .../Test_ProbabilityLazyExclusive.cs | 7 +- .../Test_ProbabilityPooledExclusive.cs | 25 +- .../Distributions/Test_ParameterValidity.cs | 11 +- .../Test_DistributionXElementRoundTrips.cs | 11 +- ...grades.cs => Test_EmpiricalConvolution.cs} | 18 +- .../Functions/Test_SegmentedPowerFunction.cs | 18 +- .../Test_AdaptiveGaussKronrodRecorder.cs | 28 +- .../Test_VegasTailFocusJacobian.cs | 10 +- ...ings.cs => Test_MCMCSamplerDiagnostics.cs} | 11 +- Test_Numerics/Test_CorrectnessRepairs.cs | 481 +++++++++++ docs/functions/index.md | 11 +- 26 files changed, 1763 insertions(+), 989 deletions(-) rename Test_Numerics/Distributions/Univariate/{Test_ConvolveUpgrades.cs => Test_EmpiricalConvolution.cs} (87%) rename Test_Numerics/Sampling/MCMC/{Test_MCMCSamplerFindings.cs => Test_MCMCSamplerDiagnostics.cs} (96%) create mode 100644 Test_Numerics/Test_CorrectnessRepairs.cs diff --git a/Numerics/Data/Statistics/Probability.cs b/Numerics/Data/Statistics/Probability.cs index 8c5e0d84..5597c0f2 100644 --- a/Numerics/Data/Statistics/Probability.cs +++ b/Numerics/Data/Statistics/Probability.cs @@ -1068,34 +1068,26 @@ public static void IndependentExclusive(IList probabilities, int[] binom } /// - /// The pooled-output form of - /// : - /// the caller supplies (and reuses) the output lists across calls, and existing - /// indicator row arrays of matching length are refilled in place — so a hot loop that - /// calls this per evaluation performs no per-call output allocations after the first. - /// The return value surfaces the inclusion-exclusion truncation that was previously - /// silent. + /// Computes independent exclusive probabilities into caller-owned output collections. + /// Existing indicator arrays with the required length are refilled and reused. /// - /// An array of probabilities for each event. Each element represents the probability of an individual event occurring. - /// An array of binomial combinations that define the number of events to consider for each calculation. - /// The caller-owned output list of exclusive event probabilities; cleared and refilled. - /// The caller-owned output list of event indicator rows; existing rows of matching length are refilled in place, and the list is trimmed to the produced count. - /// A 2D array of indicators, where each row represents a combination of events, and 0 means the event did not occur, 1 means the event did occur. - /// The absolute tolerance for evaluation convergence of the inclusion-exclusion algorithm. Default = 1E-4. - /// The relative tolerance for evaluation convergence of the inclusion-exclusion algorithm. Default = 1E-4. + /// The probability of each event. + /// The number of combinations for each subset size. + /// The event-indicator rows in subset-size order. + /// The output probabilities; cleared and refilled. + /// The output indicator rows; matching arrays are reused. + /// The non-negative absolute convergence tolerance. + /// The non-negative relative convergence tolerance. /// - /// True when the inclusion-exclusion expansion converged early and the deepest - /// combinations were TRUNCATED — the outputs then end with one closing pseudo-row - /// carrying half the remaining inclusion-exclusion gap; false when every combination - /// was enumerated. + /// when convergence stops the inclusion-exclusion expansion + /// before every combination is enumerated; otherwise, . /// - /// Thrown when either output list is null. - /// Thrown if the probabilities array is null, empty, or if the lengths of the probabilities and indicators arrays do not match. + /// Thrown when an output collection or metadata array is null. + /// Thrown when the probability or indicator metadata is structurally inconsistent. + /// Thrown when a probability or tolerance is outside its valid range. /// - /// This method uses the inclusion-exclusion principle to compute the exclusive - /// probability of each event combination, exactly as the allocating overload does (which - /// now delegates here); only the output-buffer ownership and the truncation visibility - /// differ. + /// After the output lists reach their required capacity, repeated calls with the same + /// dimensions reuse the indicator arrays and do not allocate output rows. /// public static bool IndependentExclusive(IList probabilities, int[] binomialCombinations, int[,] indicators, List eventProbabilities, List eventIndicators, double absoluteTolerance = 1E-4, double relativeTolerance = 1E-4) { @@ -1108,9 +1100,10 @@ public static bool IndependentExclusive(IList probabilities, int[] binom throw new ArgumentException("The probabilities array and the indicator array must have the same length.", nameof(probabilities)); if (eventProbabilities == null) throw new ArgumentNullException(nameof(eventProbabilities)); if (eventIndicators == null) throw new ArgumentNullException(nameof(eventIndicators)); + ValidatePooledExclusiveMetadata(probabilities, binomialCombinations, indicators, absoluteTolerance, relativeTolerance); int n = probabilities.Count; - int used = 0; // Output rows placed this call + int used = 0; // Number of output rows used by this call. eventProbabilities.Clear(); // Copies an indicator row into the pooled output slot, reusing an existing row @@ -1167,7 +1160,7 @@ void TrimToUsed() PlaceRow(indicators.GetLength(0) - 1); // Add last indicator row eventProbabilities.Add(0.5 * diff); // Add the average of the difference to the event probabilities TrimToUsed(); - return true; // Exit early when convergence is reached — the deepest combinations are truncated + return true; // Report that convergence ended the expansion early. } // Flip the sign for the next inclusion-exclusion term @@ -1202,6 +1195,35 @@ void TrimToUsed() return false; } + /// + /// Validates the structural metadata used by the pooled exclusive-probability overload. + /// + private static void ValidatePooledExclusiveMetadata(IList probabilities, int[] binomialCombinations, + int[,] indicators, double absoluteTolerance, double relativeTolerance) + { + if (binomialCombinations == null) + throw new ArgumentNullException(nameof(binomialCombinations)); + if (binomialCombinations.Length != probabilities.Count) + throw new ArgumentException("The binomial metadata must contain one count for each subset size.", nameof(binomialCombinations)); + if (!Tools.IsFinite(absoluteTolerance) || absoluteTolerance < 0d) + throw new ArgumentOutOfRangeException(nameof(absoluteTolerance), "The absolute tolerance must be finite and non-negative."); + if (!Tools.IsFinite(relativeTolerance) || relativeTolerance < 0d) + throw new ArgumentOutOfRangeException(nameof(relativeTolerance), "The relative tolerance must be finite and non-negative."); + + long rowCount = 0; + for (int i = 0; i < probabilities.Count; i++) + { + if (!Tools.IsFinite(probabilities[i]) || probabilities[i] < 0d || probabilities[i] > 1d) + throw new ArgumentOutOfRangeException(nameof(probabilities), "Probabilities must be finite and between zero and one."); + + int expected = checked((int)Factorial.BinomialCoefficient(probabilities.Count, i + 1)); + if (binomialCombinations[i] != expected) + throw new ArgumentException("The binomial metadata does not match the probability count.", nameof(binomialCombinations)); + rowCount += expected; + } + if (rowCount != indicators.GetLength(0)) + throw new ArgumentException("The indicator row count does not match the binomial metadata.", nameof(indicators)); + } /// /// The outcome of a lazily enumerated exclusive-probability expansion. /// @@ -1249,6 +1271,11 @@ public static ExclusiveEnumerationStatus IndependentExclusiveLazy(IList throw new ArgumentException("The probabilities array must have a length greater than 0.", nameof(probabilities)); if (eventProbabilities == null) throw new ArgumentNullException(nameof(eventProbabilities)); if (eventIndicators == null) throw new ArgumentNullException(nameof(eventIndicators)); + for (int i = 0; i < probabilities.Count; i++) + { + if (!Tools.IsFinite(probabilities[i]) || probabilities[i] < 0d || probabilities[i] > 1d) + throw new ArgumentOutOfRangeException(nameof(probabilities), "Probabilities must be finite and between zero and one."); + } int n = probabilities.Count; int used = 0; @@ -1287,13 +1314,15 @@ ExclusiveEnumerationStatus Close(double mass, ExclusiveEnumerationStatus status) return status; } + double noEventMass = 1d; + for (int i = 0; i < n; i++) noEventMass *= 1d - probabilities[i]; + double totalOutputMass = includeNoEventRow ? 1d : 1d - noEventMass; double emittedMass = 0d; if (includeNoEventRow) { - var noEvent = Row(); - double none = IndependentExclusive(probabilities, noEvent); - eventProbabilities.Add(none); - emittedMass += none; + Row(); + eventProbabilities.Add(noEventMass); + emittedMass += noEventMass; } double union = 0; @@ -1328,7 +1357,7 @@ ExclusiveEnumerationStatus Close(double mass, ExclusiveEnumerationStatus status) { if (maxEmittedCombinations > 0 && emitted >= maxEmittedCombinations) { - return Close(Math.Max(0d, 1d - emittedMass), ExclusiveEnumerationStatus.Capped); + return Close(Math.Max(0d, totalOutputMass - emittedMass), ExclusiveEnumerationStatus.Capped); } var row = Row(); @@ -1341,7 +1370,7 @@ ExclusiveEnumerationStatus Close(double mass, ExclusiveEnumerationStatus status) union += s * (k == 1 ? probabilities[combination[0]] : IndependentJointProbability(probabilities, row)); } - while (Factorial.NextCombination(combination, n)); + while (Factorial.NextCombinationUnchecked(combination, n)); } TrimToUsed(); diff --git a/Numerics/Data/Statistics/Statistics.cs b/Numerics/Data/Statistics/Statistics.cs index 8d4cb1b6..9f27321e 100644 --- a/Numerics/Data/Statistics/Statistics.cs +++ b/Numerics/Data/Statistics/Statistics.cs @@ -312,33 +312,31 @@ public static double Skewness(IList data) } /// - /// Computes the standard error of the statistic, using the jackknife method. + /// Computes the jackknife standard error of a statistic. /// - /// Sample of data, no sorting is assumed. - /// The statistic for estimating standard error. - /// - /// Chunked so the sum merges in a fixed order, independent of the thread count. Each chunk - /// refills one leave-one-out buffer rather than copying the sample per point. - /// + /// Sample data. + /// The statistic evaluated on isolated sample arrays. + /// The jackknife standard error. public static double JackKnifeStandardError(IList data, Func, double> statistic) { if (data == null) throw new ArgumentNullException(nameof(data)); + if (statistic == null) throw new ArgumentNullException(nameof(statistic)); if (data.Count == 0) return double.NaN; int N = data.Count; if (N == 1) return 0d; - double theta = statistic(data); + double theta = statistic(data.ToArray()); int chunks = Math.Min(JackKnifeChunks, N); var chunkSums = new double[chunks]; Parallel.For(0, chunks, c => { - var jackSample = new double[N - 1]; double sum = 0d; int start = (int)((long)c * N / chunks); int end = (int)((long)(c + 1) * N / chunks); for (int i = start; i < end; i++) { + var jackSample = new double[N - 1]; for (int k = 0; k < i; k++) jackSample[k] = data[k]; for (int k = i + 1; k < N; k++) jackSample[k - 1] = data[k]; sum += Tools.Sqr(statistic(jackSample) - theta); @@ -346,11 +344,10 @@ public static double JackKnifeStandardError(IList data, Func /// The number of accumulation chunks used by the jackknife reductions — fixed, so the /// floating-point association order does not vary with the machine or the thread count. @@ -358,28 +355,27 @@ public static double JackKnifeStandardError(IList data, Func - /// Returns a jackknifed sample. + /// Evaluates a statistic for every leave-one-out sample. /// - /// Sample of data, no sorting is assumed. - /// The statistic for estimating a sample. + /// Sample data. + /// The statistic evaluated on isolated leave-one-out arrays. + /// The statistic values, or for an empty input sample. public static double[]? JackKnifeSample(IList data, Func, double> statistic) { if (data == null) throw new ArgumentNullException(nameof(data)); + if (statistic == null) throw new ArgumentNullException(nameof(statistic)); if (data.Count == 0) return null; int N = data.Count; var thetaJack = new double[N]; - if (N == 1) return thetaJack; - - // Perform Jackknife, reusing one leave-one-out buffer per chunk. int chunks = Math.Min(JackKnifeChunks, N); Parallel.For(0, chunks, c => { - var jackSample = new double[N - 1]; int start = (int)((long)c * N / chunks); int end = (int)((long)(c + 1) * N / chunks); for (int i = start; i < end; i++) { + var jackSample = new double[N - 1]; for (int k = 0; k < i; k++) jackSample[k] = data[k]; for (int k = i + 1; k < N; k++) jackSample[k - 1] = data[k]; thetaJack[i] = statistic(jackSample); @@ -387,7 +383,6 @@ public static double JackKnifeStandardError(IList data, Func /// Estimates the kurtosis from the unsorted data array. /// Returns NaN if data is empty or any entry is NaN. diff --git a/Numerics/Distributions/Multivariate/MultivariateNormal.cs b/Numerics/Distributions/Multivariate/MultivariateNormal.cs index cf5b65fa..d59a4c20 100644 --- a/Numerics/Distributions/Multivariate/MultivariateNormal.cs +++ b/Numerics/Distributions/Multivariate/MultivariateNormal.cs @@ -81,7 +81,7 @@ public MultivariateNormal(double[] mean, double[,] covariance) private bool _covSRTed = false; /// - /// The default seed. Fixed, never clock-derived — see . + /// The constant default seed used for reproducible evaluations. /// public const int DefaultMVNUNISeed = 12345; @@ -98,7 +98,7 @@ public MultivariateNormal(double[] mean, double[,] covariance) public Random MVNUNI { get { return _MVNUNI; } - set { _MVNUNI = value; } + set { _MVNUNI = value ?? throw new ArgumentNullException(nameof(MVNUNI)); } } /// diff --git a/Numerics/Distributions/Univariate/Base/UnivariateDistributionFactory.cs b/Numerics/Distributions/Univariate/Base/UnivariateDistributionFactory.cs index 938ecc67..142b13c9 100644 --- a/Numerics/Distributions/Univariate/Base/UnivariateDistributionFactory.cs +++ b/Numerics/Distributions/Univariate/Base/UnivariateDistributionFactory.cs @@ -157,68 +157,58 @@ public static bool TryCreateDistribution(UnivariateDistributionType distribution } /// - /// Create a distribution from XElement. + /// Creates a distribution from its serialized representation. /// - /// The XElement to deserialize into a univariate distribution. - /// - /// A univariate distribution. - /// - /// - /// The serialized distribution type requires a user-provided implementation. - /// - /// - /// The serialized distribution type is not a defined value. - /// + /// The element to deserialize. + /// A validated univariate distribution. + /// Thrown when is null. + /// Thrown when the type or parameter data is missing or malformed. + /// Thrown when the serialized type requires a user-provided implementation. public static UnivariateDistributionBase CreateDistribution(XElement xElement) { - UnivariateDistributionType type = UnivariateDistributionType.Deterministic; - var typeAttr = xElement.Attribute(nameof(UnivariateDistributionBase.Type)); - if (typeAttr != null) - { - Enum.TryParse(typeAttr.Value, out type); + if (xElement == null) throw new ArgumentNullException(nameof(xElement)); - if (type == UnivariateDistributionType.Mixture) - { - return Mixture.FromXElement(xElement)!; - } - else if (type == UnivariateDistributionType.CompetingRisks) - { - return CompetingRisks.FromXElement(xElement)!; - } - else if (type == UnivariateDistributionType.PertPercentile) - { - return PertPercentile.FromXElement(xElement)!; - } - else if (type == UnivariateDistributionType.PertPercentileZ) - { - return PertPercentileZ.FromXElement(xElement)!; - } - else if (type == UnivariateDistributionType.Empirical) - { - return EmpiricalDistribution.FromXElement(xElement); - } - else if (type == UnivariateDistributionType.KernelDensity) - { - return KernelDensity.FromXElement(xElement); - } - } + var typeAttribute = xElement.Attribute(nameof(UnivariateDistributionBase.Type)); + if (typeAttribute == null + || !Enum.TryParse(typeAttribute.Value, out UnivariateDistributionType type) + || !Enum.IsDefined(typeof(UnivariateDistributionType), type)) + throw new ArgumentException("The serialized distribution type is missing or invalid.", nameof(xElement)); - var dist = CreateDistribution(type); - var names = dist.GetParameterPropertyNames; - var parms = dist.GetParameters; - var vals = new double[dist.NumberOfParameters]; - for (int i = 0; i < dist.NumberOfParameters; i++) + if (type == UnivariateDistributionType.Mixture) + return Mixture.FromXElement(xElement) + ?? throw new ArgumentException("The serialized mixture is invalid.", nameof(xElement)); + if (type == UnivariateDistributionType.CompetingRisks) + return CompetingRisks.FromXElement(xElement) + ?? throw new ArgumentException("The serialized competing-risks distribution is invalid.", nameof(xElement)); + if (type == UnivariateDistributionType.PertPercentile) + return PertPercentile.FromXElement(xElement) + ?? throw new ArgumentException("The serialized percentile PERT distribution is invalid.", nameof(xElement)); + if (type == UnivariateDistributionType.PertPercentileZ) + return PertPercentileZ.FromXElement(xElement) + ?? throw new ArgumentException("The serialized transformed percentile PERT distribution is invalid.", nameof(xElement)); + if (type == UnivariateDistributionType.Empirical) + return EmpiricalDistribution.FromXElement(xElement); + if (type == UnivariateDistributionType.KernelDensity) + return KernelDensity.FromXElement(xElement); + + var distribution = CreateDistribution(type); + var names = distribution.GetParameterPropertyNames; + var values = new double[distribution.NumberOfParameters]; + for (int i = 0; i < values.Length; i++) { - var paramAttr = xElement.Attribute(names[i]); - if (paramAttr != null) - { - double.TryParse(paramAttr.Value, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out vals[i]); - } + var parameterAttribute = xElement.Attribute(names[i]); + if (parameterAttribute == null + || !double.TryParse(parameterAttribute.Value, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out values[i]) + || !Tools.IsFinite(values[i])) + throw new ArgumentException("The serialized distribution parameter '" + names[i] + "' is missing or invalid.", nameof(xElement)); } - dist.SetParameters(vals); - return dist; - } + distribution.ValidateParameters(values, true); + distribution.SetParameters(values); + if (!distribution.ParametersValid) + throw new ArgumentException("The serialized parameters do not define a valid distribution.", nameof(xElement)); + return distribution; + } } } \ No newline at end of file diff --git a/Numerics/Distributions/Univariate/CompetingRisks.cs b/Numerics/Distributions/Univariate/CompetingRisks.cs index 5d563185..c76de5c4 100644 --- a/Numerics/Distributions/Univariate/CompetingRisks.cs +++ b/Numerics/Distributions/Univariate/CompetingRisks.cs @@ -58,6 +58,7 @@ public CompetingRisks(IUnivariateDistribution[] distributions) private bool _mvnCreated = false; private Probability.DependencyType _dependency = Probability.DependencyType.Independent; private MultivariateNormal _mvn = null!; + private int _prngSeed = MultivariateNormal.DefaultMVNUNISeed; // Soft finite floor used in tail arithmetic before returning the final log-density. private const double _logZero = -745.0; @@ -78,7 +79,17 @@ public CompetingRisks(IUnivariateDistribution[] distributions) /// is fixed. Applied when the multivariate normal is built — set it before the first /// dependent evaluation. /// - public int PRNGSeed { get; set; } = MultivariateNormal.DefaultMVNUNISeed; + public int PRNGSeed + { + get { return _prngSeed; } + set + { + if (_prngSeed == value) return; + _prngSeed = value; + _mvnCreated = false; + _empiricalCDFCreated = false; + } + } /// /// Determines the interpolation transform for the X-values. @@ -1244,7 +1255,8 @@ public override UnivariateDistributionBase Clone() MinimumOfRandomVariables = MinimumOfRandomVariables, Dependency = Dependency, XTransform = XTransform, - ProbabilityTransform = ProbabilityTransform + ProbabilityTransform = ProbabilityTransform, + PRNGSeed = PRNGSeed }; if (CorrelationMatrix != null) cr.CorrelationMatrix = (double[,])CorrelationMatrix.Clone(); @@ -1261,6 +1273,7 @@ public override XElement ToXElement() result.SetAttributeValue(nameof(ProbabilityTransform), ProbabilityTransform.ToString()); result.SetAttributeValue(nameof(MinimumOfRandomVariables), MinimumOfRandomVariables.ToString()); result.SetAttributeValue(nameof(Dependency), Dependency.ToString()); + result.SetAttributeValue(nameof(PRNGSeed), PRNGSeed.ToString(CultureInfo.InvariantCulture)); result.SetAttributeValue(nameof(Distributions), String.Join("|", Distributions.Select(x => x.Type))); // Parameters var parms = GetParameters; @@ -1304,112 +1317,151 @@ public override XElement ToXElement() } /// - /// Create a competing risks distribution from XElement. + /// Creates a competing-risks distribution from its serialized representation. /// - /// The XElement to deserialize. - /// A new competing risks distribution. + /// The element to deserialize. + /// A validated competing-risks distribution, or when the element identifies another distribution type. + /// Thrown when is null. + /// Thrown when serialized configuration, parameters, or correlation data is malformed. public static CompetingRisks? FromXElement(XElement xElement) { - UnivariateDistributionType type = UnivariateDistributionType.Deterministic; - var typeAttr = xElement.Attribute(nameof(UnivariateDistributionBase.Type)); - if (typeAttr != null) + if (xElement == null) throw new ArgumentNullException(nameof(xElement)); + + var typeAttribute = xElement.Attribute(nameof(UnivariateDistributionBase.Type)); + if (typeAttribute == null + || !Enum.TryParse(typeAttribute.Value, out UnivariateDistributionType type) + || !Enum.IsDefined(typeof(UnivariateDistributionType), type)) + throw new ArgumentException("The serialized distribution type is missing or invalid.", nameof(xElement)); + if (type != UnivariateDistributionType.CompetingRisks) return null; + + var distributionsAttribute = xElement.Attribute(nameof(Distributions)); + if (distributionsAttribute == null || string.IsNullOrWhiteSpace(distributionsAttribute.Value)) + throw new ArgumentException("The serialized competing-risks distribution has no component distributions.", nameof(xElement)); + + string[] typeTokens = distributionsAttribute.Value.Split('|'); + var distributions = new UnivariateDistributionBase[typeTokens.Length]; + for (int i = 0; i < typeTokens.Length; i++) { - Enum.TryParse(typeAttr.Value, out type); + if (!Enum.TryParse(typeTokens[i], out UnivariateDistributionType componentType) + || !Enum.IsDefined(typeof(UnivariateDistributionType), componentType)) + throw new ArgumentException("The serialized competing-risks distribution contains an invalid component type.", nameof(xElement)); + distributions[i] = UnivariateDistributionFactory.CreateDistribution(componentType); + } + var competingRisks = new CompetingRisks(distributions); + + var xTransformAttribute = xElement.Attribute(nameof(XTransform)); + if (xTransformAttribute != null) + { + if (!Enum.TryParse(xTransformAttribute.Value, out Transform xTransform) + || !Enum.IsDefined(typeof(Transform), xTransform)) + throw new ArgumentException("The serialized X transform is invalid.", nameof(xElement)); + competingRisks.XTransform = xTransform; } - if (type == UnivariateDistributionType.CompetingRisks) + + var probabilityTransformAttribute = xElement.Attribute(nameof(ProbabilityTransform)); + if (probabilityTransformAttribute != null) { - var distributions = new List(); - var distsAttr = xElement.Attribute(nameof(Distributions)); - if (distsAttr != null) - { - var types = distsAttr.Value.Split('|'); - for (int i = 0; i < types.Length; i++) - { - Enum.TryParse(types[i], out UnivariateDistributionType distType); - distributions.Add(UnivariateDistributionFactory.CreateDistribution(distType)); - } - } - var competingRisks = new CompetingRisks(distributions.ToArray()); + if (!Enum.TryParse(probabilityTransformAttribute.Value, out Transform probabilityTransform) + || !Enum.IsDefined(typeof(Transform), probabilityTransform)) + throw new ArgumentException("The serialized probability transform is invalid.", nameof(xElement)); + competingRisks.ProbabilityTransform = probabilityTransform; + } - var xTransformAttr = xElement.Attribute(nameof(XTransform)); - if (xTransformAttr != null) - { - Enum.TryParse(xTransformAttr.Value, out Transform xTransform); - competingRisks.XTransform = xTransform; - } - var probTransformAttr = xElement.Attribute(nameof(ProbabilityTransform)); - if (probTransformAttr != null) - { - Enum.TryParse(probTransformAttr.Value, out Transform probabilityTransform); - competingRisks.ProbabilityTransform = probabilityTransform; - } - var minOfRVAttr = xElement.Attribute(nameof(MinimumOfRandomVariables)); - if (minOfRVAttr != null) - { - bool.TryParse(minOfRVAttr.Value, out bool minOfValues); - competingRisks.MinimumOfRandomVariables = minOfValues; - } - var depAttr = xElement.Attribute(nameof(Dependency)); - if (depAttr != null) - { - Enum.TryParse(depAttr.Value, out Probability.DependencyType dependency); - competingRisks.Dependency = dependency; - } + var minimumAttribute = xElement.Attribute(nameof(MinimumOfRandomVariables)); + if (minimumAttribute != null) + { + if (!bool.TryParse(minimumAttribute.Value, out bool minimumOfRandomVariables)) + throw new ArgumentException("The serialized minimum-selection flag is invalid.", nameof(xElement)); + competingRisks.MinimumOfRandomVariables = minimumOfRandomVariables; + } - // Parameters - var paramsAttr = xElement.Attribute("Parameters"); - if (paramsAttr != null) + var dependencyAttribute = xElement.Attribute(nameof(Dependency)); + if (dependencyAttribute != null) + { + if (!Enum.TryParse(dependencyAttribute.Value, out Probability.DependencyType dependency) + || !Enum.IsDefined(typeof(Probability.DependencyType), dependency)) + throw new ArgumentException("The serialized dependency type is invalid.", nameof(xElement)); + competingRisks.Dependency = dependency; + } + + var seedAttribute = xElement.Attribute(nameof(PRNGSeed)); + if (seedAttribute != null) + { + if (!int.TryParse(seedAttribute.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out int seed)) + throw new ArgumentException("The serialized competing-risks seed is invalid.", nameof(xElement)); + competingRisks.PRNGSeed = seed; + } + + var parametersAttribute = xElement.Attribute("Parameters"); + if (parametersAttribute == null) + throw new ArgumentException("The serialized competing-risks parameters are missing.", nameof(xElement)); + string[] parameterTokens = parametersAttribute.Value.Split('|'); + if (parameterTokens.Length != competingRisks.NumberOfParameters) + throw new ArgumentException("The serialized competing-risks parameter count is invalid.", nameof(xElement)); + var parameters = new double[parameterTokens.Length]; + for (int i = 0; i < parameters.Length; i++) + { + if (!double.TryParse(parameterTokens[i], NumberStyles.Any, CultureInfo.InvariantCulture, out parameters[i]) + || !Tools.IsFinite(parameters[i])) + throw new ArgumentException("The serialized competing-risks parameters contain an invalid value.", nameof(xElement)); + } + + int offset = 0; + for (int i = 0; i < distributions.Length; i++) + { + int count = distributions[i].NumberOfParameters; + var componentParameters = new double[count]; + Array.Copy(parameters, offset, componentParameters, 0, count); + distributions[i].ValidateParameters(componentParameters, true); + offset += count; + } + competingRisks.SetParameters(parameters); + if (!competingRisks.ParametersValid) + throw new ArgumentException("The serialized competing-risks parameters are invalid.", nameof(xElement)); + + var correlationElement = xElement.Element(nameof(CorrelationMatrix)); + var correlationRows = correlationElement?.Elements("Correlation_Row").ToArray() ?? Array.Empty(); + if (correlationRows.Length > 0) + { + int dimension = distributions.Length; + if (correlationRows.Length != dimension) + throw new ArgumentException("The serialized correlation matrix has an invalid row count.", nameof(xElement)); + + var correlation = new double[dimension, dimension]; + for (int i = 0; i < dimension; i++) { - var vals = paramsAttr.Value.Split('|'); - var parameters = new List(); - for (int i = 0; i < vals.Length; i++) + string[] entries = correlationRows[i].Value.Split('|'); + if (entries.Length != dimension) + throw new ArgumentException("The serialized correlation matrix has an invalid column count.", nameof(xElement)); + for (int j = 0; j < dimension; j++) { - double.TryParse(vals[i], NumberStyles.Any, CultureInfo.InvariantCulture, out var parm); - parameters.Add(parm); + if (!double.TryParse(entries[j], NumberStyles.Any, CultureInfo.InvariantCulture, out correlation[i, j]) + || !Tools.IsFinite(correlation[i, j]) + || correlation[i, j] < -1d + || correlation[i, j] > 1d) + throw new ArgumentException("The serialized correlation matrix contains an invalid value.", nameof(xElement)); } - competingRisks.SetParameters(parameters); } - // Correlation matrix - var corrMatrixElement = xElement.Element(nameof(CorrelationMatrix)); - if (corrMatrixElement != null) + for (int i = 0; i < dimension; i++) { - var _corrMatrix = new double[competingRisks.Distributions.Count, competingRisks.Distributions.Count]; - int counter = 0; - foreach (var rowEl in corrMatrixElement.Elements("Correlation_Row")) + if (Math.Abs(correlation[i, i] - 1d) > 1E-12) + throw new ArgumentException("The serialized correlation matrix must have unit diagonal entries.", nameof(xElement)); + for (int j = i + 1; j < dimension; j++) { - if (counter >= competingRisks.Distributions.Count) - break; - - // Split on '|' to get each stringified value - var parts = rowEl.Value.Split('|'); - int maxCols = Math.Min(parts.Length, competingRisks.Distributions.Count); - - for (int j = 0; j < maxCols; j++) - { - // Try to parse each part; if it fails, leave as 0 or assign NaN if you prefer - if (double.TryParse(parts[j],NumberStyles.Any,CultureInfo.InvariantCulture, out var p)) - { - _corrMatrix[counter, j] = p; - } - else - { - _corrMatrix[counter, j] = double.NaN; - } - } - - counter++; + if (Math.Abs(correlation[i, j] - correlation[j, i]) > 1E-12) + throw new ArgumentException("The serialized correlation matrix must be symmetric.", nameof(xElement)); } - competingRisks.CorrelationMatrix = _corrMatrix; } - - return competingRisks; + competingRisks.CorrelationMatrix = correlation; } - else + else if (competingRisks.Dependency == Probability.DependencyType.CorrelationMatrix) { - return null; + throw new ArgumentException("A correlation-matrix dependency requires serialized correlation data.", nameof(xElement)); } + + return competingRisks; } } diff --git a/Numerics/Distributions/Univariate/EmpiricalDistribution.cs b/Numerics/Distributions/Univariate/EmpiricalDistribution.cs index 2ea95fa8..830c8c9e 100644 --- a/Numerics/Distributions/Univariate/EmpiricalDistribution.cs +++ b/Numerics/Distributions/Univariate/EmpiricalDistribution.cs @@ -541,93 +541,112 @@ public override UnivariateDistributionBase Clone() } /// - /// Convolves two empirical distributions with a log-spaced output grid — the opt-in - /// alternative for order-of-magnitude (heavy-tail) supports that the linear output grid - /// under-resolves. The FFT pipeline is exactly the linear-grid - /// (unchanged, - /// byte-identical when this overload is not used); only the OUTPUT resampling differs: - /// the returned distribution's CDF is re-read on a log-spaced ladder between the summed - /// supports, keeping tail resolution across decades. + /// Convolves two empirical distributions and optionally resamples the result on a + /// logarithmically spaced output grid. /// /// The first empirical distribution. /// The second empirical distribution. - /// The number of output points. Default = 1024. - /// True for the log-spaced output ladder; false delegates to the linear-grid method unchanged. + /// The requested output point count. + /// Whether to use logarithmic output spacing. /// The convolved empirical distribution. - /// Thrown when a log-spaced output is requested over a non-positive summed support. + /// Thrown when either distribution is null. + /// Thrown when logarithmic output requires a non-positive or degenerate support, or produces fewer than two distinct cumulative probabilities. public static EmpiricalDistribution Convolve(EmpiricalDistribution dist1, EmpiricalDistribution dist2, int numberOfPoints, bool logSpacedOutput) { + if (dist1 is null) throw new ArgumentNullException(nameof(dist1)); + if (dist2 is null) throw new ArgumentNullException(nameof(dist2)); + + if (logSpacedOutput) + { + double supportMinimum = dist1.Minimum + dist2.Minimum; + double supportMaximum = dist1.Maximum + dist2.Maximum; + if (!Tools.IsFinite(supportMinimum) || !Tools.IsFinite(supportMaximum) + || supportMinimum <= 0d || supportMaximum <= supportMinimum) + throw new ArgumentException("A logarithmic output grid requires a finite, strictly positive, non-degenerate support.", nameof(logSpacedOutput)); + } + var linear = Convolve(dist1, dist2, numberOfPoints); if (!logSpacedOutput) return linear; double minimum = linear.Minimum; double maximum = linear.Maximum; - if (minimum <= 0d || maximum <= 0d) - throw new ArgumentException("A log-spaced output grid requires a strictly positive summed support.", nameof(logSpacedOutput)); + if (!Tools.IsFinite(minimum) || !Tools.IsFinite(maximum) || minimum <= 0d || maximum <= minimum) + throw new ArgumentException("A logarithmic output grid requires a finite, strictly positive, non-degenerate support.", nameof(logSpacedOutput)); - double logMin = Math.Log10(minimum); - double logMax = Math.Log10(maximum); + double logMinimum = Math.Log10(minimum); + double logMaximum = Math.Log10(maximum); var xValues = new double[numberOfPoints]; var pValues = new double[numberOfPoints]; double previous = double.NegativeInfinity; int count = 0; for (int i = 0; i < numberOfPoints; i++) { - double x = Math.Pow(10d, logMin + (logMax - logMin) * i / (numberOfPoints - 1d)); - double p = linear.CDF(x); - // The CDF ladder must stay strictly increasing for the empirical constructor. - if (p > previous) + double x = Math.Pow(10d, logMinimum + (logMaximum - logMinimum) * i / (numberOfPoints - 1d)); + double probability = linear.CDF(x); + if (!Tools.IsFinite(x) || !Tools.IsFinite(probability)) + throw new InvalidOperationException("Convolution produced a non-finite logarithmic output value."); + + if (probability > previous) { xValues[count] = x; - pValues[count] = p; - previous = p; + pValues[count] = probability; + previous = probability; count++; } } + + if (count < 2) + throw new ArgumentException("The logarithmic output grid produced fewer than two distinct cumulative probabilities.", nameof(logSpacedOutput)); + var trimmedX = new double[count]; var trimmedP = new double[count]; Array.Copy(xValues, trimmedX, count); Array.Copy(pValues, trimmedP, count); - return new EmpiricalDistribution(trimmedX, trimmedP) { XTransform = dist1.XTransform, ProbabilityTransform = dist1.ProbabilityTransform }; + return new EmpiricalDistribution(trimmedX, trimmedP) + { + XTransform = linear.XTransform, + ProbabilityTransform = linear.ProbabilityTransform + }; } - /// - /// Convolves two discrete (atom-bearing) distributions EXACTLY on a shared uniform - /// lattice — the atom-aware form the continuous Convolve cannot represent: it - /// samples continuous PDFs, so a point mass (a CDF jump, e.g. the zero-inflation atom of - /// a defective risk curve) has no representation there. Each input's atoms deposit onto - /// the lattice with a moment-preserving two-node split (input means are preserved - /// exactly), the mass vectors convolve by FFT, and the result is the discrete mass - /// ladder on the summed lattice. + /// Approximates the convolution of two discrete distributions on a shared uniform lattice. + /// Input atoms are split between adjacent nodes to preserve their first moments before the + /// lattice masses are convolved by FFT. /// /// The first distribution's atom values. - /// The first distribution's atom masses (non-negative; typically summing to one). + /// The first distribution's atom masses. /// The second distribution's atom values. /// The second distribution's atom masses. - /// The per-input lattice resolution (rounded up to a power of two). Default = 4096. - /// The convolved lattice values (uniform, ascending). - /// The convolved lattice masses (non-negative; FFT ringing is floored and the total mass renormalized to the product of the input totals). + /// The requested per-input lattice resolution. + /// The occupied convolved lattice values. + /// The non-negative convolved lattice masses. /// Thrown when any input list is null. - /// Thrown when a values/masses pair is empty or mismatched in length, or a mass is negative or non-finite. + /// Thrown when a values/masses pair is empty or mismatched, a value is non-finite, a mass is negative or non-finite, or a total mass is not positive. public static void ConvolveDiscrete(IList values1, IList masses1, IList values2, IList masses2, int latticePoints, out double[] values, out double[] masses) { - ValidateAtoms(values1, masses1, nameof(values1)); - ValidateAtoms(values2, masses2, nameof(values2)); + double total1 = ValidateAtoms(values1, masses1, nameof(values1)); + double total2 = ValidateAtoms(values2, masses2, nameof(values2)); if (latticePoints < 8) latticePoints = 8; int n = Tools.NextPowerOfTwo(latticePoints); - // The shared lattice: each input is binned over its own span, but on the SAME step - // so the convolution lattice is uniform. The step spans the summed support. - double min1 = Min(values1), max1 = Max(values1); - double min2 = Min(values2), max2 = Max(values2); - double span = Math.Max((max1 - min1) + (max2 - min2), Tools.DoubleMachineEpsilon); - double step = span / (n - 1); + double min1 = MinimumPositiveMassValue(values1, masses1); + double max1 = MaximumPositiveMassValue(values1, masses1); + double min2 = MinimumPositiveMassValue(values2, masses2); + double max2 = MaximumPositiveMassValue(values2, masses2); + double span = (max1 - min1) + (max2 - min2); + double expected = total1 * total2; + if (span == 0d) + { + values = [min1 + min2]; + masses = [expected]; + return; + } + double step = span / (n - 1); var lattice1 = DepositAtoms(values1, masses1, min1, step, n); var lattice2 = DepositAtoms(values2, masses2, min2, step, n); - // FFT convolution of the mass vectors (zero-padded complex arrays). int fftSize = Tools.NextPowerOfTwo(2 * n); var fft1 = new double[2 * fftSize]; var fft2 = new double[2 * fftSize]; @@ -641,52 +660,80 @@ public static void ConvolveDiscrete(IList values1, IList masses1 var product = new double[2 * fftSize]; for (int i = 0; i < fftSize; i++) { - double re1 = fft1[2 * i], im1 = fft1[2 * i + 1]; - double re2 = fft2[2 * i], im2 = fft2[2 * i + 1]; - product[2 * i] = re1 * re2 - im1 * im2; - product[2 * i + 1] = re1 * im2 + im1 * re2; + double real1 = fft1[2 * i]; + double imaginary1 = fft1[2 * i + 1]; + double real2 = fft2[2 * i]; + double imaginary2 = fft2[2 * i + 1]; + product[2 * i] = real1 * real2 - imaginary1 * imaginary2; + product[2 * i + 1] = real1 * imaginary2 + imaginary1 * real2; } Mathematics.Fourier.FFT(product, inverse: true); - // The result ladder: 2n − 1 meaningful nodes from min1 + min2, floored against FFT - // ringing and renormalized to the exact product of the input mass totals. - int resultCount = 2 * n - 1; + int resultCount = LastPositiveIndex(lattice1) + LastPositiveIndex(lattice2) + 1; values = new double[resultCount]; masses = new double[resultCount]; double total = 0d; for (int i = 0; i < resultCount; i++) { - values[i] = (min1 + min2) + i * step; + values[i] = min1 + min2 + i * step; double mass = product[2 * i] / fftSize; masses[i] = mass > 0d ? mass : 0d; total += masses[i]; } - double expected = Sum(masses1) * Sum(masses2); - if (total > 0d && expected > 0d) - { - double scale = expected / total; - for (int i = 0; i < resultCount; i++) masses[i] *= scale; - } + if (!Tools.IsFinite(total) || total <= 0d) + throw new InvalidOperationException("The discrete convolution produced no finite positive mass."); + + double scale = expected / total; + for (int i = 0; i < resultCount; i++) masses[i] *= scale; } /// - /// Validates one atom list pair. + /// Validates one atom list and returns its total mass. /// /// The atom values. /// The atom masses. /// The reported parameter name. - private static void ValidateAtoms(IList values, IList masses, string parameterName) + /// The finite positive total mass. + private static double ValidateAtoms(IList values, IList masses, string parameterName) { if (values == null || masses == null) throw new ArgumentNullException(parameterName); if (values.Count == 0 || values.Count != masses.Count) throw new ArgumentException("Atom values and masses must be non-empty and equal in length.", parameterName); + + double total = 0d; for (int i = 0; i < masses.Count; i++) { if (!Tools.IsFinite(values[i]) || !Tools.IsFinite(masses[i]) || masses[i] < 0d) throw new ArgumentException("Atom values must be finite and masses non-negative.", parameterName); + total += masses[i]; } + if (!Tools.IsFinite(total) || total <= 0d) + throw new ArgumentException("Atom masses must have a finite positive total.", parameterName); + return total; } + private static double MinimumPositiveMassValue(IList values, IList masses) + { + double minimum = double.MaxValue; + for (int i = 0; i < values.Count; i++) + if (masses[i] > 0d && values[i] < minimum) minimum = values[i]; + return minimum; + } + + private static double MaximumPositiveMassValue(IList values, IList masses) + { + double maximum = double.MinValue; + for (int i = 0; i < values.Count; i++) + if (masses[i] > 0d && values[i] > maximum) maximum = values[i]; + return maximum; + } + + private static int LastPositiveIndex(IList masses) + { + for (int i = masses.Count - 1; i >= 0; i--) + if (masses[i] > 0d) return i; + throw new InvalidOperationException("The lattice contains no positive mass."); + } /// /// Deposits atoms onto a uniform lattice with the moment-preserving two-node split: an /// atom between nodes splits its mass so the lattice mean reproduces the atom mean @@ -750,10 +797,8 @@ private static double Sum(IList values) } /// - /// Serializes the empirical distribution to an XElement, overriding the base scalar-only - /// form: the X and probability tables (round-trip-exact "G17"), the probability sort - /// order, and both interpolation transforms. The base implementation writes only scalar - /// parameters, which lost the tables entirely. + /// Serializes the X and probability tables, probability ordering, and interpolation + /// transforms using invariant round-trip numeric formatting. /// /// An XElement representation of the empirical distribution. public override XElement ToXElement() @@ -786,46 +831,64 @@ public override XElement ToXElement() /// . /// /// The XElement to deserialize. - /// A new . + /// A validated . /// Thrown when is null. - /// Thrown when the element carries no parseable X/probability tables. + /// Thrown when serialized tables, ordering, or transforms are missing or invalid. public static EmpiricalDistribution FromXElement(XElement xElement) { if (xElement == null) throw new ArgumentNullException(nameof(xElement)); string? xText = xElement.Attribute(nameof(XValues))?.Value; string? pText = xElement.Attribute(nameof(ProbabilityValues))?.Value; - if (string.IsNullOrEmpty(xText) || string.IsNullOrEmpty(pText)) + if (string.IsNullOrWhiteSpace(xText) || string.IsNullOrWhiteSpace(pText)) throw new ArgumentException("The serialized empirical distribution is missing its X or probability table.", nameof(xElement)); string[] xTokens = xText!.Split('|'); string[] pTokens = pText!.Split('|'); - if (xTokens.Length != pTokens.Length) - throw new ArgumentException("The serialized empirical distribution's X and probability tables differ in length.", nameof(xElement)); + if (xTokens.Length != pTokens.Length || xTokens.Length < 2) + throw new ArgumentException("The serialized empirical tables must have equal lengths of at least two.", nameof(xElement)); var xValues = new double[xTokens.Length]; var pValues = new double[pTokens.Length]; for (int i = 0; i < xTokens.Length; i++) { if (!double.TryParse(xTokens[i], NumberStyles.Any, CultureInfo.InvariantCulture, out xValues[i]) - || !double.TryParse(pTokens[i], NumberStyles.Any, CultureInfo.InvariantCulture, out pValues[i])) - { - throw new ArgumentException("The serialized empirical distribution carries an unparseable table value.", nameof(xElement)); - } + || !double.TryParse(pTokens[i], NumberStyles.Any, CultureInfo.InvariantCulture, out pValues[i]) + || !Tools.IsFinite(xValues[i]) + || !Tools.IsFinite(pValues[i])) + throw new ArgumentException("The serialized empirical distribution contains an invalid table value.", nameof(xElement)); } - var order = SortOrder.Ascending; - var orderAttr = xElement.Attribute("ProbabilityOrder"); - if (orderAttr != null) Enum.TryParse(orderAttr.Value, out order); + var orderAttribute = xElement.Attribute("ProbabilityOrder"); + if (orderAttribute == null + || !Enum.TryParse(orderAttribute.Value, out SortOrder order) + || !Enum.IsDefined(typeof(SortOrder), order) + || (order != SortOrder.Ascending && order != SortOrder.Descending)) + throw new ArgumentException("The serialized empirical distribution has an invalid probability order.", nameof(xElement)); var distribution = new EmpiricalDistribution(xValues, pValues, SortOrder.Ascending, order); - if (Enum.TryParse(xElement.Attribute(nameof(XTransform))?.Value, out Transform xTransform)) + if (!distribution.ParametersValid) + throw new ArgumentException("The serialized empirical tables do not define a valid distribution.", nameof(xElement)); + + var xTransformAttribute = xElement.Attribute(nameof(XTransform)); + if (xTransformAttribute != null) + { + if (!Enum.TryParse(xTransformAttribute.Value, out Transform xTransform) + || !Enum.IsDefined(typeof(Transform), xTransform)) + throw new ArgumentException("The serialized empirical distribution has an invalid X transform.", nameof(xElement)); distribution.XTransform = xTransform; - if (Enum.TryParse(xElement.Attribute(nameof(ProbabilityTransform))?.Value, out Transform probabilityTransform)) + } + + var probabilityTransformAttribute = xElement.Attribute(nameof(ProbabilityTransform)); + if (probabilityTransformAttribute != null) + { + if (!Enum.TryParse(probabilityTransformAttribute.Value, out Transform probabilityTransform) + || !Enum.IsDefined(typeof(Transform), probabilityTransform)) + throw new ArgumentException("The serialized empirical distribution has an invalid probability transform.", nameof(xElement)); distribution.ProbabilityTransform = probabilityTransform; + } return distribution; } - /// /// Convolves two empirical distributions using FFT. /// diff --git a/Numerics/Distributions/Univariate/KernelDensity.cs b/Numerics/Distributions/Univariate/KernelDensity.cs index a722ce53..aec52f06 100644 --- a/Numerics/Distributions/Univariate/KernelDensity.cs +++ b/Numerics/Distributions/Univariate/KernelDensity.cs @@ -92,6 +92,8 @@ public KernelDensity(IList sampleData, KernelType kernel, double bandwid /// public KernelDensity(IList sampleData, IList weights, KernelType kernel = KernelType.Gaussian, double? bandwidthParameter = null) { + if (sampleData == null) throw new ArgumentNullException(nameof(sampleData)); + if (weights == null) throw new ArgumentNullException(nameof(weights)); if (weights.Count != sampleData.Count) throw new ArgumentException("weights length must match sampleData length"); @@ -149,6 +151,9 @@ public KernelType KernelDistribution get { return _kernelDistribution; } set { + if (!Enum.IsDefined(typeof(KernelType), value)) + throw new ArgumentOutOfRangeException(nameof(KernelDistribution), value, "The kernel type is not defined."); + _kernelDistribution = value; if (_kernelDistribution == KernelType.Epanechnikov) { @@ -166,6 +171,7 @@ public KernelType KernelDistribution { _kernel = new UniformKernel(); } + _cdfCreated = false; } } @@ -177,8 +183,10 @@ public double Bandwidth get { return _bandwidth; } set { - _parametersValid = ValidateParameters(value, false) is null; + ValidateParameters(value, true); _bandwidth = value; + _parametersValid = true; + _cdfCreated = false; } } @@ -569,7 +577,10 @@ public override void SetParameters(IList parameters) /// Sample of data, no sorting is assumed. public void SetSampleData(IList sampleData) { + ValidateSampleData(sampleData); _sampleData = sampleData.ToArray(); + _weights = null; + _sumW = 1d; ComputeMoments(_sampleData); _cdfCreated = false; } @@ -581,20 +592,48 @@ public void SetSampleData(IList sampleData) /// Weights associated with each data point. public void SetSampleData(IList sampleData, IList weights) { + ValidateSampleData(sampleData); _sampleData = sampleData.ToArray(); + if (weights == null) throw new ArgumentNullException(nameof(weights)); + if (weights.Count != sampleData.Count) + throw new ArgumentException("The weight count must match the sample count.", nameof(weights)); + for (int i = 0; i < weights.Count; i++) + { + if (!Tools.IsFinite(weights[i]) || weights[i] < 0d) + throw new ArgumentException("Weights must be finite and non-negative.", nameof(weights)); + } + _weights = weights.ToArray(); _sumW = _weights.Sum(); + if (!Tools.IsFinite(_sumW) || _sumW <= 0d) + throw new ArgumentException("The total weight must be finite and positive.", nameof(weights)); - if (_sumW <= 0) throw new ArgumentException("All weights are zero or negative."); - - ComputeMoments(_sampleData, _weights); // weighted version + ComputeMoments(_sampleData, _weights); _cdfCreated = false; } + /// + /// Validates sample data before it is stored by the distribution. + /// + /// The sample values. + private static void ValidateSampleData(IList sampleData) + { + if (sampleData == null) throw new ArgumentNullException(nameof(sampleData)); + if (sampleData.Count == 0) + throw new ArgumentException("The sample must contain at least one value.", nameof(sampleData)); + for (int i = 0; i < sampleData.Count; i++) + { + if (!Tools.IsFinite(sampleData[i])) + throw new ArgumentException("Sample values must be finite.", nameof(sampleData)); + } + + } + /// public override double PDF(double x) { + if (_weights == null) { double total = 0d; @@ -664,10 +703,8 @@ public override UnivariateDistributionBase Clone() } /// - /// Serializes the kernel density to an XElement, overriding the base scalar-only form: - /// the sample data (round-trip-exact "G17"), the kernel type, the bandwidth, the - /// optional per-sample weights, and both interpolation transforms. The base - /// implementation writes only scalar parameters, which lost the sample entirely. + /// Serializes the sample data, kernel type, bandwidth, optional sample weights, + /// interpolation transforms, and bounded-data setting using invariant numeric formatting. /// /// An XElement representation of the kernel density. public override XElement ToXElement() @@ -701,58 +738,82 @@ public override XElement ToXElement() /// The XElement to deserialize. /// A new . /// Thrown when is null. - /// Thrown when the element carries no parseable sample data. + /// Thrown when serialized sample data or configuration is missing or invalid. public static KernelDensity FromXElement(XElement xElement) { if (xElement == null) throw new ArgumentNullException(nameof(xElement)); string? sampleText = xElement.Attribute(nameof(SampleData))?.Value; - if (string.IsNullOrEmpty(sampleText)) + if (string.IsNullOrWhiteSpace(sampleText)) throw new ArgumentException("The serialized kernel density is missing its sample data.", nameof(xElement)); string[] tokens = sampleText!.Split('|'); var samples = new double[tokens.Length]; for (int i = 0; i < tokens.Length; i++) { - if (!double.TryParse(tokens[i], NumberStyles.Any, CultureInfo.InvariantCulture, out samples[i])) - throw new ArgumentException("The serialized kernel density carries an unparseable sample value.", nameof(xElement)); + if (!double.TryParse(tokens[i], NumberStyles.Any, CultureInfo.InvariantCulture, out samples[i]) + || !Tools.IsFinite(samples[i])) + throw new ArgumentException("The serialized kernel density contains an invalid sample value.", nameof(xElement)); } - var kernel = KernelType.Gaussian; - var kernelAttr = xElement.Attribute(nameof(KernelDistribution)); - if (kernelAttr != null) Enum.TryParse(kernelAttr.Value, out kernel); + var kernelAttribute = xElement.Attribute(nameof(KernelDistribution)); + if (kernelAttribute == null + || !Enum.TryParse(kernelAttribute.Value, out KernelType kernel) + || !Enum.IsDefined(typeof(KernelType), kernel)) + throw new ArgumentException("The serialized kernel density has an invalid kernel type.", nameof(xElement)); - double bandwidth = 0d; - bool hasBandwidth = double.TryParse(xElement.Attribute(nameof(Bandwidth))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out bandwidth); + if (!double.TryParse(xElement.Attribute(nameof(Bandwidth))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double bandwidth) + || !Tools.IsFinite(bandwidth) + || bandwidth <= 0d) + throw new ArgumentException("The serialized kernel density has an invalid bandwidth.", nameof(xElement)); KernelDensity distribution; string? weightsText = xElement.Attribute("Weights")?.Value; - if (!string.IsNullOrEmpty(weightsText)) + if (weightsText != null) { - string[] weightTokens = weightsText!.Split('|'); + string[] weightTokens = weightsText.Split('|'); if (weightTokens.Length != samples.Length) throw new ArgumentException("The serialized kernel density's weight count does not match its sample count.", nameof(xElement)); var weights = new double[weightTokens.Length]; for (int i = 0; i < weightTokens.Length; i++) { - if (!double.TryParse(weightTokens[i], NumberStyles.Any, CultureInfo.InvariantCulture, out weights[i])) - throw new ArgumentException("The serialized kernel density carries an unparseable weight value.", nameof(xElement)); + if (!double.TryParse(weightTokens[i], NumberStyles.Any, CultureInfo.InvariantCulture, out weights[i]) + || !Tools.IsFinite(weights[i])) + throw new ArgumentException("The serialized kernel density contains an invalid weight.", nameof(xElement)); } - distribution = hasBandwidth ? new KernelDensity(samples, weights, kernel, bandwidth) : new KernelDensity(samples, weights, kernel); + distribution = new KernelDensity(samples, weights, kernel, bandwidth); } else { - distribution = hasBandwidth ? new KernelDensity(samples, kernel, bandwidth) : new KernelDensity(samples, kernel); + distribution = new KernelDensity(samples, kernel, bandwidth); } - if (Enum.TryParse(xElement.Attribute(nameof(XTransform))?.Value, out Transform xTransform)) + var xTransformAttribute = xElement.Attribute(nameof(XTransform)); + if (xTransformAttribute != null) + { + if (!Enum.TryParse(xTransformAttribute.Value, out Transform xTransform) + || !Enum.IsDefined(typeof(Transform), xTransform)) + throw new ArgumentException("The serialized kernel density has an invalid X transform.", nameof(xElement)); distribution.XTransform = xTransform; - if (Enum.TryParse(xElement.Attribute(nameof(ProbabilityTransform))?.Value, out Transform probabilityTransform)) + } + + var probabilityTransformAttribute = xElement.Attribute(nameof(ProbabilityTransform)); + if (probabilityTransformAttribute != null) + { + if (!Enum.TryParse(probabilityTransformAttribute.Value, out Transform probabilityTransform) + || !Enum.IsDefined(typeof(Transform), probabilityTransform)) + throw new ArgumentException("The serialized kernel density has an invalid probability transform.", nameof(xElement)); distribution.ProbabilityTransform = probabilityTransform; - if (bool.TryParse(xElement.Attribute(nameof(BoundedByData))?.Value, out bool bounded)) + } + + var boundedAttribute = xElement.Attribute(nameof(BoundedByData)); + if (boundedAttribute != null) + { + if (!bool.TryParse(boundedAttribute.Value, out bool bounded)) + throw new ArgumentException("The serialized kernel density has an invalid bounded-data flag.", nameof(xElement)); distribution.BoundedByData = bounded; + } return distribution; } - /// /// Create the empirical CDF. /// @@ -785,4 +846,4 @@ private void CreateCDF() } } -} \ No newline at end of file +} diff --git a/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs b/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs index b1041a32..93e9eacb 100644 --- a/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs +++ b/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs @@ -100,83 +100,90 @@ public BootstrapAnalysis(IUnivariateDistribution distribution, ParameterEstimati #region Methods /// - /// Bootstrap a list of fitted distributions. + /// Generates fitted bootstrap distributions. /// + /// The fitted distributions; isolated failed replications are represented by null entries. + /// Thrown when every replication fails after all retries. public IUnivariateDistribution[] Distributions() { var bootDistributions = new IUnivariateDistribution[Replications]; - var r = new MersenneTwister(PRNGSeed); - var seeds = r.NextIntegers(Replications); + var failuresByReplication = new Exception?[Replications]; + var random = new MersenneTwister(PRNGSeed); + var seeds = random.NextIntegers(Replications); int failures = 0; - Parallel.For(0, Replications, idx => + + Parallel.For(0, Replications, index => { - bool failed = false; - for (int m = 0; m < _retries; m++) + Exception? lastFailure = null; + for (int attempt = 0; attempt < _retries; attempt++) { try { - bootDistributions[idx] = Distribution.Bootstrap(EstimationMethod, SampleSize, seeds[idx] + 10 * m); - failed = false; + bootDistributions[index] = Distribution.Bootstrap(EstimationMethod, SampleSize, seeds[index] + 10 * attempt); + lastFailure = null; + break; } - catch (Exception) + catch (Exception exception) { - failed = true; - }; - - if (failed == false) break; + lastFailure = exception; + } } - // MLE and certain L-moments methods can fail to find a solution - // On fail, set to null - if (failed == true) + if (lastFailure != null || bootDistributions[index] == null) { - bootDistributions[idx] = null!; + failuresByReplication[index] = lastFailure + ?? new InvalidOperationException("The bootstrap fit returned no distribution."); + bootDistributions[index] = null!; Interlocked.Increment(ref failures); } - }); + FailedReplications = failures; + if (failures == Replications) + throw new AggregateException("Every bootstrap distribution fit failed.", failuresByReplication.Where(exception => exception != null).Cast()); return bootDistributions; } /// - /// Return a list of distributions given an array of parameter sets. + /// Creates fitted distributions from parameter sets. /// - /// An array of parameter sets. + /// The parameter sets. + /// The distributions; isolated invalid sets are represented by null entries. + /// Thrown when is null. + /// Thrown when every parameter set is invalid. public IUnivariateDistribution[] Distributions(ParameterSet[] parameterSets) { + if (parameterSets == null) throw new ArgumentNullException(nameof(parameterSets)); var bootDistributions = new IUnivariateDistribution[parameterSets.Length]; + if (parameterSets.Length == 0) return bootDistributions; + + var failuresByReplication = new Exception?[parameterSets.Length]; int failures = 0; - Parallel.For(0, parameterSets.Length, idx => + Parallel.For(0, parameterSets.Length, index => { - bool failed = false; - try { - var dist = ((UnivariateDistributionBase)Distribution).Clone(); - dist.SetParameters(parameterSets[idx].Values); - bootDistributions[idx] = dist; - failed = false; + var distribution = ((UnivariateDistributionBase)Distribution).Clone(); + distribution.ValidateParameters(parameterSets[index].Values, true); + distribution.SetParameters(parameterSets[index].Values); + if (!distribution.ParametersValid) + throw new ArgumentException("The parameter set does not define a valid distribution.", nameof(parameterSets)); + bootDistributions[index] = distribution; } - catch (Exception) - { - failed = true; - }; - - // On fail, set to null - if (failed == true) + catch (Exception exception) { - bootDistributions[idx] = null!; + failuresByReplication[index] = exception; + bootDistributions[index] = null!; Interlocked.Increment(ref failures); } - - }); + FailedReplications = failures; + if (failures == parameterSets.Length) + throw new AggregateException("Every bootstrap parameter set was invalid.", failuresByReplication.Where(exception => exception != null).Cast()); return bootDistributions; } - /// /// Bootstrap an array of distribution parameters. /// @@ -348,60 +355,65 @@ public UncertaintyAnalysisResults Estimate(IList probabilities, double a } /// - /// Bootstrap the expected non-exceedance probabilities given the input quantile values. Returns the x-values interpolated from the list of desired non-exceedance probabilities. + /// Interpolates quantiles at requested probabilities from the mean bootstrap CDF. /// - /// List quantile values. - /// List of non-exceedance probabilities. - /// Optional. Pass in an array of bootstrapped distributions. Default = null. + /// Quantile ordinates; ordering is not required. + /// The probabilities to interpolate. + /// Optional precomputed bootstrap distributions. + /// The interpolated quantiles. public double[] ExpectedProbabilities(IList quantiles, IList probabilities, IUnivariateDistribution[]? distributions = null) { - var quants = quantiles.ToArray(); - var probs = probabilities.ToArray(); - Array.Sort(quants); - var bootDistributions = distributions != null ? distributions : Distributions(); - var expected = MeanCDFs(quants, bootDistributions); - - double minY = double.MaxValue; - double maxY = double.MinValue; - var yVals = new List(); - var xVals = new List(); - yVals.Add(quantiles[0]); - xVals.Add(expected[0]); - for (int i = 1; i < quantiles.Count; i++) + if (quantiles == null) throw new ArgumentNullException(nameof(quantiles)); + if (probabilities == null) throw new ArgumentNullException(nameof(probabilities)); + if (quantiles.Count < 2) throw new ArgumentException("At least two quantiles are required.", nameof(quantiles)); + + var sortedQuantiles = quantiles.ToArray(); + Array.Sort(sortedQuantiles); + var targetProbabilities = probabilities.ToArray(); + var bootDistributions = distributions ?? Distributions(); + var expected = MeanCDFs(sortedQuantiles, bootDistributions); + + var cdfValues = new List { expected[0] }; + var ordinateValues = new List { sortedQuantiles[0] }; + double minimumOrdinate = sortedQuantiles[0]; + double maximumOrdinate = sortedQuantiles[0]; + for (int i = 1; i < sortedQuantiles.Length; i++) { - if (expected[i] > xVals.Last()) + if (expected[i] > cdfValues[cdfValues.Count - 1]) { - minY = Math.Min(minY, quantiles[i]); - maxY = Math.Max(maxY, quantiles[i]); - yVals.Add(quantiles[i]); - xVals.Add(expected[i]); + minimumOrdinate = Math.Min(minimumOrdinate, sortedQuantiles[i]); + maximumOrdinate = Math.Max(maximumOrdinate, sortedQuantiles[i]); + ordinateValues.Add(sortedQuantiles[i]); + cdfValues.Add(expected[i]); } } - bool useLogTransform = false; - if (minY > 0 && (Math.Log10(maxY) - Math.Log10(minY)) > 1) - useLogTransform = true; + if (cdfValues.Count < 2) + throw new InvalidOperationException("The mean bootstrap CDF does not contain two distinct probabilities."); - Linear linint = new Linear(xVals, yVals) { XTransform = Transform.NormalZ, YTransform = useLogTransform ? Transform.Logarithmic : Transform.None }; - return linint.Interpolate(probs); + bool useLogTransform = minimumOrdinate > 0d + && Math.Log10(maximumOrdinate) - Math.Log10(minimumOrdinate) > 1d; + var interpolation = new Linear(cdfValues, ordinateValues) + { + XTransform = Transform.NormalZ, + YTransform = useLogTransform ? Transform.Logarithmic : Transform.None + }; + return interpolation.Interpolate(targetProbabilities); } /// - /// The mean CDF across the bootstrapped distributions at each quantile. + /// Computes the mean CDF across successful bootstrap fits at each quantile. /// - /// The quantile values to evaluate, ascending. - /// The bootstrapped distributions; null entries are failed fits. - /// The expected non-exceedance probability at each quantile. - /// - /// Replications are split into chunks, each summed - /// sequentially and merged in chunk order, so the result does not depend on the thread - /// count. Failed fits are excluded from both the sum and the divisor. - /// + /// The quantiles to evaluate. + /// The bootstrap distributions; null entries represent failed fits. + /// The mean CDF values. private static double[] MeanCDFs(double[] quantiles, IUnivariateDistribution[] distributions) { int replications = distributions.Length; int quantileCount = quantiles.Length; var expected = new double[quantileCount]; - if (replications == 0 || quantileCount == 0) return expected; + if (quantileCount == 0) return expected; + if (replications == 0) + throw new InvalidOperationException("No bootstrap distributions were supplied."); int chunks = Math.Min(ReductionChunks, replications); var chunkSums = new double[chunks][]; @@ -420,16 +432,15 @@ private static double[] MeanCDFs(double[] quantiles, IUnivariateDistribution[] d if (distribution == null) continue; valid++; for (int i = 0; i < quantileCount; i++) - { accumulator[i] += distribution.CDF(quantiles[i]); - } } chunkValid[c] = valid; }); int validCount = 0; for (int c = 0; c < chunks; c++) validCount += chunkValid[c]; - if (validCount == 0) return expected; + if (validCount == 0) + throw new InvalidOperationException("Every bootstrap distribution fit failed."); for (int i = 0; i < quantileCount; i++) { @@ -439,7 +450,6 @@ private static double[] MeanCDFs(double[] quantiles, IUnivariateDistribution[] d } return expected; } - /// /// Bootstrap the expected non-exceedance probabilities given the input quantile values. /// @@ -481,510 +491,453 @@ public double[] ComputeMinMaxQuantiles(double minProbability, double maxProbabil if (local.Max > output[1]) output[1] = local.Max; } }); + if (output[0] == double.MaxValue || output[1] == double.MinValue) + throw new InvalidOperationException("Every bootstrap distribution fit failed."); return output; } /// - /// Bootstrap confidence intervals for a list of quantiles using the percentile method. + /// Computes percentile bootstrap confidence intervals for quantiles. /// - /// List of non-exceedance probabilities. - /// The confidence level; Default = 0.1, which will result in the 90% confidence intervals. - /// Optional. Pass in an array of bootstrapped distributions. Default = null. + /// The non-exceedance probabilities. + /// The excluded two-sided probability. + /// Optional precomputed bootstrap distributions. + /// The lower and upper confidence limits for each probability. public double[,] PercentileQuantileCI(IList probabilities, double alpha = 0.1, IUnivariateDistribution[]? distributions = null) { - var CIs = new double[] { alpha / 2d, 1d - alpha / 2d }; - var Output = new double[probabilities.Count, 2]; - var bootDistributions = distributions != null ? distributions : Distributions(); + var confidenceProbabilities = new[] { alpha / 2d, 1d - alpha / 2d }; + var output = new double[probabilities.Count, 2]; + var bootDistributions = distributions ?? Distributions(); for (int i = 0; i < probabilities.Count; i++) { - var XValues = new double[bootDistributions.Length]; - Parallel.For(0, bootDistributions.Length, idx => { XValues[idx] = bootDistributions[idx] != null ? bootDistributions[idx].InverseCDF(probabilities[i]) : double.NaN; }); - - // Filter valid values and sort - int validCount = 0; - for (int k = 0; k < XValues.Length; k++) - { - if (!double.IsNaN(XValues[k])) validCount++; - } - var validValues = new double[validCount]; - int writeIdx = 0; - for (int k = 0; k < XValues.Length; k++) + var values = new double[bootDistributions.Length]; + Parallel.For(0, bootDistributions.Length, index => { - if (!double.IsNaN(XValues[k])) - validValues[writeIdx++] = XValues[k]; - } - Array.Sort(validValues); + values[index] = bootDistributions[index] != null + ? bootDistributions[index].InverseCDF(probabilities[i]) + : double.NaN; + }); - // Record percentiles for CIs - for (int j = 0; j < 2; j++) - Output[i, j] = Statistics.Percentile(validValues, CIs[j], true); + var successfulValues = FiniteValuesOrThrow(values, "percentile confidence intervals"); + Array.Sort(successfulValues); + for (int j = 0; j < confidenceProbabilities.Length; j++) + output[i, j] = Statistics.Percentile(successfulValues, confidenceProbabilities[j], true); } - return Output; + return output; } - /// - /// Bootstrap confidence intervals for a list of quantiles using the bias-corrected percentile method. + /// Computes bias-corrected percentile confidence intervals for quantiles. /// - /// List of non-exceedance probabilities. - /// The confidence level; Default = 0.1, which will result in the 90% confidence intervals. - /// Optional. Pass in an array of bootstrapped distributions. Default = null. + /// The non-exceedance probabilities. + /// The excluded two-sided probability. + /// Optional precomputed bootstrap distributions. + /// The lower and upper confidence limits for each probability. public double[,] BiasCorrectedQuantileCI(IList probabilities, double alpha = 0.1, IUnivariateDistribution[]? distributions = null) { - // Create list of original X values given probability values - var populationXValues = new double[probabilities.Count]; + var populationValues = new double[probabilities.Count]; for (int i = 0; i < probabilities.Count; i++) - populationXValues[i] = Distribution.InverseCDF(probabilities[i]); + populationValues[i] = Distribution.InverseCDF(probabilities[i]); - var CIs = new double[] { alpha / 2d, 1d - alpha / 2d }; - var Output = new double[probabilities.Count, 2]; - var bootDistributions = distributions != null ? distributions : Distributions(); - int replications = bootDistributions.Length; + var confidenceProbabilities = new[] { alpha / 2d, 1d - alpha / 2d }; + var output = new double[probabilities.Count, 2]; + var bootDistributions = distributions ?? Distributions(); for (int i = 0; i < probabilities.Count; i++) { - var XValues = new double[replications]; - Parallel.For(0, replications, idx => + var values = new double[bootDistributions.Length]; + Parallel.For(0, bootDistributions.Length, index => { - XValues[idx] = bootDistributions[idx] != null ? bootDistributions[idx].InverseCDF(probabilities[i]) : double.NaN; + values[index] = bootDistributions[index] != null + ? bootDistributions[index].InverseCDF(probabilities[i]) + : double.NaN; }); - // Counted sequentially so the proportion does not depend on the thread count. - double P0 = 0d; // proportions of values less than population - for (int idx = 0; idx < replications; idx++) - { - if (!double.IsNaN(XValues[idx]) && XValues[idx] <= populationXValues[i]) P0 += 1d; - } - - // get proportion - P0 = P0 / (replications + 1); - - // Filter valid values and sort - int validCount = 0; - for (int k = 0; k < XValues.Length; k++) - { - if (!double.IsNaN(XValues[k])) validCount++; - } - var validValues = new double[validCount]; - int writeIdx = 0; - for (int k = 0; k < XValues.Length; k++) - { - if (!double.IsNaN(XValues[k])) - validValues[writeIdx++] = XValues[k]; - } - Array.Sort(validValues); + var successfulValues = FiniteValuesOrThrow(values, "bias-corrected confidence intervals"); + int lessOrEqual = 0; + for (int index = 0; index < successfulValues.Length; index++) + if (successfulValues[index] <= populationValues[i]) lessOrEqual++; - // Record percentiles for CIs - for (int j = 0; j < 2; j++) + double proportion = (lessOrEqual + 1d) / (successfulValues.Length + 1d); + Array.Sort(successfulValues); + double bias = Normal.StandardZ(proportion); + for (int j = 0; j < confidenceProbabilities.Length; j++) { - double Z0 = Normal.StandardZ(P0); - double Z = Normal.StandardZ(CIs[j]); - double BC = Normal.StandardCDF(2d * Z0 + Z); - Output[i, j] = Statistics.Percentile(validValues, BC, true); + double adjusted = Normal.StandardCDF(2d * bias + Normal.StandardZ(confidenceProbabilities[j])); + output[i, j] = Statistics.Percentile(successfulValues, adjusted, true); } } - return Output; + return output; } - /// - /// Bootstrap confidence intervals for a list of quantiles using the Normal, or standard method. + /// Computes normal-approximation bootstrap confidence intervals for quantiles. /// - /// List of non-exceedance probabilities. - /// The confidence level; Default = 0.1, which will result in the 90% confidence intervals. - /// Optional. Pass in an array of bootstrapped distributions. Default = null. + /// The non-exceedance probabilities. + /// The excluded two-sided probability. + /// Optional precomputed bootstrap distributions. + /// The lower and upper confidence limits for each probability. public double[,] NormalQuantileCI(IList probabilities, double alpha = 0.1, IUnivariateDistribution[]? distributions = null) { - - // Create list of original X values given probability values - // Use a cube-root transform to make results transformation invariant - var populationXValues = new double[probabilities.Count]; + var transformedPopulationValues = new double[probabilities.Count]; for (int i = 0; i < probabilities.Count; i++) - populationXValues[i] = Math.Pow(Distribution.InverseCDF(probabilities[i]), 1d / 3d); + transformedPopulationValues[i] = CubeRoot(Distribution.InverseCDF(probabilities[i])); - var CIs = new double[] { alpha / 2d, 1d - alpha / 2d }; - var Output = new double[probabilities.Count, 2]; - var bootDistributions = distributions != null ? distributions : Distributions(); + var confidenceProbabilities = new[] { alpha / 2d, 1d - alpha / 2d }; + var output = new double[probabilities.Count, 2]; + var bootDistributions = distributions ?? Distributions(); for (int i = 0; i < probabilities.Count; i++) { - var XValues = new double[bootDistributions.Length]; - Parallel.For(0, bootDistributions.Length, idx => { XValues[idx] = bootDistributions[idx] != null ? Math.Pow(bootDistributions[idx].InverseCDF(probabilities[i]), 1d / 3d) : double.NaN; }); - - // Filter valid values - int validCount = 0; - for (int k = 0; k < XValues.Length; k++) - { - if (!double.IsNaN(XValues[k])) validCount++; - } - var validValues = new double[validCount]; - int writeIdx = 0; - for (int k = 0; k < XValues.Length; k++) + var transformedValues = new double[bootDistributions.Length]; + Parallel.For(0, bootDistributions.Length, index => { - if (!double.IsNaN(XValues[k])) - validValues[writeIdx++] = XValues[k]; - } - - // Get Standard error - double SE = Statistics.StandardDeviation(validValues); + transformedValues[index] = bootDistributions[index] != null + ? CubeRoot(bootDistributions[index].InverseCDF(probabilities[i])) + : double.NaN; + }); - // Record percentiles for CIs - for (int j = 0; j < 2; j++) + var successfulValues = FiniteValuesOrThrow(transformedValues, "normal confidence intervals", 2); + double standardError = Statistics.StandardDeviation(successfulValues); + for (int j = 0; j < confidenceProbabilities.Length; j++) { - double Z = Normal.StandardZ(CIs[j]); - Output[i, j] = Math.Pow(populationXValues[i] + SE * Z, 3d); + double transformedLimit = transformedPopulationValues[i] + + standardError * Normal.StandardZ(confidenceProbabilities[j]); + output[i, j] = transformedLimit * transformedLimit * transformedLimit; } } - return Output; + return output; } - #region Bias-Corrected and Accelerated /// - /// Bootstrap confidence intervals for a list of quantiles using the bias-corrected and accelerated (BCa) percentile method. + /// Computes bias-corrected and accelerated percentile confidence intervals. /// - /// Sample of data. - /// List of non-exceedance probabilities. - /// The confidence level; Default = 0.1, which will result in the 90% confidence intervals. + /// The sample used to estimate the parent distribution. + /// The non-exceedance probabilities. + /// The excluded two-sided probability. + /// The lower and upper confidence limits for each probability. public double[,] BCaQuantileCI(IList sampleData, IList probabilities, double alpha = 0.1) { - var CIs = new double[] { alpha / 2d, 1d - alpha / 2d }; - var Output = new double[probabilities.Count, 2]; + var confidenceProbabilities = new[] { alpha / 2d, 1d - alpha / 2d }; + var output = new double[probabilities.Count, 2]; - // Estimate distribution SampleSize = sampleData.Count; ((IEstimation)Distribution).Estimate(sampleData, EstimationMethod); - - // Create list of original X values given probability values - var populationXValues = new double[probabilities.Count]; + var populationValues = new double[probabilities.Count]; for (int i = 0; i < probabilities.Count; i++) - populationXValues[i] = Distribution.InverseCDF(probabilities[i]); - - // Get acceleration constants - var a = AccelerationConstants(sampleData, probabilities, populationXValues); + populationValues[i] = Distribution.InverseCDF(probabilities[i]); - // Get bootstrapped distributions + var acceleration = AccelerationConstants(sampleData, probabilities, populationValues); var bootDistributions = Distributions(); for (int i = 0; i < probabilities.Count; i++) { - var XValues = new double[Replications]; - Parallel.For(0, Replications, idx => + var values = new double[bootDistributions.Length]; + Parallel.For(0, bootDistributions.Length, index => { - XValues[idx] = bootDistributions[idx] != null ? bootDistributions[idx].InverseCDF(probabilities[i]) : double.NaN; + values[index] = bootDistributions[index] != null + ? bootDistributions[index].InverseCDF(probabilities[i]) + : double.NaN; }); - // Counted sequentially so the proportion does not depend on the thread count. - double P0 = 0d; // proportions of values less than population - for (int idx = 0; idx < Replications; idx++) - { - if (!double.IsNaN(XValues[idx]) && XValues[idx] <= populationXValues[i]) P0 += 1d; - } + var successfulValues = FiniteValuesOrThrow(values, "BCa confidence intervals"); + int lessOrEqual = 0; + for (int index = 0; index < successfulValues.Length; index++) + if (successfulValues[index] <= populationValues[i]) lessOrEqual++; - // get proportion - P0 = (P0 + 1) / (Replications + 1); - - // Filter valid values and sort - int validCount = 0; - for (int k = 0; k < XValues.Length; k++) + double proportion = (lessOrEqual + 1d) / (successfulValues.Length + 1d); + double bias = Normal.StandardZ(proportion); + Array.Sort(successfulValues); + for (int j = 0; j < confidenceProbabilities.Length; j++) { - if (!double.IsNaN(XValues[k])) validCount++; - } - var validValues = new double[validCount]; - int writeIdx = 0; - for (int k = 0; k < XValues.Length; k++) - { - if (!double.IsNaN(XValues[k])) - validValues[writeIdx++] = XValues[k]; - } - Array.Sort(validValues); - - // Record percentiles for CIs - for (int j = 0; j < 2; j++) - { - double Z0 = Normal.StandardZ(P0); - double Z = Normal.StandardZ(CIs[j]); - double num = Z0 + Z; - double den = 1 - a[i] * (Z0 + Z); - double BC = Normal.StandardCDF(Z0 + num / den); - Output[i, j] = Statistics.Percentile(validValues, BC, true); + double normalQuantile = Normal.StandardZ(confidenceProbabilities[j]); + double denominator = 1d - acceleration[i] * (bias + normalQuantile); + double adjusted = Normal.StandardCDF(bias + (bias + normalQuantile) / denominator); + output[i, j] = Statistics.Percentile(successfulValues, adjusted, true); } } - return Output; + return output; } - /// - /// Estimates the acceleration constants for each probability. + /// Estimates acceleration constants from successful leave-one-out fits. /// - /// Sample of data. - /// List of non-exceedance probabilities. - /// The list of best-estimate quantiles. - /// - /// Chunked so the moment sums merge in a fixed order, independent of the thread count. - /// Each chunk refills one leave-one-out buffer rather than copying the sample per point. - /// private double[] AccelerationConstants(IList sampleData, IList probabilities, IList thetaHats) { - var N = sampleData.Count; + int sampleCount = sampleData.Count; int probabilityCount = probabilities.Count; - var a = new double[probabilityCount]; - if (N == 0) return a; - - int chunks = Math.Min(ReductionChunks, N); - var chunkI2 = new double[chunks][]; - var chunkI3 = new double[chunks][]; - for (int c = 0; c < chunks; c++) + var acceleration = new double[probabilityCount]; + if (sampleCount == 0) return acceleration; + + int chunks = Math.Min(ReductionChunks, sampleCount); + var chunkSecondMoments = new double[chunks][]; + var chunkThirdMoments = new double[chunks][]; + var chunkSuccesses = new int[chunks]; + var failures = new Exception?[sampleCount]; + for (int chunk = 0; chunk < chunks; chunk++) { - chunkI2[c] = new double[probabilityCount]; - chunkI3[c] = new double[probabilityCount]; + chunkSecondMoments[chunk] = new double[probabilityCount]; + chunkThirdMoments[chunk] = new double[probabilityCount]; } - Parallel.For(0, chunks, c => + Parallel.For(0, chunks, chunk => { - var i2 = chunkI2[c]; - var i3 = chunkI3[c]; - var jackSample = new double[N - 1]; - int start = (int)((long)c * N / chunks); - int end = (int)((long)(c + 1) * N / chunks); - for (int idx = start; idx < end; idx++) - { - for (int k = 0; k < idx; k++) jackSample[k] = sampleData[k]; - for (int k = idx + 1; k < N; k++) jackSample[k - 1] = sampleData[k]; - - // Cloned per point: a failed Estimate can leave the instance partially set. - var newDistribution = ((UnivariateDistributionBase)Distribution).Clone(); + var secondMoments = chunkSecondMoments[chunk]; + var thirdMoments = chunkThirdMoments[chunk]; + int start = (int)((long)chunk * sampleCount / chunks); + int end = (int)((long)(chunk + 1) * sampleCount / chunks); + int successes = 0; + for (int index = start; index < end; index++) + { + var jackknifeSample = new double[sampleCount - 1]; + for (int k = 0; k < index; k++) jackknifeSample[k] = sampleData[k]; + for (int k = index + 1; k < sampleCount; k++) jackknifeSample[k - 1] = sampleData[k]; + + var distribution = ((UnivariateDistributionBase)Distribution).Clone(); try { - ((IEstimation)newDistribution).Estimate(jackSample, EstimationMethod); + ((IEstimation)distribution).Estimate(jackknifeSample, EstimationMethod); for (int i = 0; i < probabilityCount; i++) { - double thetaJack = newDistribution.InverseCDF(probabilities[i]); - i2[i] += Math.Pow(thetaHats[i] - thetaJack, 2); - i3[i] += Math.Pow(thetaHats[i] - thetaJack, 3); + double difference = thetaHats[i] - distribution.InverseCDF(probabilities[i]); + secondMoments[i] += difference * difference; + thirdMoments[i] += difference * difference * difference; } + successes++; } - catch (Exception) + catch (Exception exception) { - // MLE and certain L-moments methods can fail to find a solution - }; + failures[index] = exception; + } } + chunkSuccesses[chunk] = successes; }); - // Get acceleration constant + int successfulFits = 0; + for (int chunk = 0; chunk < chunks; chunk++) successfulFits += chunkSuccesses[chunk]; + if (successfulFits == 0) + throw new AggregateException("Every jackknife acceleration fit failed.", failures.Where(exception => exception != null).Cast()); + for (int i = 0; i < probabilityCount; i++) { - double I2 = 0d, I3 = 0d; - for (int c = 0; c < chunks; c++) + double secondMoment = 0d; + double thirdMoment = 0d; + for (int chunk = 0; chunk < chunks; chunk++) { - I2 += chunkI2[c][i]; - I3 += chunkI3[c][i]; + secondMoment += chunkSecondMoments[chunk][i]; + thirdMoment += chunkThirdMoments[chunk][i]; } - a[i] = I3 / (Math.Pow(I2, 1.5) * 6); + acceleration[i] = secondMoment > 0d && Tools.IsFinite(secondMoment) + ? thirdMoment / (6d * Math.Pow(secondMoment, 1.5d)) + : 0d; } - - return a; + return acceleration; } - #endregion #region Bootstrap-t (aka Student-t Bootstrap) /// - /// Bootstrap confidence intervals for a list of quantiles using the Bootstrap-t method. + /// Computes studentized bootstrap confidence intervals for quantiles. /// - /// List of non-exceedance probabilities. - /// The confidence level; Default = 0.1, which will result in the 90% confidence intervals. + /// The non-exceedance probabilities. + /// The excluded two-sided probability. + /// The lower and upper confidence limits for each probability. public double[,] BootstrapTQuantileCI(IList probabilities, double alpha = 0.1) { - // Create list of original X values given probability values - // Use a cube-root transform to make results transformation invariant - var populationXValues = new double[probabilities.Count]; + var populationValues = new double[probabilities.Count]; for (int i = 0; i < probabilities.Count; i++) - populationXValues[i] = Math.Pow(Distribution.InverseCDF(probabilities[i]), 1d / 3d); - - var xValues = new double[Replications, probabilities.Count]; - var studentT = new double[Replications, probabilities.Count]; - var CIs = new double[] { alpha / 2d, 1d - alpha / 2d }; - var Output = new double[probabilities.Count, 2]; - - // First create list of bootstrap distributions, - // and estimate standard error for each quantiles - var bootDistributions = new IUnivariateDistribution[Replications]; - var r = new MersenneTwister(PRNGSeed); - var seeds = r.NextIntegers(Replications); - Parallel.For(0, Replications, i => + populationValues[i] = CubeRoot(Distribution.InverseCDF(probabilities[i])); + + var transformedValues = new double[Replications, probabilities.Count]; + var studentizedValues = new double[Replications, probabilities.Count]; + var failures = new Exception?[Replications]; + var confidenceProbabilities = new[] { alpha / 2d, 1d - alpha / 2d }; + var output = new double[probabilities.Count, 2]; + var random = new MersenneTwister(PRNGSeed); + var seeds = random.NextIntegers(Replications); + int failedFits = 0; + + Parallel.For(0, Replications, index => { try { - var newDistribution = ((UnivariateDistributionBase)Distribution).Clone(); - var sample = newDistribution.GenerateRandomValues(SampleSize, seeds[i]); - ((IEstimation)newDistribution).Estimate(sample, EstimationMethod); - bootDistributions[i] = newDistribution; + var distribution = ((UnivariateDistributionBase)Distribution).Clone(); + var sample = distribution.GenerateRandomValues(SampleSize, seeds[index]); + ((IEstimation)distribution).Estimate(sample, EstimationMethod); - // Record inner boot thetas - var bootXValues = new double[probabilities.Count]; + var bootstrapValues = new double[probabilities.Count]; for (int j = 0; j < probabilities.Count; j++) - bootXValues[j] = Math.Pow(bootDistributions[i].InverseCDF(probabilities[j]), 1d / 3d); + bootstrapValues[j] = CubeRoot(distribution.InverseCDF(probabilities[j])); - // Now estimate the standard error at each quantile using the jackknife method - //var bootSE = StandardError(sample, probabilities, bootXValues); - var bootSE = BootstrapStandardError(newDistribution, probabilities, 300, seeds[i]); + var standardErrors = BootstrapStandardError(distribution, probabilities, 300, seeds[index]); for (int j = 0; j < probabilities.Count; j++) { - xValues[i, j] = bootXValues[j]; - studentT[i, j] = (populationXValues[j] - bootXValues[j]) / bootSE[j]; + transformedValues[index, j] = bootstrapValues[j]; + studentizedValues[index, j] = (populationValues[j] - bootstrapValues[j]) / standardErrors[j]; } - } - catch (Exception) + catch (Exception exception) { - // MLE and certain L-moments methods can fail to find a solution - // On fail, set to null - bootDistributions[i] = null!; + failures[index] = exception; + Interlocked.Increment(ref failedFits); for (int j = 0; j < probabilities.Count; j++) { - xValues[i, j] = double.NaN; - studentT[i, j] = double.NaN; + transformedValues[index, j] = double.NaN; + studentizedValues[index, j] = double.NaN; } - }; - + } }); + if (failedFits == Replications) + throw new AggregateException("Every studentized bootstrap fit failed.", failures.Where(exception => exception != null).Cast()); for (int i = 0; i < probabilities.Count; i++) { - var rawX = xValues.GetColumn(i); - var rawT = studentT.GetColumn(i); - int validCount = 0; - for (int k = 0; k < rawX.Length; k++) - { - if (!double.IsNaN(rawX[k])) validCount++; - } - var XValues = new double[validCount]; - var TValues = new double[validCount]; - int writeIdx = 0; - for (int k = 0; k < rawX.Length; k++) + var rawValues = transformedValues.GetColumn(i); + var rawStudentized = studentizedValues.GetColumn(i); + var values = new List(); + var studentized = new List(); + for (int index = 0; index < rawValues.Length; index++) { - if (!double.IsNaN(rawX[k])) + if (Tools.IsFinite(rawValues[index]) && Tools.IsFinite(rawStudentized[index])) { - XValues[writeIdx] = rawX[k]; - TValues[writeIdx] = rawT[k]; - writeIdx++; + values.Add(rawValues[index]); + studentized.Add(rawStudentized[index]); } } + if (values.Count < 2) + throw new InvalidOperationException("Insufficient successful fits are available for studentized confidence intervals."); - // Get Standard error - double SE = Statistics.StandardDeviation(XValues); - Array.Sort(TValues); - - // Record percentiles for CIs - for (int j = 0; j < 2; j++) + double standardError = Statistics.StandardDeviation(values); + var sortedStudentized = studentized.ToArray(); + Array.Sort(sortedStudentized); + for (int j = 0; j < confidenceProbabilities.Length; j++) { - double T = Statistics.Percentile(TValues, CIs[j], true); - Output[i, j] = Math.Pow(populationXValues[i] + SE * T, 3d); + double studentizedQuantile = Statistics.Percentile(sortedStudentized, confidenceProbabilities[j], true); + double transformedLimit = populationValues[i] + standardError * studentizedQuantile; + output[i, j] = transformedLimit * transformedLimit * transformedLimit; } } - - return Output; + return output; } - /// - /// Estimates the standard error for each probability using the parametric bootstrap. + /// Estimates quantile standard errors using successful inner bootstrap fits. /// - ///The parent distribution. - ///The list of probabilities where the standard error is calculated. - ///The number of bootstrap replications. Default = 300. - ///The PRNG seed. Default = 12345. private double[] BootstrapStandardError(UnivariateDistributionBase parentDist, IList probabilities, int replications = 300, int seed = 12345) { - int B = replications; - var r = new MersenneTwister(seed); - var seeds = r.NextIntegers(replications); - var xValues = new double[B, probabilities.Count]; - var se = new double[probabilities.Count]; - Parallel.For(0, replications, i => + var random = new MersenneTwister(seed); + var seeds = random.NextIntegers(replications); + var values = new double[replications, probabilities.Count]; + var failures = new Exception?[replications]; + int failedFits = 0; + + Parallel.For(0, replications, index => { try { - var bootDist = parentDist.Clone(); - var sample = bootDist.GenerateRandomValues(SampleSize, seeds[i]); - ((IEstimation)bootDist).Estimate(sample, EstimationMethod); - - // Record inner boot thetas + var distribution = parentDist.Clone(); + var sample = distribution.GenerateRandomValues(SampleSize, seeds[index]); + ((IEstimation)distribution).Estimate(sample, EstimationMethod); for (int j = 0; j < probabilities.Count; j++) - xValues[i, j] = Math.Pow(bootDist.InverseCDF(probabilities[j]), 1d / 3d); - + values[index, j] = CubeRoot(distribution.InverseCDF(probabilities[j])); } - catch (Exception) + catch (Exception exception) { - // MLE and certain L-moments methods can fail to find a solution - // On fail, set to null - - }; - + failures[index] = exception; + Interlocked.Increment(ref failedFits); + for (int j = 0; j < probabilities.Count; j++) values[index, j] = double.NaN; + } }); - // Get standard error + if (failedFits == replications) + throw new AggregateException("Every inner bootstrap fit failed.", failures.Where(exception => exception != null).Cast()); + + var standardErrors = new double[probabilities.Count]; for (int i = 0; i < probabilities.Count; i++) - se[i] = Statistics.StandardDeviation(xValues.GetColumn(i)); - return se; + { + var successfulValues = FiniteValuesOrThrow(values.GetColumn(i), "inner bootstrap standard errors", 2); + standardErrors[i] = Statistics.StandardDeviation(successfulValues); + } + return standardErrors; } - /// - /// Estimates the standard error for each probability. + /// Estimates jackknife standard errors from successful leave-one-out fits. /// - /// Sample of data. - /// List of non-exceedance probabilities. - /// The list of best-estimate quantiles. - /// - /// Chunked as . - /// private double[] StandardError(IList sampleData, IList probabilities, IList thetaHats) { - var N = sampleData.Count; + int sampleCount = sampleData.Count; int probabilityCount = probabilities.Count; - var se = new double[probabilityCount]; - if (N == 0) return se; + var standardErrors = new double[probabilityCount]; + if (sampleCount == 0) return standardErrors; - int chunks = Math.Min(ReductionChunks, N); - var chunkI2 = new double[chunks][]; - for (int c = 0; c < chunks; c++) chunkI2[c] = new double[probabilityCount]; + int chunks = Math.Min(ReductionChunks, sampleCount); + var chunkSecondMoments = new double[chunks][]; + var chunkSuccesses = new int[chunks]; + var failures = new Exception?[sampleCount]; + for (int chunk = 0; chunk < chunks; chunk++) + chunkSecondMoments[chunk] = new double[probabilityCount]; - // Perform Jackknife - Parallel.For(0, chunks, c => + Parallel.For(0, chunks, chunk => { - var i2 = chunkI2[c]; - var jackSample = new double[N - 1]; - int start = (int)((long)c * N / chunks); - int end = (int)((long)(c + 1) * N / chunks); - for (int idx = start; idx < end; idx++) + var secondMoments = chunkSecondMoments[chunk]; + int start = (int)((long)chunk * sampleCount / chunks); + int end = (int)((long)(chunk + 1) * sampleCount / chunks); + int successes = 0; + for (int index = start; index < end; index++) { - for (int k = 0; k < idx; k++) jackSample[k] = sampleData[k]; - for (int k = idx + 1; k < N; k++) jackSample[k - 1] = sampleData[k]; + var jackknifeSample = new double[sampleCount - 1]; + for (int k = 0; k < index; k++) jackknifeSample[k] = sampleData[k]; + for (int k = index + 1; k < sampleCount; k++) jackknifeSample[k - 1] = sampleData[k]; - var newDistribution = ((UnivariateDistributionBase)Distribution).Clone(); + var distribution = ((UnivariateDistributionBase)Distribution).Clone(); try { - ((IEstimation)newDistribution).Estimate(jackSample, EstimationMethod); + ((IEstimation)distribution).Estimate(jackknifeSample, EstimationMethod); for (int i = 0; i < probabilityCount; i++) { - double thetaJack = Math.Pow(newDistribution.InverseCDF(probabilities[i]), 1d / 3d); - i2[i] += Math.Pow(thetaHats[i] - thetaJack, 2); + double difference = thetaHats[i] - CubeRoot(distribution.InverseCDF(probabilities[i])); + secondMoments[i] += difference * difference; } + successes++; } - catch (Exception) + catch (Exception exception) { - // MLE and certain L-moments methods can fail to find a solution - }; + failures[index] = exception; + } } + chunkSuccesses[chunk] = successes; }); - // Get standard error + int successfulFits = 0; + for (int chunk = 0; chunk < chunks; chunk++) successfulFits += chunkSuccesses[chunk]; + if (successfulFits == 0) + throw new AggregateException("Every jackknife standard-error fit failed.", failures.Where(exception => exception != null).Cast()); + for (int i = 0; i < probabilityCount; i++) { - double I2 = 0d; - for (int c = 0; c < chunks; c++) I2 += chunkI2[c][i]; - se[i] = Math.Sqrt((N - 1) / (double)N * I2); + double secondMoment = 0d; + for (int chunk = 0; chunk < chunks; chunk++) secondMoment += chunkSecondMoments[chunk][i]; + standardErrors[i] = successfulFits > 1 + ? Math.Sqrt((successfulFits - 1d) / successfulFits * secondMoment) + : 0d; } - - return se; + return standardErrors; + } + /// + /// Returns finite results from successful fits and enforces a minimum sample count. + /// + private static double[] FiniteValuesOrThrow(double[] values, string operation, int minimumCount = 1) + { + var successfulValues = values.Where(Tools.IsFinite).ToArray(); + if (successfulValues.Length < minimumCount) + throw new InvalidOperationException("Insufficient successful fits are available for " + operation + "."); + return successfulValues; } + /// + /// Computes the real cube root while preserving the sign of negative values. + /// + private static double CubeRoot(double value) + { + if (value == 0d) return value; + return value < 0d ? -Math.Pow(-value, 1d / 3d) : Math.Pow(value, 1d / 3d); + } #endregion #endregion diff --git a/Numerics/Functions/CompositeFunction.cs b/Numerics/Functions/CompositeFunction.cs index 5ca43fcd..7be0dadd 100644 --- a/Numerics/Functions/CompositeFunction.cs +++ b/Numerics/Functions/CompositeFunction.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Runtime.CompilerServices; using System.Xml.Linq; namespace Numerics.Functions @@ -52,9 +53,12 @@ public CompositeFunction(IList functions) _weights = new double[functions.Count]; for (int i = 0; i < functions.Count; i++) { + if (functions[i] == null) throw new ArgumentException("Child functions cannot contain null entries.", nameof(functions)); _functions[i] = functions[i]; _weights[i] = 1d / functions.Count; } + _functionsView = Array.AsReadOnly(_functions); + _weightsView = Array.AsReadOnly(_weights); } /// @@ -73,28 +77,52 @@ public CompositeFunction(IList functions, IList wei if (functions.Count != weights.Count) throw new ArgumentException("The weight count must match the function count.", nameof(weights)); _functions = new IUnivariateFunction[functions.Count]; _weights = new double[functions.Count]; - for (int i = 0; i < functions.Count; i++) _functions[i] = functions[i]; + for (int i = 0; i < functions.Count; i++) + { + if (functions[i] == null) throw new ArgumentException("Child functions cannot contain null entries.", nameof(functions)); + _functions[i] = functions[i]; + } + _functionsView = Array.AsReadOnly(_functions); + _weightsView = Array.AsReadOnly(_weights); + ValidateParameters(weights, true); SetParameters(weights); } private bool _parametersValid = true; private IUnivariateFunction[] _functions; private double[] _weights; + private readonly IReadOnlyList _functionsView; + private readonly IReadOnlyList _weightsView; + private static readonly ConditionalWeakTable ChildLocks = new ConditionalWeakTable(); + + /// + /// The combination mode. Default = weighted average. + /// + private CompositeFunctionMode _mode = CompositeFunctionMode.WeightedAverage; /// /// The combination mode. Default = weighted average. /// - public CompositeFunctionMode Mode { get; set; } = CompositeFunctionMode.WeightedAverage; + public CompositeFunctionMode Mode + { + get { return _mode; } + set + { + if (!Enum.IsDefined(typeof(CompositeFunctionMode), value)) + throw new ArgumentOutOfRangeException(nameof(Mode), value, "The composite mode is not defined."); + _mode = value; + } + } /// /// The child functions. /// - public IReadOnlyList Functions => _functions; + public IReadOnlyList Functions => _functionsView; /// /// The child weights (non-negative, summing to one). /// - public IReadOnlyList Weights => _weights; + public IReadOnlyList Weights => _weightsView; /// /// The parameters are the child weights; children own their own parameters. @@ -199,10 +227,11 @@ public bool IsDeterministic /// The parameters are the child weights. public void SetParameters(IList parameters) { - // Validate parameters - _parametersValid = ValidateParameters(parameters, false) is null; - // Set parameters - for (int i = 0; i < _weights.Length && i < parameters.Count; i++) + var validationError = ValidateParameters(parameters, false); + if (validationError != null) return; + _parametersValid = true; + + for (int i = 0; i < _weights.Length; i++) _weights[i] = parameters[i]; } @@ -244,11 +273,10 @@ public double Function(double x) if (IsDeterministic == true || ConfidenceLevel < 0 || ConfidenceLevel > 1) { - // The mean convention: the weighted average of the children's own mean - // evaluations (children left at their configured state). + // Evaluate each child using the shared mean convention. double mean = 0d; for (int i = 0; i < _functions.Length; i++) - mean += _weights[i] * _functions[i].Function(x); + mean += _weights[i] * EvaluateChildAt(i, -1d, x); return mean; } @@ -272,6 +300,9 @@ public double InverseFunction(double y) if (ParametersValid == false) ValidateParameters(_weights, true); + if (!Tools.IsFinite(y)) + throw new ArgumentOutOfRangeException(nameof(y), "The inverse value must be finite."); + if (Mode == CompositeFunctionMode.Mixture && IsDeterministic == false && ConfidenceLevel >= 0 && ConfidenceLevel <= 1) { int index = SelectMixtureChild(ConfidenceLevel, out double remainder); @@ -284,8 +315,20 @@ public double InverseFunction(double y) double upper = Maximum; if (double.IsInfinity(lower) || lower == double.MinValue) lower = -1d; if (double.IsInfinity(upper) || upper == double.MaxValue) upper = 1d; - while (Function(lower) > y && Tools.IsFinite(lower)) lower = lower < 0 ? lower * 2d : (lower - 1d) * 2d; - while (Function(upper) < y && Tools.IsFinite(upper)) upper = upper > 0 ? upper * 2d : (upper + 1d) * 2d; + for (int iteration = 0; Function(lower) > y && iteration < 128; iteration++) + { + double candidate = lower < 0d ? lower * 2d : (lower - 1d) * 2d; + if (!Tools.IsFinite(candidate)) break; + lower = candidate; + } + for (int iteration = 0; Function(upper) < y && iteration < 128; iteration++) + { + double candidate = upper > 0d ? upper * 2d : (upper + 1d) * 2d; + if (!Tools.IsFinite(candidate)) break; + upper = candidate; + } + if (Function(lower) > y || Function(upper) < y) + throw new ArgumentOutOfRangeException(nameof(y), "The value is outside the invertible range of the composite function."); return Mathematics.RootFinding.Brent.Solve(h => Function(h) - y, lower, upper); } @@ -335,7 +378,7 @@ public static CompositeFunction FromXElement(XElement xElement) if (functions.Count == 0) throw new ArgumentException("The serialized composite function carries no child functions.", nameof(xElement)); - var composite = new CompositeFunction(functions); + CompositeFunction composite; string? weightsText = xElement.Attribute(nameof(Weights))?.Value; if (!string.IsNullOrEmpty(weightsText)) { @@ -348,10 +391,21 @@ public static CompositeFunction FromXElement(XElement xElement) if (!double.TryParse(tokens[i], NumberStyles.Any, CultureInfo.InvariantCulture, out weights[i])) throw new ArgumentException("The serialized composite function carries an unparseable weight.", nameof(xElement)); } - composite.SetParameters(weights); + composite = new CompositeFunction(functions, weights); + } + else + { + composite = new CompositeFunction(functions); } - if (Enum.TryParse(xElement.Attribute(nameof(Mode))?.Value, out CompositeFunctionMode mode)) + + string? modeText = xElement.Attribute(nameof(Mode))?.Value; + if (modeText != null) + { + if (!Enum.TryParse(modeText, out CompositeFunctionMode mode) + || !Enum.IsDefined(typeof(CompositeFunctionMode), mode)) + throw new ArgumentException("The serialized composite mode is invalid.", nameof(xElement)); composite.Mode = mode; + } return composite; } @@ -365,21 +419,25 @@ public static CompositeFunction FromXElement(XElement xElement) private int SelectMixtureChild(double u, out double remainder) { double cumulative = 0d; + int lastPositive = -1; for (int i = 0; i < _weights.Length; i++) { if (_weights[i] <= 0d) continue; - if (u <= cumulative + _weights[i] || i == _weights.Length - 1) + lastPositive = i; + double next = cumulative + _weights[i]; + if (u <= next) { remainder = (u - cumulative) / _weights[i]; if (remainder < 0d) remainder = 0d; if (remainder > 1d) remainder = 1d; return i; } - cumulative += _weights[i]; + cumulative = next; } - // All-zero weights cannot validate; fall back to the last child defensively. - remainder = u; - return _weights.Length - 1; + if (lastPositive < 0) + throw new InvalidOperationException("The composite has no positive child weight."); + remainder = 1d; + return lastPositive; } /// @@ -393,11 +451,23 @@ private int SelectMixtureChild(double u, out double remainder) private double EvaluateChildAt(int index, double confidenceLevel, double x) { var child = _functions[index]; - double restore = child.ConfidenceLevel; - child.ConfidenceLevel = confidenceLevel; - double value = child.Function(x); - child.ConfidenceLevel = restore; - return value; + if (child.IsDeterministic) + return child.Function(x); + + object syncRoot = ChildLocks.GetValue(child, key => new object()); + lock (syncRoot) + { + double restore = child.ConfidenceLevel; + try + { + child.ConfidenceLevel = confidenceLevel; + return child.Function(x); + } + finally + { + child.ConfidenceLevel = restore; + } + } } /// @@ -411,11 +481,23 @@ private double EvaluateChildAt(int index, double confidenceLevel, double x) private double InvertChildAt(int index, double confidenceLevel, double y) { var child = _functions[index]; - double restore = child.ConfidenceLevel; - child.ConfidenceLevel = confidenceLevel; - double value = child.InverseFunction(y); - child.ConfidenceLevel = restore; - return value; + if (child.IsDeterministic) + return child.InverseFunction(y); + + object syncRoot = ChildLocks.GetValue(child, key => new object()); + lock (syncRoot) + { + double restore = child.ConfidenceLevel; + try + { + child.ConfidenceLevel = confidenceLevel; + return child.InverseFunction(y); + } + finally + { + child.ConfidenceLevel = restore; + } + } } /// diff --git a/Numerics/Functions/EnsembleFunction.cs b/Numerics/Functions/EnsembleFunction.cs index 74a41fc6..d15e5499 100644 --- a/Numerics/Functions/EnsembleFunction.cs +++ b/Numerics/Functions/EnsembleFunction.cs @@ -1,6 +1,7 @@ -using Numerics.Mathematics.Optimization; +using Numerics.Mathematics.Optimization; using System; using System.Collections.Generic; +using System.Globalization; using System.Xml.Linq; namespace Numerics.Functions @@ -15,15 +16,10 @@ namespace Numerics.Functions /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil /// /// - /// This is how an imported fitted function (e.g., a BestFit rating curve) carries knowledge - /// uncertainty into a simulation engine. Both sampling surfaces are pure: every call - /// returns a brand-new clone of the template configured with the selected parameter set, so - /// concurrent realizations never share mutable state — the thread-safe alternative to - /// mutating one instance's ConfidenceLevel/SetParameters inside a - /// Parallel.For hot loop. The template is captured as an immutable serialized - /// snapshot at construction (each clone re-materializes through - /// ), so later edits to - /// the original instance never leak into samples. + /// Every sample is a new clone of the template configured with one parameter set, so + /// concurrent samples do not share mutable function state. The constructor stores a + /// serialized template snapshot, and each sample is reconstructed through + /// . /// /// /// Percentile sampling maps u ∈ [0, 1] onto the index ladder as @@ -48,37 +44,29 @@ public EnsembleFunction(IUnivariateFunction template, IList parame if (parameterSets == null) throw new ArgumentNullException(nameof(parameterSets)); if (parameterSets.Count == 0) throw new ArgumentException("At least one parameter set is required.", nameof(parameterSets)); + _templateXml = SerializeTemplate(template).ToString(SaveOptions.DisableFormatting); + int expectedLength = template.NumberOfParameters; _parameterSets = new ParameterSet[parameterSets.Count]; for (int i = 0; i < parameterSets.Count; i++) { if (parameterSets[i].Values == null || parameterSets[i].Values.Length != expectedLength) throw new ArgumentException("Parameter set " + i + " must carry exactly " + expectedLength + " values (one per template parameter).", nameof(parameterSets)); - _parameterSets[i] = parameterSets[i]; + for (int j = 0; j < parameterSets[i].Values.Length; j++) + { + if (!Tools.IsFinite(parameterSets[i].Values[j])) + throw new ArgumentException("Parameter set " + i + " contains a non-finite value.", nameof(parameterSets)); + } + + var validationFunction = UnivariateFunctionFactory.CreateFromXElement(XElement.Parse(_templateXml)); + var validationError = validationFunction.ValidateParameters(parameterSets[i].Values, false); + if (validationError != null) + throw new ArgumentException("Parameter set " + i + " is invalid for the template function.", nameof(parameterSets), validationError); + _parameterSets[i] = parameterSets[i].Clone(); } - // The immutable template snapshot: parsing a private string per sample guarantees - // clones never share XML tree state across threads. - _templateXml = SerializeTemplate(template).ToString(SaveOptions.DisableFormatting); } - /// - /// Construct a new ensemble function from a serialized template element and its - /// posterior parameter sets (the deserialization path). - /// - /// The template's serialized form. - /// The posterior parameter sets. - /// Thrown when either argument is null. - /// Thrown when no parameter sets are supplied. - private EnsembleFunction(XElement templateElement, IList parameterSets) - { - if (templateElement == null) throw new ArgumentNullException(nameof(templateElement)); - if (parameterSets == null) throw new ArgumentNullException(nameof(parameterSets)); - if (parameterSets.Count == 0) throw new ArgumentException("At least one parameter set is required.", nameof(parameterSets)); - _parameterSets = new ParameterSet[parameterSets.Count]; - for (int i = 0; i < parameterSets.Count; i++) _parameterSets[i] = parameterSets[i]; - _templateXml = templateElement.ToString(SaveOptions.DisableFormatting); - } private readonly string _templateXml; private readonly ParameterSet[] _parameterSets; @@ -91,7 +79,15 @@ private EnsembleFunction(XElement templateElement, IList parameter /// /// The posterior parameter sets. /// - public IReadOnlyList ParameterSets => _parameterSets; + public IReadOnlyList ParameterSets + { + get + { + var copy = new ParameterSet[_parameterSets.Length]; + for (int i = 0; i < copy.Length; i++) copy[i] = _parameterSets[i].Clone(); + return Array.AsReadOnly(copy); + } + } /// /// Samples the posterior by index: a fresh template clone configured with the indexed @@ -106,6 +102,8 @@ public IUnivariateFunction Sample(int index) throw new ArgumentOutOfRangeException(nameof(index), "The posterior index must be within [0, Count)."); var clone = UnivariateFunctionFactory.CreateFromXElement(XElement.Parse(_templateXml)); clone.SetParameters(_parameterSets[index].Values); + if (!clone.ParametersValid) + clone.ValidateParameters(_parameterSets[index].Values, true); return clone; } @@ -118,7 +116,7 @@ public IUnivariateFunction Sample(int index) /// Thrown when is outside [0, 1]. public IUnivariateFunction Sample(double percentile) { - if (percentile < 0d || percentile > 1d) + if (!Tools.IsFinite(percentile) || percentile < 0d || percentile > 1d) throw new ArgumentOutOfRangeException(nameof(percentile), "The percentile must be between 0 and 1."); int index = (int)Math.Floor(percentile * _parameterSets.Length); if (index > _parameterSets.Length - 1) index = _parameterSets.Length - 1; @@ -167,12 +165,53 @@ public static EnsembleFunction FromXElement(XElement xElement) if (setsElement != null) { foreach (var child in setsElement.Elements()) - sets.Add(new ParameterSet(child)); + sets.Add(ParseParameterSet(child)); } if (sets.Count == 0) throw new ArgumentException("The serialized ensemble function carries no parameter sets.", nameof(xElement)); - return new EnsembleFunction(template, sets); + return new EnsembleFunction(UnivariateFunctionFactory.CreateFromXElement(template), sets); + } + + /// + /// Deserializes and validates one ensemble parameter set. + /// + /// The serialized parameter set. + /// The parsed parameter set. + /// Thrown when a value is missing, malformed, or non-finite. + private static ParameterSet ParseParameterSet(XElement element) + { + if (element == null) throw new ArgumentNullException(nameof(element)); + string? valuesText = element.Attribute(nameof(ParameterSet.Values))?.Value; + if (string.IsNullOrWhiteSpace(valuesText)) + throw new ArgumentException("The serialized parameter set is missing its values.", nameof(element)); + + string[] tokens = valuesText!.Split('|'); + var values = new double[tokens.Length]; + for (int i = 0; i < tokens.Length; i++) + { + if (!double.TryParse(tokens[i], NumberStyles.Any, CultureInfo.InvariantCulture, out values[i]) + || !Tools.IsFinite(values[i])) + { + throw new ArgumentException("The serialized parameter set contains an invalid value.", nameof(element)); + } + } + + double fitness = 0d; + string? fitnessText = element.Attribute(nameof(ParameterSet.Fitness))?.Value; + if (fitnessText != null + && (!double.TryParse(fitnessText, NumberStyles.Any, CultureInfo.InvariantCulture, out fitness) + || !Tools.IsFinite(fitness))) + throw new ArgumentException("The serialized parameter-set fitness is invalid.", nameof(element)); + + double weight = 0d; + string? weightText = element.Attribute(nameof(ParameterSet.Weight))?.Value; + if (weightText != null + && (!double.TryParse(weightText, NumberStyles.Any, CultureInfo.InvariantCulture, out weight) + || !Tools.IsFinite(weight))) + throw new ArgumentException("The serialized parameter-set weight is invalid.", nameof(element)); + + return new ParameterSet(values, fitness, weight); } /// diff --git a/Numerics/Functions/SegmentedPowerFunction.cs b/Numerics/Functions/SegmentedPowerFunction.cs index 6f43c001..ab4db081 100644 --- a/Numerics/Functions/SegmentedPowerFunction.cs +++ b/Numerics/Functions/SegmentedPowerFunction.cs @@ -1,4 +1,4 @@ -using Numerics.Distributions; +using Numerics.Distributions; using Numerics.Mathematics.RootFinding; using System; using System.Collections.Generic; @@ -8,7 +8,7 @@ namespace Numerics.Functions { /// - /// A segmented power function in the BaRatin matrix-of-controls ADDITION mode: + /// A segmented power function in the BaRatin matrix-of-controls addition mode: /// Q(h) = Σₖ 10^(log₁₀αₖ) · (h − hₖ)^βₖ · 𝟙{h > hₖ}, with a log₁₀-space Gaussian residual σ. /// /// @@ -17,9 +17,8 @@ namespace Numerics.Functions /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil /// /// - /// The parameter-vector layout is exactly the RMC-BestFit rating-curve layout - /// (RMC.BestFit/Models/RatingCurve/RatingCurve.cs), so a fitted posterior parameter - /// set applies directly through : + /// Parameter sets apply directly through using + /// the layout: /// [h₁, log₁₀α₁, β₁, h₂, log₁₀α₂, β₂, …, σ] with length 3·segments + 1. Under /// addition mode the BaRatin continuity derivation collapses each control's offset to its /// activation stage (b_k = κ_k), the breakpoints must be strictly ordered @@ -28,10 +27,10 @@ namespace Numerics.Functions /// with α = 10^(log₁₀α₁), β = β₁, ξ = h₁. /// /// - /// The residual is log₁₀-space, matching the BestFit stochastic prediction: with a + /// The residual is Gaussian in log₁₀ space: with a /// confidence level u, Q(h) is multiplied by 10^(z) where z ~ N(0, σ) evaluated at u. The /// numeric uses monotone bracketing with Brent's - /// method — the addition-mode sum is strictly increasing above h₁ for non-negative + /// method — the addition-mode sum is strictly increasing above h₁ for positive /// exponents. /// /// @@ -77,7 +76,7 @@ public SegmentedPowerFunction(int numberOfSegments) } /// - /// Construct a new segmented power function directly from a BestFit-layout parameter + /// Construct a new segmented power function directly from a parameter /// vector. The segment count is inferred from the vector length (3·segments + 1), and /// the function is stochastic (σ is the last entry). /// @@ -92,6 +91,7 @@ public SegmentedPowerFunction(IList parameters) _numberOfSegments = (parameters.Count - 1) / 3; _parameters = new double[parameters.Count]; IsDeterministic = false; + ValidateParameters(parameters, true); SetParameters(parameters); } @@ -125,7 +125,18 @@ public double Minimum } /// - public double Maximum { get; set; } = double.MaxValue; + public double Maximum + { + get { return _maximum; } + set + { + if (!Tools.IsFinite(value) || value <= Minimum) + throw new ArgumentOutOfRangeException(nameof(Maximum), "Maximum must be finite and greater than the first breakpoint."); + _maximum = value; + } + } + + private double _maximum = double.MaxValue; /// public double[] MinimumOfParameters @@ -137,7 +148,7 @@ public double[] MinimumOfParameters { result[3 * k] = double.MinValue; result[3 * k + 1] = double.MinValue; - result[3 * k + 2] = 0d; + result[3 * k + 2] = Tools.DoubleMachineEpsilon; } result[result.Length - 1] = 0d; return result; @@ -200,10 +211,11 @@ public double GetBeta(int segmentOneBased) /// public void SetParameters(IList parameters) { - // Validate parameters - _parametersValid = ValidateParameters(parameters, false) is null; - // Set parameters - for (int i = 0; i < _parameters.Length && i < parameters.Count; i++) + var validationError = ValidateParameters(parameters, false); + if (validationError != null) return; + _parametersValid = true; + + for (int i = 0; i < _parameters.Length; i++) _parameters[i] = parameters[i]; _normal.SetParameters(0d, _parameters[_parameters.Length - 1]); } @@ -228,9 +240,9 @@ public void SetParameters(IList parameters) } for (int k = 0; k < (parameters.Count - 1) / 3; k++) { - if (parameters[3 * k + 2] < 0) + if (parameters[3 * k + 2] <= 0) { - var error = new ArgumentOutOfRangeException(nameof(parameters), "Exponents must be non-negative for a monotone rating."); + var error = new ArgumentOutOfRangeException(nameof(parameters), "Exponents must be greater than zero for a strictly increasing function."); if (throwException) throw error; return error; } @@ -241,6 +253,12 @@ public void SetParameters(IList parameters) return error; } } + if (parameters[0] >= Maximum) + { + var error = new ArgumentOutOfRangeException(nameof(parameters), "The first breakpoint must be less than Maximum."); + if (throwException) throw error; + return error; + } if (IsDeterministic == false && parameters[parameters.Count - 1] <= 0) { var error = new ArgumentOutOfRangeException(nameof(Sigma), "Standard error must be greater than zero."); @@ -264,7 +282,7 @@ public double Function(double x) if (IsDeterministic == true || ConfidenceLevel < 0 || ConfidenceLevel > 1) return q; - // Log₁₀-space residual, matching the BestFit stochastic prediction. + // Apply the Gaussian residual in log₁₀ space. return q * Math.Pow(10d, _normal.InverseCDF(ConfidenceLevel)); } @@ -276,6 +294,10 @@ public double InverseFunction(double y) ValidateParameters(_parameters, true); // Fold the residual out first: the stochastic curve is the deterministic curve + if (double.IsNaN(y) || double.IsNegativeInfinity(y)) + throw new ArgumentOutOfRangeException(nameof(y), "The inverse value must not be NaN or negative infinity."); + if (double.IsPositiveInfinity(y)) return Maximum; + // scaled by 10^z, so the inverse divides before the monotone root find. if (IsDeterministic == false && ConfidenceLevel >= 0 && ConfidenceLevel <= 1) y /= Math.Pow(10d, _normal.InverseCDF(ConfidenceLevel)); @@ -304,7 +326,7 @@ public double InverseFunction(double y) /// /// Serializes the function's configuration to an XElement: the segment count, the - /// BestFit-layout parameter vector, the deterministic flag, and the upper support bound. + /// parameter vector, the deterministic flag, and the upper support bound. /// derives from h₁ and is runtime /// sampling state — neither is serialized. /// @@ -338,6 +360,8 @@ public static SegmentedPowerFunction FromXElement(XElement xElement) throw new ArgumentException("The serialized segmented power function is missing its parameter vector.", nameof(xElement)); string[] tokens = text!.Split('|'); + if (tokens.Length < 4 || (tokens.Length - 1) % 3 != 0) + throw new ArgumentException("The serialized parameter vector has an invalid length.", nameof(xElement)); var parameters = new double[tokens.Length]; for (int i = 0; i < tokens.Length; i++) { @@ -345,18 +369,44 @@ public static SegmentedPowerFunction FromXElement(XElement xElement) throw new ArgumentException("The serialized segmented power function carries an unparseable parameter value.", nameof(xElement)); } - var function = new SegmentedPowerFunction(parameters); - if (bool.TryParse(xElement.Attribute(nameof(IsDeterministic))?.Value, out bool isDeterministic)) - function.IsDeterministic = isDeterministic; - if (double.TryParse(xElement.Attribute(nameof(Maximum))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double maximum)) - function.Maximum = maximum; + int inferredSegments = (parameters.Length - 1) / 3; + string? segmentText = xElement.Attribute(nameof(NumberOfSegments))?.Value; + if (segmentText != null + && (!int.TryParse(segmentText, NumberStyles.Integer, CultureInfo.InvariantCulture, out int serializedSegments) + || serializedSegments != inferredSegments)) + { + throw new ArgumentException("The serialized segment count does not match the parameter vector.", nameof(xElement)); + } + + bool isDeterministic = false; + string? deterministicText = xElement.Attribute(nameof(IsDeterministic))?.Value; + if (deterministicText != null && !bool.TryParse(deterministicText, out isDeterministic)) + throw new ArgumentException("The serialized deterministic flag is invalid.", nameof(xElement)); + + double? maximum = null; + string? maximumText = xElement.Attribute(nameof(Maximum))?.Value; + if (maximumText != null) + { + if (!double.TryParse(maximumText, NumberStyles.Any, CultureInfo.InvariantCulture, out double parsedMaximum)) + throw new ArgumentException("The serialized maximum is invalid.", nameof(xElement)); + maximum = parsedMaximum; + } + + var function = new SegmentedPowerFunction(inferredSegments) + { + IsDeterministic = isDeterministic + }; + function.SetParameters(parameters); + if (!function.ParametersValid) + function.ValidateParameters(parameters, true); + if (maximum.HasValue) + function.Maximum = maximum.Value; return function; } /// /// The deterministic addition-mode discharge: the sum of the active controls' power - /// laws, zero at and below the main-channel cease-to-flow stage h₁ (the BestFit - /// Predict body). + /// laws, zero at and below the main-channel cease-to-flow stage h₁. /// /// The stage. /// The deterministic discharge. diff --git a/Numerics/Mathematics/Integration/AdaptiveGuassKronrod.cs b/Numerics/Mathematics/Integration/AdaptiveGuassKronrod.cs index b485d367..01330715 100644 --- a/Numerics/Mathematics/Integration/AdaptiveGuassKronrod.cs +++ b/Numerics/Mathematics/Integration/AdaptiveGuassKronrod.cs @@ -152,22 +152,15 @@ private static double[] BuildCaptureWeights() public Func Function { get; } /// - /// An optional recorder invoked as (x, weight, f(x)) for every node of every ACCEPTED - /// interval — the intervals of the final composite rule. Null (the default) records - /// nothing and leaves the integration path unchanged with zero overhead. + /// Gets or sets a callback invoked as (x, weight, f(x)) for quadrature nodes belonging + /// to accepted intervals. A null callback disables recording. /// /// - /// A naive per-evaluation weight hand-off double-counts under adaptivity: a rejected - /// interval's 21 evaluations are superseded by its children's, so their weights must - /// never carry measure. The recorder therefore fires only when an interval is accepted - /// (tolerance reached, or the depth/evaluation caps force acceptance), with each node's - /// Kronrod weight scaled by the interval half-length. Two identities follow: the weights - /// of one accepted interval sum to exactly its width, so the recorded weights sum to the - /// integration domain's width (any double count would overshoot it); and Σ weight·f(x) - /// over all recorded nodes reproduces to floating-point - /// reassociation (the composite rule IS that sum). Recorded abscissas are strictly - /// interior to their interval (the G10K21 property), so adjacent intervals never repeat - /// a node. + /// Evaluations from subdivided parent intervals are omitted because their children + /// replace them in the composite rule. Each reported Kronrod weight includes the + /// interval half-length, so the weights sum to the integration-domain width and their + /// weighted function values reproduce up to floating-point + /// reassociation. The callback is snapshotted when integration begins. /// public Action? Recorder { get; set; } @@ -203,14 +196,15 @@ public override void Integrate() StandardError = 0; ClearResults(); Validate(); + Action? recorder = Recorder; try { // Initial evaluation using Gauss-Kronrod rule on the whole interval - var (kronrodResult, gaussResult, nodes, values) = EvaluateGaussKronrod(a, b, 0); + var (kronrodResult, gaussResult, nodes, values) = EvaluateGaussKronrod(a, b, 0, recorder); // Recursively sub-divide - Result = AdaptiveGK(Function, a, b, MaxDepth, kronrodResult, gaussResult, a, b, nodes, values, 0); + Result = AdaptiveGK(Function, a, b, MaxDepth, kronrodResult, gaussResult, a, b, nodes, values, 0, recorder); // Standard error calculated after recursion completes StandardError = Math.Sqrt(_squaredError); @@ -241,6 +235,7 @@ public void Integrate(List bins) StandardError = 0; ClearResults(); Validate(); + Action? recorder = Recorder; try { @@ -250,10 +245,10 @@ public void Integrate(List bins) // Initial evaluation using Gauss-Kronrod rule on the bin interval double binA = bins[i].LowerBound; double binB = bins[i].UpperBound; - var (kronrodResult, gaussResult, nodes, values) = EvaluateGaussKronrod(binA, binB, 0); + var (kronrodResult, gaussResult, nodes, values) = EvaluateGaussKronrod(binA, binB, 0, recorder); // Recursively sub-divide - mu += AdaptiveGK(Function, binA, binB, MaxDepth, kronrodResult, gaussResult, binA, binB, nodes, values, 0); + mu += AdaptiveGK(Function, binA, binB, MaxDepth, kronrodResult, gaussResult, binA, binB, nodes, values, 0, recorder); } // Final result and standard error @@ -280,22 +275,23 @@ public void Integrate(List bins) /// Evaluates the Gauss-Kronrod G10K21 rule over the interval [a, b]. When a /// is attached, the 21 node abscissas and function values are /// also captured (in the fixed capture order matching the static weight layout) so the - /// interval can flush them if it is later accepted; with no recorder the capture is + /// interval can report them if it is accepted; with no recorder the capture is /// skipped entirely and the evaluation path is unchanged. /// /// The lower bound of integration. /// The upper bound of integration. - /// The capture-buffer slot; distinct for every interval alive at once. + /// The capture-buffer slot. + /// The recorder snapshot for this integration. /// A tuple containing (Kronrod estimate, Gauss estimate, captured nodes, captured values). - private (double kronrod, double gauss, double[]? nodes, double[]? values) EvaluateGaussKronrod(double a, double b, int slot) + private (double kronrod, double gauss, double[]? nodes, double[]? values) EvaluateGaussKronrod(double a, double b, int slot, Action? recorder) { double center = 0.5 * (a + b); double halfLength = 0.5 * (b - a); double resultGauss = 0.0; double resultKronrod = 0.0; - double[]? nodes = Recorder != null ? RentCapture(ref _nodePool, slot) : null; - double[]? values = Recorder != null ? RentCapture(ref _valuePool, slot) : null; + double[]? nodes = recorder != null ? RentCapture(ref _nodePool, slot) : null; + double[]? values = recorder != null ? RentCapture(ref _valuePool, slot) : null; // Evaluate at center point (x = 0) double f0 = Function(center); @@ -354,14 +350,16 @@ public void Integrate(List bins) /// The original upper bound of the integral. /// The interval's captured node abscissas (null when no recorder is attached). /// The interval's captured function values (null when no recorder is attached). - /// The recursion level, which selects this interval's children's capture slots. + /// The recursion level used to select capture slots. + /// The recorder snapshot for this integration. /// /// An evaluation of the integral using adaptive Gauss-Kronrod with error less than the specified tolerance. /// This is accomplished by subdividing the interval until the error between the Gauss and Kronrod estimates /// is sufficiently small. /// private double AdaptiveGK(Func f, double a, double b, int depth, - double kronrodWhole, double gaussWhole, double a0, double b0, double[]? nodes, double[]? values, int level) + double kronrodWhole, double gaussWhole, double a0, double b0, double[]? nodes, double[]? values, int level, + Action? recorder) { // Error estimate: difference between Kronrod and Gauss results double error = Math.Abs(kronrodWhole - gaussWhole); @@ -380,16 +378,14 @@ private double AdaptiveGK(Func f, double a, double b, int depth, // Convergence is reached _squaredError += error * error; // Accumulate squared errors - // The interval is ACCEPTED: it is part of the final composite rule, so its - // nodes carry quadrature measure — flush them to the recorder with the - // half-length-scaled Kronrod weights. Rejected (subdivided) intervals never - // reach this point, so their superseded evaluations never carry weight. - if (Recorder != null && nodes != null && values != null) + // Report only nodes that belong to the final composite rule, using weights + // scaled by the accepted interval's half-length. + if (recorder != null && nodes != null && values != null) { double halfLength = 0.5 * (b - a); for (int j = 0; j < 21; j++) { - Recorder(nodes[j], wCapture[j] * halfLength, values[j]); + recorder(nodes[j], wCapture[j] * halfLength, values[j]); } } return kronrodWhole; // Return the more accurate Kronrod estimate @@ -403,14 +399,14 @@ private double AdaptiveGK(Func f, double a, double b, int depth, // Both halves are evaluated before either recurses, so they need distinct capture // slots; their own children take the slots of the next level, which are free by // the time each subtree runs. - var (kronrodLeft, gaussLeft, nodesLeft, valuesLeft) = EvaluateGaussKronrod(a, m, 2 * level + 1); + var (kronrodLeft, gaussLeft, nodesLeft, valuesLeft) = EvaluateGaussKronrod(a, m, 2 * level + 1, recorder); // Evaluate Gauss-Kronrod on right half - var (kronrodRight, gaussRight, nodesRight, valuesRight) = EvaluateGaussKronrod(m, b, 2 * level + 2); + var (kronrodRight, gaussRight, nodesRight, valuesRight) = EvaluateGaussKronrod(m, b, 2 * level + 2, recorder); // Recursively subdivide the intervals and accumulate results - var leftResult = AdaptiveGK(f, a, m, depth - 1, kronrodLeft, gaussLeft, a0, b0, nodesLeft, valuesLeft, level + 1); - var rightResult = AdaptiveGK(f, m, b, depth - 1, kronrodRight, gaussRight, a0, b0, nodesRight, valuesRight, level + 1); + var leftResult = AdaptiveGK(f, a, m, depth - 1, kronrodLeft, gaussLeft, a0, b0, nodesLeft, valuesLeft, level + 1, recorder); + var rightResult = AdaptiveGK(f, m, b, depth - 1, kronrodRight, gaussRight, a0, b0, nodesRight, valuesRight, level + 1, recorder); return leftResult + rightResult; } diff --git a/Numerics/Mathematics/Integration/Vegas.cs b/Numerics/Mathematics/Integration/Vegas.cs index dd1870f0..8c4d694e 100644 --- a/Numerics/Mathematics/Integration/Vegas.cs +++ b/Numerics/Mathematics/Integration/Vegas.cs @@ -6,8 +6,8 @@ namespace Numerics.Mathematics.Integration { /// - /// A class for adaptive Monte Carlo integration for multidimensional integration. - /// Enhanced with Power Transform for rare event simulation. + /// Adaptive multidimensional Monte Carlo integration using the VEGAS algorithm, with an + /// optional probability-space power transform for tail-focused sampling. /// /// /// @@ -20,10 +20,8 @@ namespace Numerics.Mathematics.Integration /// in those areas of the integrand that make the greatest contribution. /// /// - /// Power Transform Enhancement: - /// The Power Transform (γ parameter) enables efficient sampling of rare tail events without changing the integrand. - /// For rare events (p < 1e-4), set TailFocusParameter > 1 to concentrate samples in tail regions. - /// This maintains numerical stability in high dimensions (unlike z-space methods). + /// The power transform p' = 1 - (1-p)^γ concentrates samples near the upper probability + /// boundary when γ is greater than one. The integration weights include its Jacobian. /// /// References: /// @@ -86,6 +84,7 @@ public Vegas(Func function, int dimensions, IList - /// Power transform parameter for tail-focused rare event sampling. - /// Default = 1.0 (standard uniform sampling, backward compatible). - /// Set γ > 1 to focus sampling on upper tail (p → 1) for rare events. - /// Recommended values: γ=2 (moderate focus), γ=4 (strong focus), γ=10 (very strong focus for p < 1e-6). + /// Gets or sets the positive finite power-transform exponent. A value of one applies no + /// transform; values greater than one concentrate samples near the upper probability boundary. /// + /// Thrown when the value is not finite and positive. /// /// The power transform uses p' = 1 - (1-p)^γ to concentrate samples in the upper tail. /// Unlike z-space transforms, this maintains numerical stability in high dimensions. /// Weights are corrected by Jacobian: dp'/dp = γ(1-p)^(γ-1), which stays O(1). /// - public double TailFocusParameter { get; set; } = 1.0; + public double TailFocusParameter + { + get { return _tailFocusParameter; } + set + { + if (!Tools.IsFinite(value) || value <= 0d) + throw new ArgumentOutOfRangeException(nameof(TailFocusParameter), "The tail-focus parameter must be finite and positive."); + _tailFocusParameter = value; + } + } /// /// Gets the stratification grid boundaries. @@ -250,20 +257,19 @@ private void InitializeParameters() /// /// Apply power transform to probability for tail-focused sampling. /// p' = 1 - (1-p)^γ concentrates samples in upper tail when γ > 1. - /// When γ = 1, this is identity transform (backward compatible). + /// When γ = 1, the transform is the identity. /// private double ApplyPowerTransform(double p) { double gamma = TailFocusParameter; - // Identity transform when γ = 1 (standard Vegas behavior) + // A unit exponent leaves the probability unchanged. if (Math.Abs(gamma - 1.0) < 1e-10) { return p; } - // Power transform for upper tail focus - // Maps [0,1] → [0,1] but concentrates samples near 1 + // Map [0, 1] to itself while concentrating samples near one. return 1.0 - Math.Pow(1.0 - p, gamma); } @@ -276,14 +282,13 @@ private double PowerTransformJacobian(double p) { double gamma = TailFocusParameter; - // Identity Jacobian when γ = 1 + // The identity transform has a unit Jacobian. if (Math.Abs(gamma - 1.0) < 1e-10) { return 1.0; } - // Jacobian: dp'/dp = γ(1-p)^(γ-1) - // Clamp (1-p) to avoid numerical issues + // Clamp 1-p away from zero before evaluating the Jacobian. double oneMinusP = Math.Max(1.0 - p, 1e-15); return gamma * Math.Pow(oneMinusP, gamma - 1.0); } @@ -293,18 +298,23 @@ private double PowerTransformJacobian(double p) /// Automatically sets TailFocusParameter based on target probability. /// /// Target rare event probability (e.g., 1e-6) + /// + /// is not finite or is outside the open interval (0, 1). + /// public void ConfigureForRareEvents(double targetProbability) { - // Choose γ so that target probability appears in ~5% of transformed samples - // Solving: (1 - 0.95)^γ ≈ targetProbability - // γ ≈ ln(targetProbability) / ln(0.05) + if (!Tools.IsFinite(targetProbability) || targetProbability <= 0d || targetProbability >= 1d) + throw new ArgumentOutOfRangeException(nameof(targetProbability), "The target probability must be finite and between zero and one."); + + // Choose γ so the target tail occupies approximately five percent of transformed draws: + // γ = ln(targetProbability) / ln(0.05). double gamma = Math.Log(targetProbability) / Math.Log(0.05); - gamma = Math.Max(1.0, Math.Min(gamma, 20.0)); // Clamp to [1, 20] + gamma = Math.Max(1.0, Math.Min(gamma, 20.0)); this.TailFocusParameter = gamma; this.NumberOfBins = Math.Max(100, this.NumberOfBins); - this.Alpha = 1.8; // More aggressive grid adaptation + this.Alpha = 1.8; } /// diff --git a/Numerics/Mathematics/Special Functions/Factorial.cs b/Numerics/Mathematics/Special Functions/Factorial.cs index 5e4b39c5..3c059626 100644 --- a/Numerics/Mathematics/Special Functions/Factorial.cs +++ b/Numerics/Mathematics/Special Functions/Factorial.cs @@ -184,18 +184,37 @@ public static IEnumerable FindCombinations(int m, int n) } /// - /// Advances a k-combination over [0, n) to its lexicographic successor, in place. + /// Advances a valid k-combination over [0, n) to its lexicographic successor in place. /// - /// The current strictly increasing index tuple; advanced in place. - /// The overall count. - /// False when the combination was the last of its size. - /// Thrown when the combination is null. + /// The current strictly increasing index tuple. + /// The overall item count. + /// when the tuple is the last combination of its size. + /// Thrown when is null. + /// Thrown when is negative. + /// Thrown when the tuple is empty, too long, out of range, or not strictly increasing. public static bool NextCombination(int[] combination, int n) { if (combination == null) throw new ArgumentNullException(nameof(combination)); - int k = combination.Length; - if (k == 0 || k > n) return false; + if (n < 0) throw new ArgumentOutOfRangeException(nameof(n), "The item count must be non-negative."); + if (combination.Length == 0 || combination.Length > n) + throw new ArgumentException("The combination length must be between one and the item count.", nameof(combination)); + + int previous = -1; + for (int i = 0; i < combination.Length; i++) + { + if (combination[i] <= previous || combination[i] >= n) + throw new ArgumentException("Combination indexes must be strictly increasing and within [0, n).", nameof(combination)); + previous = combination[i]; + } + return NextCombinationUnchecked(combination, n); + } + /// + /// Advances a combination that has already been validated. + /// + internal static bool NextCombinationUnchecked(int[] combination, int n) + { + int k = combination.Length; int i = k - 1; while (i >= 0 && combination[i] == n - k + i) i--; if (i < 0) return false; @@ -203,7 +222,6 @@ public static bool NextCombination(int[] combination, int n) for (int j = i + 1; j < k; j++) combination[j] = combination[j - 1] + 1; return true; } - /// /// Enumerates every non-empty subset of n items as an index tuple, in the order /// lays out its rows — subset size ascending, then @@ -230,7 +248,7 @@ public static IEnumerable AllCombinationsLazy(int n) { yield return combination; } - while (NextCombination(combination, n)); + while (NextCombinationUnchecked(combination, n)); } } diff --git a/Numerics/Sampling/MCMC/NUTS.cs b/Numerics/Sampling/MCMC/NUTS.cs index 6ffc2d2d..f509d36c 100644 --- a/Numerics/Sampling/MCMC/NUTS.cs +++ b/Numerics/Sampling/MCMC/NUTS.cs @@ -1,4 +1,4 @@ -using Numerics.Distributions; +using Numerics.Distributions; using Numerics.Mathematics; using Numerics.Mathematics.LinearAlgebra; using Numerics.Mathematics.Optimization; @@ -29,18 +29,14 @@ namespace Numerics.Sampling.MCMC /// negative Hamiltonian. /// /// - /// During the warmup phase, the leapfrog step size is automatically adapted using dual averaging + /// During warmup, the leapfrog step size is automatically adapted using dual averaging /// to achieve a target Metropolis acceptance probability of approximately 80%. A diagonal mass matrix /// is estimated during warmup using Stan-style windowed adaptation (Welford's online algorithm) to /// precondition the Hamiltonian dynamics for multi-scale posteriors. /// /// - /// Key applications include: - /// - /// Bayesian flood frequency analysis in RMC-BestFit where manual HMC tuning is impractical. - /// Bayesian parameter estimation for complex hydrologic models in RFA. - /// Posterior inference for mixture models and hierarchical models in TotalRisk. - /// + /// NUTS is suitable for Bayesian parameter estimation in models where manual tuning of HMC + /// trajectory lengths is impractical, including hierarchical and mixture models. /// /// /// References: @@ -361,11 +357,8 @@ protected override void InitializeCustomSettings() _welfordCount[i] = 0; } - // Compute adaptation window boundaries following Stan exactly. - // Stan defaults: init_buffer=75, term_buffer=50, base_window=25 - // Phase 1 (init_buffer): step size adaptation only, identity mass matrix - // Phase 2 (slow adaptation): mass matrix + step size in doubling windows - // Phase 3 (term_buffer): final step size tuning with fixed mass matrix + // Use Stan's initial buffer, doubling mass-matrix windows, and terminal buffer. + // Default lengths are init_buffer=75, base_window=25, and term_buffer=50. int totalWarmup = WarmupIterations * ThinningInterval; // Stan default window sizes, scaled if warmup is too short @@ -578,7 +571,7 @@ protected override ParameterSet ChainIteration(int index, ParameterSet state) double eps = _chainStepSizes[index]; int D = NumberOfParameters; - // Step 1: Sample momentum from N(0, M) using per-chain mass matrix + // Sample momentum from N(0, M) using the per-chain mass matrix. var phi = new Vector(D); for (int i = 0; i < D; i++) phi[i] = Math.Sqrt(_massMatrix[index][i]) * Normal.StandardZ(_chainPRNGs[index].NextDouble()); @@ -586,7 +579,7 @@ protected override ParameterSet ChainIteration(int index, ParameterSet state) // Compute initial Hamiltonian using per-chain inverse mass matrix double H0 = -state.Fitness + 0.5 * DiagonalQuadraticFormVec(phi, _inverseMassMatrix[index]); - // Step 2: Initialize tree + // Initialize the trajectory tree. var theta = new Vector(state.Values); var thetaMinus = theta.Clone(); var thetaPlus = theta.Clone(); @@ -604,7 +597,7 @@ protected override ParameterSet ChainIteration(int index, ParameterSet state) int trajectoryDepth = 0; bool trajectoryDivergent = false; - // Step 3: Build tree by doubling until U-turn or max depth + // Double the trajectory tree until a U-turn or the maximum depth. while (depth < MaxTreeDepth) { // Choose a random direction @@ -656,7 +649,7 @@ protected override ParameterSet ChainIteration(int index, ParameterSet state) } double averageAcceptanceProbability = numAlpha > 0 ? sumAlpha / numAlpha : 0d; - // Step 4: Warmup adaptation (step size + mass matrix) + // Adapt the step size and mass matrix during warmup. if (sampleNum <= warmupSteps) { // Always do dual averaging step size adaptation during warmup @@ -666,7 +659,7 @@ protected override ParameterSet ChainIteration(int index, ParameterSet state) : DELTA_TARGET; DualAveragingUpdate(index, adaptationAcceptanceProbability); - // Accumulate Welford statistics during mass matrix adaptation windows (Phase 2) + // Accumulate Welford statistics during mass-matrix adaptation windows. if (AdaptMassMatrix && sampleNum > _initBuffer && sampleNum <= warmupSteps - _termBuffer) { AccumulateWelfordStatistics(index, candidate.Array); diff --git a/Test_Numerics/Data/Statistics/Test_ProbabilityLazyExclusive.cs b/Test_Numerics/Data/Statistics/Test_ProbabilityLazyExclusive.cs index cb5e012b..012fefb2 100644 --- a/Test_Numerics/Data/Statistics/Test_ProbabilityLazyExclusive.cs +++ b/Test_Numerics/Data/Statistics/Test_ProbabilityLazyExclusive.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Data.Statistics; @@ -7,9 +7,8 @@ namespace Data.Statistics { /// - /// Unit tests for the lazily enumerated exclusive expansion (N13): row-order equivalence with - /// , output parity with the dense overload, the - /// emitted-combination cap, and enumeration past the dense form's dimension ceiling. + /// Unit tests for lazy exclusive-probability enumeration, including dense-order agreement, + /// convergence, residual mass, row reuse, and dimensions beyond dense enumeration. /// /// /// Authors: diff --git a/Test_Numerics/Data/Statistics/Test_ProbabilityPooledExclusive.cs b/Test_Numerics/Data/Statistics/Test_ProbabilityPooledExclusive.cs index c97f89a4..8268c502 100644 --- a/Test_Numerics/Data/Statistics/Test_ProbabilityPooledExclusive.cs +++ b/Test_Numerics/Data/Statistics/Test_ProbabilityPooledExclusive.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Data.Statistics; @@ -6,10 +6,8 @@ namespace Data.Statistics { /// - /// Unit tests for the pooled - /// overload (N11): output parity with the allocating overload, steady-state row-array reuse - /// across calls, and the truncation flag that surfaces the previously silent - /// inclusion-exclusion early exit. + /// Unit tests for pooled independent-exclusive probabilities, including agreement with the + /// allocating overload, output-row reuse, and early-convergence reporting. /// /// /// Authors: @@ -30,9 +28,7 @@ public class Test_ProbabilityPooledExclusive private static readonly int[] ThreeEventCombinations = { 3, 3, 1 }; /// - /// Test that the pooled overload reproduces the allocating overload exactly (the - /// allocating form now delegates to it), and reports no truncation on a full - /// enumeration. + /// Verifies agreement with the allocating overload for a complete enumeration. /// [TestMethod] public void Test_Pooled_MatchesAllocatingOverload() @@ -51,8 +47,8 @@ public void Test_Pooled_MatchesAllocatingOverload() Assert.HasCount(expectedIndicators.Count, pooledIndicators); for (int i = 0; i < expectedProbabilities.Count; i++) { - Assert.AreEqual(expectedProbabilities[i], pooledProbabilities[i], 0d, $"Row {i} probability parity."); - CollectionAssert.AreEqual(expectedIndicators[i], pooledIndicators[i], $"Row {i} indicator parity."); + Assert.AreEqual(expectedProbabilities[i], pooledProbabilities[i], 0d, $"Row {i} probability mismatch."); + CollectionAssert.AreEqual(expectedIndicators[i], pooledIndicators[i], $"Row {i} indicator mismatch."); } } @@ -78,7 +74,7 @@ public void Test_Pooled_ReusesRowArraysAcrossCalls() Assert.AreSame(firstCallRow6, pooledIndicators[6], "Every pooled row must be reused."); Assert.HasCount(7, pooledIndicators); - // Parity against a fresh allocating call on the second inputs. + // Compare with a fresh allocating call on the second inputs. Probability.IndependentExclusive(second, ThreeEventCombinations, ThreeEventIndicators, out List expectedProbabilities, out _); for (int i = 0; i < expectedProbabilities.Count; i++) @@ -88,10 +84,7 @@ public void Test_Pooled_ReusesRowArraysAcrossCalls() } /// - /// Test the truncation flag: many small probabilities converge the inclusion-exclusion - /// expansion early, so the deepest combinations are truncated behind one closing - /// pseudo-row — previously silent, now reported, with the allocating overload's outputs - /// unchanged. + /// Verifies that early convergence is reported and appends one closing pseudo-row. /// [TestMethod] public void Test_Pooled_TruncationIsReported() @@ -111,7 +104,7 @@ public void Test_Pooled_TruncationIsReported() Assert.IsLessThan(indicators.GetLength(0), pooledProbabilities.Count, "The deepest combinations were not enumerated."); Assert.HasCount(pooledProbabilities.Count, pooledIndicators); - // The allocating overload produces the same truncated outputs (it delegates). + // The allocating overload produces the same truncated outputs. Probability.IndependentExclusive(probabilities, combinations, indicators, out List expectedProbabilities, out _); Assert.HasCount(expectedProbabilities.Count, pooledProbabilities); diff --git a/Test_Numerics/Distributions/Test_ParameterValidity.cs b/Test_Numerics/Distributions/Test_ParameterValidity.cs index 46fa3ab0..c0059f65 100644 --- a/Test_Numerics/Distributions/Test_ParameterValidity.cs +++ b/Test_Numerics/Distributions/Test_ParameterValidity.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -73,10 +73,11 @@ public void SpecializedUnivariateSettersTrackFinalState() var kernel = new KernelDensity(new[] { -1d, 0d, 1d }); - kernel.Bandwidth = double.NaN; - Assert.IsFalse(kernel.ParametersValid); - kernel.Bandwidth = double.PositiveInfinity; - Assert.IsFalse(kernel.ParametersValid); + double originalBandwidth = kernel.Bandwidth; + Assert.Throws(() => kernel.Bandwidth = double.NaN); + Assert.AreEqual(originalBandwidth, kernel.Bandwidth, 0d); + Assert.Throws(() => kernel.Bandwidth = double.PositiveInfinity); + Assert.AreEqual(originalBandwidth, kernel.Bandwidth, 0d); kernel.Bandwidth = 0.5d; Assert.IsTrue(kernel.ParametersValid); diff --git a/Test_Numerics/Distributions/Univariate/Test_DistributionXElementRoundTrips.cs b/Test_Numerics/Distributions/Univariate/Test_DistributionXElementRoundTrips.cs index a4ee5696..6b63b1ea 100644 --- a/Test_Numerics/Distributions/Univariate/Test_DistributionXElementRoundTrips.cs +++ b/Test_Numerics/Distributions/Univariate/Test_DistributionXElementRoundTrips.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Data; using Numerics.Distributions; @@ -6,11 +6,8 @@ namespace Distributions.Univariate { /// - /// Unit tests for the data-bearing distribution XElement round-trips: the - /// and overrides that fix - /// the base scalar-only serialization (their X/probability and sample tables were lost — - /// the inherited form could not even complete, because both types report parameter names - /// without scalar parameter values). + /// Unit tests for empirical and kernel-density XML round trips, including their data tables, + /// transforms, kernels, bandwidths, and optional sample weights. /// /// /// Authors: @@ -49,7 +46,7 @@ public void Test_EmpiricalDistribution_RoundTrip() Assert.AreEqual(original.CDF(500d), restored.CDF(500d), 1E-12); Assert.AreEqual(original.InverseCDF(0.5d), restored.InverseCDF(0.5d), 1E-12); - // A missing table is loud, never a silent scalar-only form. + // A serialized empirical distribution requires both data tables. Assert.Throws(() => EmpiricalDistribution.FromXElement(new System.Xml.Linq.XElement("Distribution"))); } diff --git a/Test_Numerics/Distributions/Univariate/Test_ConvolveUpgrades.cs b/Test_Numerics/Distributions/Univariate/Test_EmpiricalConvolution.cs similarity index 87% rename from Test_Numerics/Distributions/Univariate/Test_ConvolveUpgrades.cs rename to Test_Numerics/Distributions/Univariate/Test_EmpiricalConvolution.cs index 3bba4afe..e8b77395 100644 --- a/Test_Numerics/Distributions/Univariate/Test_ConvolveUpgrades.cs +++ b/Test_Numerics/Distributions/Univariate/Test_EmpiricalConvolution.cs @@ -1,30 +1,27 @@ -using System; +using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Distributions; namespace Distributions.Univariate { /// - /// Unit tests for the N8 convolution upgrades: the atom-aware exact-lattice - /// (point masses — e.g. the - /// zero-inflation atom of a defective risk curve — have no representation in the - /// continuous-PDF pipeline) and the opt-in log-spaced output grid. + /// Unit tests for lattice-based discrete convolution and logarithmic output resampling. /// /// /// Authors: /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil /// [TestClass] - public class Test_ConvolveUpgrades + public class Test_EmpiricalConvolution { /// - /// Test the discrete convolution against exact enumeration: total mass is the product + /// Test the discrete convolution against direct atom enumeration: total mass is the product /// of the input totals, the mean is the sum of the input means (the moment-preserving /// two-node split makes this exact), and the cumulative mass at probes between the /// enumerated sum atoms matches the enumerated CDF. /// [TestMethod] - public void Test_ConvolveDiscrete_ExactEnumeration() + public void Test_ConvolveDiscrete_KnownDistribution() { double[] values1 = { 0d, 10d }; double[] masses1 = { 0.6d, 0.4d }; @@ -82,9 +79,8 @@ public void Test_ConvolveDiscrete_ZeroAtoms() } /// - /// Test the log-spaced output option: false delegates to the linear-grid method - /// unchanged (the same instance), true re-reads the same convolved CDF on a log-spaced - /// ladder (agreeing at probes) and requires a strictly positive summed support. + /// Verifies that logarithmic output resamples the linear convolution consistently and + /// rejects non-positive summed support. /// [TestMethod] public void Test_Convolve_LogSpacedOutput() diff --git a/Test_Numerics/Functions/Test_SegmentedPowerFunction.cs b/Test_Numerics/Functions/Test_SegmentedPowerFunction.cs index 53475a59..94556045 100644 --- a/Test_Numerics/Functions/Test_SegmentedPowerFunction.cs +++ b/Test_Numerics/Functions/Test_SegmentedPowerFunction.cs @@ -1,14 +1,12 @@ -using System; +using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Functions; namespace Functions { /// - /// Unit tests for the BaRatin addition-mode : parity - /// with the RMC-BestFit rating-curve prediction, the single-segment degeneracy to - /// , the numeric inverse, the parameter-vector contract, and the - /// serialization round-trip. + /// Unit tests for the BaRatin addition-mode , including + /// known values, the single-segment power-law case, inversion, parameters, and serialization. /// /// /// Authors: @@ -18,13 +16,11 @@ namespace Functions public class Test_SegmentedPowerFunction { /// - /// Test parity with the BestFit rating-curve prediction. The reference constants were - /// evaluated independently from the BaRatin addition-mode closed form - /// Q(h) = Σₖ 10^(log₁₀αₖ)(h − hₖ)^βₖ·𝟙{h > hₖ}, the exact body of - /// RMC.BestFit RatingCurve.Predict (Python evaluation, 2026-07-25). + /// Verifies one-, two-, and three-segment addition-mode values against the defining + /// closed form, including a fixed stochastic quantile. /// [TestMethod] - public void Test_BestFitParity() + public void Test_AdditionModeKnownValues() { // 1 segment: [h₁, log₁₀α₁, β₁, σ] = [1, 1.5, 2, 0.1] var oneSegment = new SegmentedPowerFunction(new[] { 1d, 1.5d, 2d, 0.1d }) { IsDeterministic = true }; @@ -89,7 +85,7 @@ public void Test_InverseFunction_RoundTrip() } /// - /// Test the parameter-vector contract: SetParameters applies a BestFit-layout posterior + /// Test the parameter-vector contract: SetParameters applies a fitted posterior /// draw directly, and the validation guards reject malformed vectors. /// [TestMethod] diff --git a/Test_Numerics/Mathematics/Integration/Test_AdaptiveGaussKronrodRecorder.cs b/Test_Numerics/Mathematics/Integration/Test_AdaptiveGaussKronrodRecorder.cs index b783648e..cd138fef 100644 --- a/Test_Numerics/Mathematics/Integration/Test_AdaptiveGaussKronrodRecorder.cs +++ b/Test_Numerics/Mathematics/Integration/Test_AdaptiveGaussKronrodRecorder.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Mathematics; @@ -8,12 +8,8 @@ namespace Mathematics.Integration { /// - /// Unit tests for the — the acceptance-aware - /// node ledger: recorded weights must partition the integration domain exactly (a rejected - /// interval's superseded evaluations never carry measure, so any double count would - /// overshoot the domain width), the weighted node sum must reproduce the returned integral - /// (the composite rule IS that sum), and an unattached recorder must leave the integration - /// byte-identical. + /// Unit tests for accepted-interval quadrature records, including domain mass, weighted + /// values, stratified integration, and the disabled-recorder path. /// /// /// Authors: @@ -30,10 +26,8 @@ private static double SharpPeak(double x) } /// - /// Test that the recorded weights sum to the domain width and the weighted node sum - /// reproduces the returned integral under forced deep subdivision — the proof that - /// abandoned (subdivided) parents never surface: if a rejected parent's nodes carried - /// weight, the weight sum would overshoot the domain width by that parent's width. + /// Verifies that accepted-interval weights partition the domain and their weighted + /// function values reproduce the integral after adaptive subdivision. /// [TestMethod] public void Test_Recorder_MassAndResultIdentities() @@ -48,9 +42,9 @@ public void Test_Recorder_MassAndResultIdentities() gk.Integrate(); Assert.AreEqual(IntegrationStatus.Success, gk.Status); - // The ledger arrives 21 nodes per accepted interval, and subdivision must have + // Records arrive as 21 nodes per accepted interval, and subdivision must have // happened for this peak at this tolerance. - Assert.AreEqual(0, records.Count % 21, "The ledger flushes whole 21-node intervals."); + Assert.AreEqual(0, records.Count % 21, "Records contain complete 21-node intervals."); int intervals = records.Count / 21; Assert.IsGreaterThan(1, intervals, "The sharp peak must force subdivision."); @@ -74,7 +68,7 @@ public void Test_Recorder_MassAndResultIdentities() } /// - /// Test the stratified-bin form: the ledger covers the union of the bins and reproduces + /// Test the stratified-bin form: the records cover the union of the bins and reproduces /// the summed result, with per-bin acceptance. /// [TestMethod] @@ -102,14 +96,12 @@ public void Test_Recorder_StratifiedBins() weightSum += records[i].Weight; weightedValueSum += records[i].Weight * records[i].Value; } - Assert.AreEqual(1d, weightSum, 1E-12, "The ledger covers the union of the stratification bins."); + Assert.AreEqual(1d, weightSum, 1E-12, "The records cover the union of the stratification bins."); Assert.AreEqual(gk.Result, weightedValueSum, Math.Abs(gk.Result) * 1E-12); } /// - /// Test that attaching the recorder does not perturb the integration itself, and that - /// leaving it unattached reproduces the original behavior bit-for-bit (the evaluation - /// sequence is identical; capture only observes it). + /// Verifies that recording does not change the result or function-evaluation count. /// [TestMethod] public void Test_Recorder_OffIsByteIdentical() diff --git a/Test_Numerics/Mathematics/Integration/Test_VegasTailFocusJacobian.cs b/Test_Numerics/Mathematics/Integration/Test_VegasTailFocusJacobian.cs index 41d3fcae..4488a91e 100644 --- a/Test_Numerics/Mathematics/Integration/Test_VegasTailFocusJacobian.cs +++ b/Test_Numerics/Mathematics/Integration/Test_VegasTailFocusJacobian.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Mathematics; using Numerics.Mathematics.Integration; @@ -7,13 +7,7 @@ namespace Mathematics.Integration { /// - /// Unit tests for the Vegas power-transform tail focus (N9): the upstream half of the - /// engine-level empirical audit. The transform p' = 1 − (1 − p)^γ concentrates samples in - /// the upper tail, and its Jacobian must fold into the weight handed to the integrand — - /// otherwise every consumer treating that weight as probability measure is biased even when - /// the returned integral is correct. A known heavy-tail integrand must integrate to the - /// same value at γ ∈ {1, 4, 10}, and the weights must sum to the domain volume per - /// evaluation batch at every γ. + /// Unit tests for the VEGAS probability-space power transform and its Jacobian correction. /// /// /// Authors: diff --git a/Test_Numerics/Sampling/MCMC/Test_MCMCSamplerFindings.cs b/Test_Numerics/Sampling/MCMC/Test_MCMCSamplerDiagnostics.cs similarity index 96% rename from Test_Numerics/Sampling/MCMC/Test_MCMCSamplerFindings.cs rename to Test_Numerics/Sampling/MCMC/Test_MCMCSamplerDiagnostics.cs index 570d6270..90f8904a 100644 --- a/Test_Numerics/Sampling/MCMC/Test_MCMCSamplerFindings.cs +++ b/Test_Numerics/Sampling/MCMC/Test_MCMCSamplerDiagnostics.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; using Numerics; using Numerics.Data.Statistics; using Numerics.Distributions; @@ -9,15 +9,10 @@ namespace Sampling.MCMC { /// - /// Characterizes the adaptive-random-walk and NUTS diagnostic findings used by - /// the RMC.BestFit verification program. + /// Unit tests for adaptive-random-walk covariance updates and NUTS diagnostics. /// - /// - /// These tests intentionally pin the current public behavior before any sampler - /// correction. They use deterministic inline targets and do not require R or Python. - /// [TestClass] - public class Test_MCMCSamplerFindings + public class Test_MCMCSamplerDiagnostics { /// /// Confirms that rejected transitions before the warmup boundary advance the diff --git a/Test_Numerics/Test_CorrectnessRepairs.cs b/Test_Numerics/Test_CorrectnessRepairs.cs new file mode 100644 index 00000000..fd52f2a1 --- /dev/null +++ b/Test_Numerics/Test_CorrectnessRepairs.cs @@ -0,0 +1,481 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using System.Xml.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Data; +using Numerics.Data.Statistics; +using Numerics.Distributions; +using Numerics.Functions; +using Numerics.Mathematics; +using Numerics.Mathematics.Integration; +using Numerics.Mathematics.Optimization; +using Numerics.Mathematics.SpecialFunctions; + +namespace Correctness +{ + /// + /// Regression tests for function state, validation, and serialization behavior. + /// + [TestClass] + public class Test_FunctionCorrectnessRepairs + { + /// Verifies deterministic restoration and atomic validation for segmented power functions. + [TestMethod] + public void SegmentedPower_DeterministicXmlAndAtomicValidation() + { + var function = new SegmentedPowerFunction(1) { IsDeterministic = true, Maximum = 20d }; + function.SetParameters(new[] { 1d, 0.5d, 2d, 0d }); + Assert.IsTrue(function.ParametersValid); + + var restored = (SegmentedPowerFunction)UnivariateFunctionFactory.CreateFromXElement(function.ToXElement()); + Assert.IsTrue(restored.IsDeterministic); + Assert.AreEqual(20d, restored.Maximum, 0d); + Assert.AreEqual(function.Function(4d), restored.Function(4d), 0d); + + double originalBeta = function.GetBeta(1); + function.SetParameters(new[] { 1d, 0.5d, 0d, 0d }); + Assert.IsTrue(function.ParametersValid, "A rejected update must leave the prior state valid."); + Assert.AreEqual(originalBeta, function.GetBeta(1), 0d); + + Assert.Throws(() => function.Maximum = function.Minimum); + Assert.AreEqual(20d, function.Maximum, 0d); + Assert.Throws(() => new SegmentedPowerFunction(new[] { 1d, 0.5d, 0d, 0.1d })); + + XElement malformed = function.ToXElement(); + malformed.SetAttributeValue(nameof(SegmentedPowerFunction.Maximum), "0"); + Assert.Throws(() => SegmentedPowerFunction.FromXElement(malformed)); + } + + /// Verifies that composite evaluation restores child confidence state after an exception. + [TestMethod] + public void Composite_RestoresChildStateAfterExceptions() + { + var child = new ConfidenceProbeFunction { ConfidenceLevel = 0.35d, ThrowOnEvaluation = true }; + var composite = new CompositeFunction(new IUnivariateFunction[] { child }) { ConfidenceLevel = 0.8d }; + + Assert.Throws(() => composite.Function(1d)); + Assert.AreEqual(0.35d, child.ConfidenceLevel, 0d); + Assert.Throws(() => composite.InverseFunction(1d)); + Assert.AreEqual(0.35d, child.ConfidenceLevel, 0d); + } + + /// Verifies concurrent stateful evaluation and the lock-free deterministic path. + [TestMethod] + public void Composite_SynchronizesOnlyStatefulChildren() + { + var child = new ConfidenceProbeFunction { ConfidenceLevel = 0.5d }; + var lower = new CompositeFunction(new IUnivariateFunction[] { child }) { ConfidenceLevel = 0.2d }; + var upper = new CompositeFunction(new IUnivariateFunction[] { child }) { ConfidenceLevel = 0.8d }; + int failures = 0; + + Parallel.For(0, 400, i => + { + double expected = i % 2 == 0 ? 0.2d : 0.8d; + double actual = i % 2 == 0 ? lower.Function(0d) : upper.Function(0d); + if (actual != expected) Interlocked.Increment(ref failures); + }); + + Assert.AreEqual(0, failures); + Assert.AreEqual(0.5d, child.ConfidenceLevel, 0d); + + child.IsDeterministic = true; + child.ThrowOnConfidenceAssignment = true; + Assert.AreEqual(0.5d, lower.Function(0d), 0d, + "Deterministic child evaluation must not mutate confidence state."); + } + + /// Verifies atomic weight rejection and composite mode validation. + [TestMethod] + public void Composite_RejectsInvalidStateAtomically() + { + var composite = new CompositeFunction( + new IUnivariateFunction[] { new LinearFunction(), new LinearFunction(1d, 2d) }, + new[] { 0.25d, 0.75d }); + composite.SetParameters(new[] { double.NaN, 0.75d }); + Assert.IsTrue(composite.ParametersValid); + Assert.AreEqual(0.25d, composite.Weights[0], 0d); + Assert.Throws(() => composite.Mode = (CompositeFunctionMode)999); + + XElement malformed = composite.ToXElement(); + malformed.SetAttributeValue(nameof(CompositeFunction.Mode), "999"); + Assert.Throws(() => CompositeFunction.FromXElement(malformed)); + } + + /// Verifies ensemble parameter ownership and serialized-set validation. + [TestMethod] + public void Ensemble_OwnsDeepCopiesAndValidatesXmlSets() + { + var values = new[] { 1d, 0.5d, 2d, 0.1d }; + var ensemble = new EnsembleFunction( + new SegmentedPowerFunction(values), + new[] { new ParameterSet(values, 1d, 0.5d) }); + + values[0] = 99d; + Assert.AreEqual(1d, ((SegmentedPowerFunction)ensemble.Sample(0)).GetBreakpoint(1), 0d); + + ParameterSet exposed = ensemble.ParameterSets[0]; + exposed.Values[0] = 88d; + Assert.AreEqual(1d, ((SegmentedPowerFunction)ensemble.Sample(0)).GetBreakpoint(1), 0d); + Assert.Throws(() => ensemble.Sample(double.NaN)); + + XElement invalidValues = ensemble.ToXElement(); + invalidValues.Element("ParameterSets")!.Element(nameof(ParameterSet))! + .SetAttributeValue(nameof(ParameterSet.Values), "1|0.5|0|0.1"); + Assert.Throws(() => EnsembleFunction.FromXElement(invalidValues)); + + XElement invalidFitness = ensemble.ToXElement(); + invalidFitness.Element("ParameterSets")!.Element(nameof(ParameterSet))! + .SetAttributeValue(nameof(ParameterSet.Fitness), "NaN"); + Assert.Throws(() => EnsembleFunction.FromXElement(invalidFitness)); + } + + private sealed class ConfidenceProbeFunction : IUnivariateFunction + { + private double _confidenceLevel = -1d; + + public bool ThrowOnEvaluation { get; set; } + public bool ThrowOnConfidenceAssignment { get; set; } + public int NumberOfParameters => 0; + public bool ParametersValid => true; + public double Minimum { get; set; } = double.MinValue; + public double Maximum { get; set; } = double.MaxValue; + public double[] MinimumOfParameters => Array.Empty(); + public double[] MaximumOfParameters => Array.Empty(); + public bool IsDeterministic { get; set; } + public double ConfidenceLevel + { + get { return _confidenceLevel; } + set + { + if (ThrowOnConfidenceAssignment) throw new InvalidOperationException("Confidence assignment was not expected."); + _confidenceLevel = value; + } + } + + public void SetParameters(IList parameters) + { + if (parameters == null) throw new ArgumentNullException(nameof(parameters)); + if (parameters.Count != 0) throw new ArgumentException("This test function has no parameters.", nameof(parameters)); + } + + public ArgumentOutOfRangeException ValidateParameters(IList parameters, bool throwException) + { + return null; + } + + public double Function(double x) + { + if (ThrowOnEvaluation) throw new InvalidOperationException("Test evaluation failure."); + double first = ConfidenceLevel; + Thread.SpinWait(10000); + if (first != ConfidenceLevel) throw new InvalidOperationException("Confidence state changed during evaluation."); + return ConfidenceLevel; + } + + public double InverseFunction(double y) + { + if (ThrowOnEvaluation) throw new InvalidOperationException("Test inverse failure."); + return ConfidenceLevel; + } + } + } + + /// + /// Regression tests for probability and jackknife edge cases. + /// + [TestClass] + public class Test_StatisticsCorrectnessRepairs + { + /// Verifies residual mass when capped enumeration omits the no-event row. + [TestMethod] + public void LazyExclusive_CappedWithoutNoEventRowPreservesUnionMass() + { + double[] probabilities = { 0.4d, 0.35d, 0.3d, 0.25d, 0.2d, 0.15d }; + var output = new List(); + var indicators = new List(); + + var status = Probability.IndependentExclusiveLazy(probabilities, output, indicators, + includeNoEventRow: false, maxEmittedCombinations: 10, + absoluteTolerance: 0d, relativeTolerance: 0d); + + Assert.AreEqual(Probability.ExclusiveEnumerationStatus.Capped, status); + double noEventMass = probabilities.Aggregate(1d, (mass, probability) => mass * (1d - probability)); + Assert.AreEqual(1d - noEventMass, output.Sum(), 1E-12); + Assert.HasCount(11, output); + } + + /// Verifies structural and tolerance validation for pooled enumeration. + [TestMethod] + public void PooledExclusive_RejectsMalformedMetadata() + { + double[] probabilities = { 0.2d, 0.3d }; + var output = new List(); + var indicators = new List(); + int[,] rows = { { 1, 0 }, { 0, 1 }, { 1, 1 } }; + + Assert.Throws(() => Probability.IndependentExclusive( + probabilities, new[] { 2 }, rows, output, indicators)); + Assert.Throws(() => Probability.IndependentExclusive( + probabilities, new[] { 1, 2 }, rows, output, indicators)); + Assert.Throws(() => Probability.IndependentExclusive( + probabilities, new[] { 2, 1 }, rows, output, indicators, double.NaN)); + } + + /// Verifies public combination tuple validation. + [TestMethod] + public void NextCombination_RejectsInvalidTuples() + { + Assert.Throws(() => Factorial.NextCombination(null!, 3)); + Assert.Throws(() => Factorial.NextCombination(Array.Empty(), 3)); + Assert.Throws(() => Factorial.NextCombination(new[] { 0, 0 }, 3)); + Assert.Throws(() => Factorial.NextCombination(new[] { 0, 3 }, 3)); + Assert.Throws(() => Factorial.NextCombination(new[] { 0 }, -1)); + } + + /// Verifies single-element behavior and callback sample isolation. + [TestMethod] + public void Jackknife_PreservesSingleElementAndIsolatesCallbacks() + { + int calls = 0; + double standardError = Statistics.JackKnifeStandardError(new[] { 5d }, sample => + { + Interlocked.Increment(ref calls); + return sample.Count; + }); + Assert.AreEqual(0d, standardError, 0d); + Assert.AreEqual(0, calls, "The single-element standard error does not evaluate an empty sample."); + + double[] single = Statistics.JackKnifeSample(new[] { 5d }, sample => + { + Interlocked.Increment(ref calls); + return sample.Count; + }); + Assert.IsNotNull(single); + Assert.AreEqual(0d, single[0], 0d); + Assert.AreEqual(1, calls); + + double[] original = { 1d, 2d, 3d, 4d }; + var callbackSamples = new List>(); + object sync = new object(); + Statistics.JackKnifeSample(original, sample => + { + lock (sync) callbackSamples.Add(sample); + if (sample.Count > 0) sample[0] = -100d; + return sample.Count; + }); + CollectionAssert.AreEqual(new[] { 1d, 2d, 3d, 4d }, original); + Assert.HasCount(original.Length, callbackSamples); + Assert.AreEqual(original.Length, callbackSamples.Distinct().Count()); + } + } + + /// + /// Regression tests for bootstrap fitting and interpolation behavior. + /// + [TestClass] + public class Test_BootstrapCorrectnessRepairs + { + /// Verifies successful-fit denominators and explicit all-failure errors. + [TestMethod] + public void Bootstrap_UsesOnlySuccessfulFitsAndRejectsAllFailures() + { + var parent = new Normal(0d, 1d); + var analysis = new BootstrapAnalysis(parent, ParameterEstimationMethod.MethodOfMoments, 10, 1234); + IUnivariateDistribution[] mixed = { new Normal(0d, 1d), null!, new Normal(1d, 1d) }; + + double[] mean = analysis.ExpectedProbabilities(new[] { 0d }, mixed); + double expected = 0.5d * (new Normal(0d, 1d).CDF(0d) + new Normal(1d, 1d).CDF(0d)); + Assert.AreEqual(expected, mean[0], 1E-14); + Assert.Throws(() => + analysis.ExpectedProbabilities(new[] { 0d }, new IUnivariateDistribution[] { null!, null! })); + + var aggregate = Assert.Throws(() => analysis.Distributions(new[] + { + new ParameterSet(new[] { 0d, -1d }, 0d), + new ParameterSet(new[] { double.NaN, 1d }, 0d), + })); + Assert.HasCount(2, aggregate.InnerExceptions); + } + + /// Verifies sign-preserving transforms for negative quantiles. + [TestMethod] + public void Bootstrap_NormalIntervalsPreserveNegativeQuantiles() + { + var parent = new Normal(-10d, 1d); + var analysis = new BootstrapAnalysis(parent, ParameterEstimationMethod.MethodOfMoments, 10, 1234); + IUnivariateDistribution[] fits = + { + new Normal(-9.5d, 1d), + new Normal(-10d, 1.1d), + null!, + new Normal(-10.5d, 0.9d), + }; + + double[,] interval = analysis.NormalQuantileCI(new[] { 0.5d }, 0.1d, fits); + Assert.IsFalse(double.IsNaN(interval[0, 0]) || double.IsInfinity(interval[0, 0])); + Assert.IsFalse(double.IsNaN(interval[0, 1]) || double.IsInfinity(interval[0, 1])); + Assert.IsLessThan(0d, interval[0, 0]); + Assert.IsLessThan(0d, interval[0, 1]); + } + + /// Verifies interpolation equivalence for sorted and unsorted ordinates. + [TestMethod] + public void Bootstrap_InterpolationKeepsSortedPairsTogether() + { + var parent = new Normal(0d, 1d); + var analysis = new BootstrapAnalysis(parent, ParameterEstimationMethod.MethodOfMoments, 10, 1234); + IUnivariateDistribution[] fits = { new Normal(0d, 1d), new Normal(1d, 2d) }; + double[] sorted = { -3d, -1d, 0d, 2d, 5d }; + double[] unsorted = { 2d, -3d, 5d, 0d, -1d }; + double[] probabilities = { 0.1d, 0.5d, 0.9d }; + + double[] expected = analysis.ExpectedProbabilities(sorted, probabilities, fits); + double[] actual = analysis.ExpectedProbabilities(unsorted, probabilities, fits); + CollectionAssert.AreEqual(expected, actual); + } + } + + /// + /// Regression tests for distribution serialization and numerical edge cases. + /// + [TestClass] + public class Test_DistributionCorrectnessRepairs + { + /// Verifies rejection of malformed serialized distribution data. + [TestMethod] + public void DistributionXml_RejectsMalformedTablesEnumsAndWeights() + { + var empirical = new EmpiricalDistribution(new[] { 1d, 2d }, new[] { 0d, 1d }); + XElement invalidOrder = empirical.ToXElement(); + invalidOrder.SetAttributeValue("ProbabilityOrder", "999"); + Assert.Throws(() => UnivariateDistributionFactory.CreateDistribution(invalidOrder)); + + XElement invalidTransform = empirical.ToXElement(); + invalidTransform.SetAttributeValue(nameof(EmpiricalDistribution.XTransform), "999"); + Assert.Throws(() => EmpiricalDistribution.FromXElement(invalidTransform)); + + var kernel = new KernelDensity(new[] { 1d, 2d, 3d }, new[] { 1d, 1d, 1d }, KernelDensity.KernelType.Gaussian, 0.5d); + XElement zeroWeights = kernel.ToXElement(); + zeroWeights.SetAttributeValue("Weights", "0|0|0"); + Assert.Throws(() => KernelDensity.FromXElement(zeroWeights)); + + XElement invalidBandwidth = kernel.ToXElement(); + invalidBandwidth.SetAttributeValue(nameof(KernelDensity.Bandwidth), "NaN"); + Assert.Throws(() => KernelDensity.FromXElement(invalidBandwidth)); + + XElement missingType = new Normal().ToXElement(); + missingType.Attribute(nameof(UnivariateDistributionBase.Type))!.Remove(); + Assert.Throws(() => UnivariateDistributionFactory.CreateDistribution(missingType)); + } + + /// Verifies occupied support, total mass, and mean for lattice convolution. + [TestMethod] + public void DiscreteConvolution_UsesOccupiedSupportAndPositiveMass() + { + EmpiricalDistribution.ConvolveDiscrete( + new[] { -100d, 2d, 5d }, new[] { 0d, 0.25d, 0.75d }, + new[] { 3d, 7d, 100d }, new[] { 0.5d, 0.5d, 0d }, + 256, out double[] values, out double[] masses); + + Assert.AreEqual(5d, values[0], 1E-12); + double step = values[1] - values[0]; + Assert.IsGreaterThanOrEqualTo(12d, values[values.Length - 1]); + Assert.IsLessThanOrEqualTo(step + 1E-12, values[values.Length - 1] - 12d, + "The occupied lattice may extend at most one node beyond the exact support."); + Assert.AreEqual(1d, masses.Sum(), 1E-12); + double mean = values.Zip(masses, (value, mass) => value * mass).Sum(); + Assert.AreEqual(9.25d, mean, 1E-10); + + Assert.Throws(() => EmpiricalDistribution.ConvolveDiscrete( + new[] { 1d, 2d }, new[] { 0d, 0d }, new[] { 1d }, new[] { 1d }, + 256, out _, out _)); + } + + /// Verifies early rejection of degenerate logarithmic output support. + [TestMethod] + public void LogConvolution_RejectsDegenerateSupportBeforeFft() + { + var point1 = new EmpiricalDistribution(new[] { 2d, 2d }, new[] { 0d, 1d }); + var point2 = new EmpiricalDistribution(new[] { 3d, 3d }, new[] { 0d, 1d }); + Assert.Throws(() => + EmpiricalDistribution.Convolve(point1, point2, 128, logSpacedOutput: true)); + } + + /// Verifies competing-risk seed invalidation, cloning, and serialization. + [TestMethod] + public void CompetingRisks_SeedInvalidatesCachesAndRoundTrips() + { + var distribution = new CompetingRisks(new UnivariateDistributionBase[] + { + new Normal(0d, 1d), + new Exponential(2d), + }) { PRNGSeed = 2468 }; + + FieldInfo mvnCreated = typeof(CompetingRisks).GetField("_mvnCreated", BindingFlags.Instance | BindingFlags.NonPublic)!; + FieldInfo empiricalCreated = typeof(CompetingRisks).GetField("_empiricalCDFCreated", BindingFlags.Instance | BindingFlags.NonPublic)!; + mvnCreated.SetValue(distribution, true); + empiricalCreated.SetValue(distribution, true); + distribution.PRNGSeed = 1357; + Assert.IsFalse((bool)mvnCreated.GetValue(distribution)!); + Assert.IsFalse((bool)empiricalCreated.GetValue(distribution)!); + + var clone = (CompetingRisks)distribution.Clone(); + Assert.AreEqual(1357, clone.PRNGSeed); + var restored = (CompetingRisks)UnivariateDistributionFactory.CreateDistribution(distribution.ToXElement()); + Assert.AreEqual(1357, restored.PRNGSeed); + + XElement malformed = distribution.ToXElement(); + malformed.SetAttributeValue(nameof(CompetingRisks.Dependency), "999"); + Assert.Throws(() => CompetingRisks.FromXElement(malformed)); + + var multivariate = new MultivariateNormal(2); + Assert.Throws(() => multivariate.MVNUNI = null!); + } + } + + /// + /// Regression tests for integration state and tail-focus validation. + /// + [TestClass] + public class Test_IntegrationCorrectnessRepairs + { + /// Verifies that recorder mutation does not alter an active integration. + [TestMethod] + public void AdaptiveRecorder_IsSnapshottedForAnIntegration() + { + int calls = 0; + AdaptiveGaussKronrod integration = null; + integration = new AdaptiveGaussKronrod(x => x * x, 0d, 1d) + { + MinDepth = 2, + Recorder = (x, weight, value) => + { + Interlocked.Increment(ref calls); + integration!.Recorder = null; + }, + }; + + integration.Integrate(); + Assert.AreEqual(IntegrationStatus.Success, integration.Status); + Assert.IsGreaterThan(1, calls, "The recorder snapshot remains active until the integration completes."); + } + + /// Verifies finite positive tail-focus validation. + [TestMethod] + public void Vegas_RejectsInvalidTailFocusParametersAtomically() + { + var vegas = new Vegas((point, weight) => point[0] * weight, 1, new[] { 0d }, new[] { 1d }); + Assert.Throws(() => vegas.TailFocusParameter = 0d); + Assert.Throws(() => vegas.TailFocusParameter = double.NaN); + Assert.Throws(() => vegas.TailFocusParameter = double.PositiveInfinity); + Assert.AreEqual(1d, vegas.TailFocusParameter, 0d); + Assert.Throws(() => vegas.ConfigureForRareEvents(0d)); + Assert.Throws(() => vegas.ConfigureForRareEvents(1d)); + Assert.Throws(() => vegas.ConfigureForRareEvents(double.NaN)); + } + } +} \ No newline at end of file diff --git a/docs/functions/index.md b/docs/functions/index.md index edc84d31..a06e099b 100644 --- a/docs/functions/index.md +++ b/docs/functions/index.md @@ -1,4 +1,4 @@ -# Univariate Functions +# Univariate Functions [Back to Index](../index.md) @@ -32,10 +32,10 @@ Every function implements `IUnivariateFunction`: ### Segmented power (BaRatin addition mode) -`SegmentedPowerFunction` matches the RMC-BestFit rating-curve parameterization exactly. The +`SegmentedPowerFunction` uses a parameter vector that can be applied directly to fitted draws. The parameter vector is `[h₁, log₁₀α₁, β₁, h₂, log₁₀α₂, β₂, …, σ]` (length `3·segments + 1`), so a fitted posterior `ParameterSet.Values` applies directly through `SetParameters`. Breakpoints -must be strictly ordered, exponents non-negative (the monotone-rating constraint behind the +must be strictly ordered, exponents positive (the monotonicity constraint behind the numeric Brent inverse), discharge is zero at and below the cease-to-flow stage `h₁`, and one segment degenerates to the plain `PowerFunction`. @@ -46,7 +46,7 @@ using Numerics.Functions; var rating = new SegmentedPowerFunction(new[] { 1.0, 1.5, 2.0, 3.0, 1.2, 1.5, 0.1 }); double q = rating.Function(5.0); // deterministic (mean) discharge rating.IsDeterministic = false; -rating.ConfidenceLevel = 0.75; // multiplies by 10^(z·σ), the BestFit stochastic form +rating.ConfidenceLevel = 0.75; // multiplies by 10^(z·σ) double q75 = rating.Function(5.0); ``` @@ -79,8 +79,7 @@ derives from ξ and never serializes. The serialization surface lives on the con ## Posterior ensembles `EnsembleFunction` carries a template function plus an array of posterior `ParameterSet` -draws — the vehicle for imported fitted functions (e.g., a BestFit rating-curve posterior) -inside a simulation engine. Both sampling surfaces are **pure**: every call returns a fresh +draws for use inside a simulation engine. Both sampling surfaces return a fresh clone of the template configured with the selected draw, so concurrent realizations share no mutable state. From d5f8e46e40ec598f819b19556951c32c4a473080 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 27 Jul 2026 07:50:28 -0600 Subject: [PATCH 022/222] Modernize MCMC convergence diagnostics --- .../Sampling/MCMC/Support/MCMCDiagnostics.cs | 559 ++++++++++++++---- .../Sampling/MCMC/Test_MCMCDiagnostics.cs | 102 ++++ 2 files changed, 545 insertions(+), 116 deletions(-) diff --git a/Numerics/Sampling/MCMC/Support/MCMCDiagnostics.cs b/Numerics/Sampling/MCMC/Support/MCMCDiagnostics.cs index 7371f31c..dd97a102 100644 --- a/Numerics/Sampling/MCMC/Support/MCMCDiagnostics.cs +++ b/Numerics/Sampling/MCMC/Support/MCMCDiagnostics.cs @@ -19,174 +19,501 @@ namespace Numerics.Sampling.MCMC /// public class MCMCDiagnostics { + /// + /// Blom offset used by the rank-normal inverse transformation. + /// + private const double RankOffset = 3d / 8d; + + /// + /// IEEE-754 double-precision machine epsilon used by R posterior + /// to identify constant diagnostic inputs. + /// + private const double MachineEpsilon = 2.2204460492503131E-16d; /// - /// Compute the effective sample size. + /// Computes a conservative rank-normalized effective sample size. /// /// The series of posterior samples to evaluate. + /// + /// The minimum of bulk, lower-tail, and upper-tail effective sample size, + /// or when the series is insufficient or degenerate. + /// + /// Thrown when is null. + /// + /// The series is split into two half-chains. Bulk ESS uses pooled rank-normalized + /// draws; tail ESS uses indicators at the pooled 5th and 95th percentiles. Each + /// component uses Geyer's multi-chain initial-positive and initial-monotone sequence. + /// Reference: Vehtari et al. (2021), Bayesian Analysis 16(2), 667-718. + /// public static double EffectiveSampleSize(IList series) { - //https://www.rdocumentation.org/packages/LaplacesDemon/versions/16.1.4/topics/ESS - int N = series.Count; - var acf = Fourier.Autocorrelation(series, (int)Math.Ceiling((double)N / 2)); - if (acf == null) return N; - double rho = 0; - for (int i = 1; i < acf.GetLength(0); i++) - { - if (acf[i, 1] < 0.0) break; - rho += acf[i, 1]; - } - return Math.Min(N / (1d + 2d * rho), N); + if (series == null) + throw new ArgumentNullException(nameof(series)); + + return ComputeConservativeEffectiveSampleSize(new[] { series.ToArray() }); } /// - /// Computes the effective samples size for each model parameter. + /// Computes a conservative rank-normalized effective sample size for each model parameter. /// - /// The list of Markov Chains to be evaluated. The chains must be of equal length. + /// + /// The Markov chains to evaluate. When lengths differ, only their common leading + /// length is used, matching the historical pooled-output behavior. + /// /// Output. A jagged array of averaged autocorrelation functions, one for each parameter. + /// + /// One conservative scalar ESS per parameter: the minimum of bulk, lower-tail, + /// and upper-tail ESS. Unusable inputs return . + /// + /// Thrown when no chains are provided or a chain is empty. + /// Thrown when no model parameters are present. + /// + /// The 51-row averaged original-scale autocorrelation output is retained for plotting. + /// It is not used to compute the modern scalar ESS. + /// public static double[] EffectiveSampleSize(IList> markovChains, out double[][,] averageACF) { - // Get number of chains - int M = markovChains.Count; - if (M == 0) throw new ArgumentException(nameof(markovChains), "No chains provided."); - // Get number of parameters - int P = markovChains[0][0].Values.Length; - // Get the minimum number of iterations - int N = markovChains.Min(chain => chain.Count); + if (markovChains == null || markovChains.Count == 0) + throw new ArgumentException("No chains provided.", nameof(markovChains)); + if (markovChains.Any(chain => chain == null || chain.Count == 0)) + throw new ArgumentException("Every chain must contain at least one iteration.", nameof(markovChains)); - // Validation checks - if (N < 2) throw new ArgumentOutOfRangeException(nameof(markovChains), "There must be at least two iterations to evaluate."); - if (P < 1) throw new ArgumentOutOfRangeException(nameof(markovChains), "There must be at least one parameter to evaluate."); + int chainCount = markovChains.Count; + int parameterCount = markovChains[0][0].Values.Length; + int commonLength = markovChains.Min(chain => chain.Count); + if (parameterCount < 1) + throw new ArgumentOutOfRangeException(nameof(markovChains), "There must be at least one parameter to evaluate."); - // Create result arrays. - var ESS= new double[P]; - averageACF = new double[P][,]; - - for (int p = 0; p < P; p++) + var effectiveSampleSizes = new double[parameterCount]; + averageACF = new double[parameterCount][,]; + for (int parameterIndex = 0; parameterIndex < parameterCount; parameterIndex++) { - // Compute the Autocorrelation Function (ACF) and Effective Sample Size (ESS) - // Average the ACF and sum the ESS - averageACF[p] = new double[51, 2]; - double meanRho = 0; - - for (int i = 0; i < M; i++) + var valuesByChain = new double[chainCount][]; + averageACF[parameterIndex] = new double[51, 2]; + for (int chainIndex = 0; chainIndex < chainCount; chainIndex++) { - // Get values for this parameter within this chain - var values = markovChains[i].Select(set => set.Values[p]).ToArray(); - - // Get ACF for this chain - var acf = Fourier.Autocorrelation(values, (int)Math.Ceiling((double)N / 2)); - if (acf == null) continue; - // Update the average ACF across all chains - for (int j = 0; j < acf.GetLength(0); j++) - { - if (j > 50) break; - averageACF[p][j, 1] += acf[j, 1] / M; - } - // https://www.rdocumentation.org/packages/LaplacesDemon/versions/16.1.4/topics/ESS - // Get rho for the current chain - double rho = 0; - for (int j = 1; j < acf.GetLength(0); j++) - { - if (acf[j, 1] < 0.0) break; - rho += acf[j, 1]; - } - meanRho += rho / M; - + var values = new double[commonLength]; + for (int iteration = 0; iteration < commonLength; iteration++) + values[iteration] = markovChains[chainIndex][iteration].Values[parameterIndex]; + valuesByChain[chainIndex] = values; + AccumulateAverageAutocorrelation(values, averageACF[parameterIndex], chainCount); } - ESS[p] += Math.Min(N * M / (1d + 2d * meanRho), N * M); + + effectiveSampleSizes[parameterIndex] = + ComputeConservativeEffectiveSampleSize(valuesByChain); } - return ESS; + return effectiveSampleSizes; } /// - /// The Gelman-Rubin diagnostic. + /// Computes rank-normalized split and folded split R-hat. /// /// /// - /// The Gelman-Rubin diagnostic tests for lack of convergence by comparing the variance between multiple chains - /// to the variance within each chain. If convergence has been achieved, the between-chain and within-chain - /// variances should be identical. To be most effective in detecting evidence for non convergence, each chain should - /// have been initialized to starting values that are dispersed relative to the target distribution. + /// Each retained chain is split in half and pooled ranks are transformed to normal + /// scores. The returned value is the maximum of rank-normalized split R-hat and + /// folded rank-normalized split R-hat, making it sensitive to both location and scale. + /// + /// + /// A single original chain returns for compatibility and + /// because independent-chain comparison is unavailable. Reference: Vehtari et al. + /// (2021), Bayesian Analysis 16(2), 667-718. /// /// /// The list of Markov Chains to be evaluated. The chains must be of equal length. /// The number of warm up MCMC iterations to discard at the beginning of the chains. + /// One modern R-hat value per model parameter. + /// Thrown when no chains are provided or chain lengths differ. + /// Thrown for an invalid warmup count or parameter count. public static double[] GelmanRubin(IList> markovChains, int warmupIterations = 0) { + if (markovChains == null || markovChains.Count == 0) + throw new ArgumentException("No chains provided.", nameof(markovChains)); + if (markovChains.Any(chain => chain == null || chain.Count == 0)) + throw new ArgumentException("Every chain must contain at least one iteration.", nameof(markovChains)); - // Get number of chains - int M = markovChains.Count; - if (M == 0) throw new ArgumentException(nameof(markovChains), "No chains provided."); - // Get number of parameters - int P = markovChains[0][0].Values.Length; - // Get the number of iterations - int N = markovChains[0].Count; + int chainCount = markovChains.Count; + int parameterCount = markovChains[0][0].Values.Length; + int iterationCount = markovChains[0].Count; foreach (var chain in markovChains) { - if (chain.Count != N) + if (chain.Count != iterationCount) throw new ArgumentException("All chains must have the same length."); } - // Create result array. - var Rhat = new double[P]; - Rhat.Fill(double.NaN); - + if (parameterCount < 1) + throw new ArgumentOutOfRangeException(nameof(markovChains), "There must be at least one parameter to evaluate."); + if (warmupIterations < 0 || warmupIterations >= iterationCount) + throw new ArgumentOutOfRangeException(nameof(warmupIterations), "Warmup iterations must leave at least one retained iteration."); - // Validation checks - if (M < 2) return Rhat; - if (N < 2) throw new ArgumentOutOfRangeException(nameof(markovChains), "There must be at least two iterations to evaluate."); - if (P < 1) throw new ArgumentOutOfRangeException(nameof(markovChains), "There must be at least one parameter to evaluate."); - if (warmupIterations < 0) throw new ArgumentOutOfRangeException(nameof(warmupIterations), "The warm up iterations must be non-negative."); - int startIndex = Math.Max(0, warmupIterations); + var rhat = Enumerable.Repeat(double.NaN, parameterCount).ToArray(); + int retainedCount = iterationCount - warmupIterations; + if (chainCount < 2 || retainedCount < 4) + return rhat; - // Compute R-hat or each parameter - for (int p = 0; p < P; p++) + for (int parameterIndex = 0; parameterIndex < parameterCount; parameterIndex++) { - // Step 1. Compute between- and within-chain mean - var chainMeans = new double[M]; - double overallMean = 0; - for (int i = 0; i < M; i++) + var retained = new double[chainCount][]; + for (int chainIndex = 0; chainIndex < chainCount; chainIndex++) { - for (int j = startIndex; j < N; j++) + retained[chainIndex] = new double[retainedCount]; + for (int iteration = 0; iteration < retainedCount; iteration++) { - chainMeans[i] += markovChains[i][j].Values[p]; + retained[chainIndex][iteration] = + markovChains[chainIndex][warmupIterations + iteration].Values[parameterIndex]; } - // Get within-chain mean - chainMeans[i] /= (N - startIndex); - overallMean += chainMeans[i]; } - // Get between-chain mean - overallMean /= M; - // Step 2. Compute between- and within-chain variance - int n = N - startIndex; - double B = 0, W = 0; - for (int i = 0; i < M; i++) + if (ShouldReturnNaN(retained)) + continue; + + double rankRhat = ComputeBasicRhat(RankNormalize(SplitChains(retained))); + double foldedRhat = ComputeBasicRhat( + RankNormalize(SplitChains(FoldAroundMedian(retained)))); + if (Tools.IsFinite(rankRhat) && Tools.IsFinite(foldedRhat)) + rhat[parameterIndex] = Math.Max(rankRhat, foldedRhat); + } + + return rhat; + } + + /// + /// Adds one chain's original-scale autocorrelation to the retained plotting output. + /// + /// Common-length parameter draws for one chain. + /// The 51-row output accumulator. + /// Number of chains contributing to the average. + private static void AccumulateAverageAutocorrelation( + double[] values, + double[,] averageAutocorrelation, + int chainCount) + { + if (values.Length < 2) + return; + + int maximumLag = Math.Min(50, values.Length - 1); + double[,]? autocorrelation = Fourier.Autocorrelation(values, maximumLag); + if (autocorrelation == null) + return; + + for (int lag = 0; lag < autocorrelation.GetLength(0); lag++) + averageAutocorrelation[lag, 1] += autocorrelation[lag, 1] / chainCount; + } + + /// + /// Computes the minimum of rank-normalized bulk and two quantile-indicator ESS values. + /// + /// Original-scale chains with a common length. + /// The conservative scalar ESS, or . + private static double ComputeConservativeEffectiveSampleSize(double[][] chains) + { + if (chains.Length == 0 || chains[0].Length < 6 || ShouldReturnNaN(chains)) + return double.NaN; + + double bulk = ComputeGeyerEffectiveSampleSize(RankNormalize(SplitChains(chains))); + double lowerTail = ComputeQuantileEffectiveSampleSize(chains, 0.05d); + double upperTail = ComputeQuantileEffectiveSampleSize(chains, 0.95d); + if (!Tools.IsFinite(bulk) || !Tools.IsFinite(lowerTail) || !Tools.IsFinite(upperTail)) + return double.NaN; + + return Math.Min(bulk, Math.Min(lowerTail, upperTail)); + } + + /// + /// Computes ESS for an indicator at a pooled sample quantile. + /// + /// Original-scale chains. + /// Quantile probability. + /// The split-chain indicator ESS. + private static double ComputeQuantileEffectiveSampleSize(double[][] chains, double probability) + { + double threshold = Quantile(Flatten(chains), probability); + var indicators = new double[chains.Length][]; + for (int chainIndex = 0; chainIndex < chains.Length; chainIndex++) + { + indicators[chainIndex] = new double[chains[chainIndex].Length]; + for (int iteration = 0; iteration < chains[chainIndex].Length; iteration++) + indicators[chainIndex][iteration] = chains[chainIndex][iteration] <= threshold ? 1d : 0d; + } + + return ComputeGeyerEffectiveSampleSize(SplitChains(indicators)); + } + + /// + /// Computes multi-chain ESS using posterior's FFT autocovariance and Geyer sequence. + /// + /// Transformed chains with a common length. + /// The effective sample size, which may exceed the draw count for negative correlation. + private static double ComputeGeyerEffectiveSampleSize(double[][] chains) + { + int chainCount = chains.Length; + int iterationCount = chains[0].Length; + if (iterationCount < 3 || ShouldReturnNaN(chains)) + return double.NaN; + + var averageAutocovariance = new double[iterationCount]; + var chainMeans = new double[chainCount]; + for (int chainIndex = 0; chainIndex < chainCount; chainIndex++) + { + chainMeans[chainIndex] = chains[chainIndex].Average(); + double[] autocovariance = Autocovariance(chains[chainIndex], chainMeans[chainIndex]); + for (int lag = 0; lag < iterationCount; lag++) + averageAutocovariance[lag] += autocovariance[lag] / chainCount; + } + + double meanVariance = averageAutocovariance[0] * iterationCount / (iterationCount - 1d); + double variancePlus = meanVariance * (iterationCount - 1d) / iterationCount; + if (chainCount > 1) + variancePlus += SampleVariance(chainMeans); + if (!Tools.IsFinite(variancePlus) || variancePlus <= 0d) + return double.NaN; + + var rho = new double[iterationCount]; + int sequenceIndex = 0; + double rhoEven = 1d; + rho[0] = rhoEven; + double rhoOdd = 1d - (meanVariance - averageAutocovariance[1]) / variancePlus; + rho[1] = rhoOdd; + while (sequenceIndex < iterationCount - 5 && + Tools.IsFinite(rhoEven + rhoOdd) && + rhoEven + rhoOdd > 0d) + { + sequenceIndex += 2; + rhoEven = 1d - (meanVariance - averageAutocovariance[sequenceIndex]) / variancePlus; + rhoOdd = 1d - (meanVariance - averageAutocovariance[sequenceIndex + 1]) / variancePlus; + if (rhoEven + rhoOdd >= 0d) { - double sum = 0; - for (int j = startIndex; j < N; j++) - { - sum += Tools.Sqr(markovChains[i][j].Values[p] - chainMeans[i]); - } - // within-chain variance - W += sum / (n - 1); - // between-chain variance - B += Tools.Sqr(chainMeans[i] - overallMean); + rho[sequenceIndex] = rhoEven; + rho[sequenceIndex + 1] = rhoOdd; + } + } + + int maximumIndex = sequenceIndex; + if (rhoEven > 0d) + rho[maximumIndex] = rhoEven; + + sequenceIndex = 0; + while (sequenceIndex <= maximumIndex - 4) + { + sequenceIndex += 2; + double previousPair = rho[sequenceIndex - 2] + rho[sequenceIndex - 1]; + double currentPair = rho[sequenceIndex] + rho[sequenceIndex + 1]; + if (currentPair > previousPair) + { + rho[sequenceIndex] = previousPair / 2d; + rho[sequenceIndex + 1] = rho[sequenceIndex]; } - // Set between- and within-chain variance - W /= M; - B *= n / (double)(M - 1); + } - // Step 3. Compute the pooled variance - double V = ((n - 1d) / n) * W + (1d / n) * B; + int sumLength = Math.Max(maximumIndex, 1); + double rhoSum = 0d; + for (int lag = 0; lag < sumLength; lag++) + rhoSum += rho[lag]; + double integratedAutocorrelationTime = + -1d + 2d * rhoSum + rho[maximumIndex]; + double totalDraws = chainCount * (double)iterationCount; + double lowerBound = 1d / Math.Log10(totalDraws); + if (integratedAutocorrelationTime < lowerBound) + integratedAutocorrelationTime = lowerBound; + + return totalDraws / integratedAutocorrelationTime; + } + + /// + /// Computes biased lag autocovariances using a zero-padded FFT. + /// + /// One transformed chain. + /// Precomputed chain mean. + /// Autocovariances for every nonnegative lag, divided by chain length. + private static double[] Autocovariance(double[] values, double mean) + { + int transformLength = 1; + while (transformLength < 2 * values.Length) + transformLength <<= 1; - // Step 4. Compute R-hat - Rhat[p] = Math.Sqrt(V / W); + var centered = new double[transformLength]; + for (int index = 0; index < values.Length; index++) + centered[index] = values[index] - mean; + double[] correlation = Fourier.Correlation(centered, centered); + var autocovariance = new double[values.Length]; + for (int lag = 0; lag < values.Length; lag++) + autocovariance[lag] = correlation[lag] / values.Length; + return autocovariance; + } + + /// + /// Splits every chain in half, discarding the middle draw when its length is odd. + /// + /// Original chains with a common length. + /// Twice as many chains, each with half the original length. + private static double[][] SplitChains(double[][] chains) + { + int halfLength = chains[0].Length / 2; + var split = new double[chains.Length * 2][]; + for (int chainIndex = 0; chainIndex < chains.Length; chainIndex++) + { + split[chainIndex] = new double[halfLength]; + split[chainIndex + chains.Length] = new double[halfLength]; + Array.Copy(chains[chainIndex], 0, split[chainIndex], 0, halfLength); + Array.Copy( + chains[chainIndex], + chains[chainIndex].Length - halfLength, + split[chainIndex + chains.Length], + 0, + halfLength); } + return split; + } - return Rhat; + /// + /// Rank-normalizes pooled chain values using average ranks and Blom's offset. + /// + /// Chains to rank as one pooled collection. + /// Chains with values replaced by standard-normal rank scores. + private static double[][] RankNormalize(double[][] chains) + { + double[] flattened = Flatten(chains); + var order = Enumerable.Range(0, flattened.Length) + .OrderBy(index => flattened[index]) + .ToArray(); + var ranks = new double[flattened.Length]; + int start = 0; + while (start < order.Length) + { + int end = start + 1; + while (end < order.Length && flattened[order[end]].Equals(flattened[order[start]])) + end++; + double averageRank = (start + 1d + end) / 2d; + for (int index = start; index < end; index++) + ranks[order[index]] = averageRank; + start = end; + } + + var normalized = new double[chains.Length][]; + int flatIndex = 0; + double denominator = flattened.Length - 2d * RankOffset + 1d; + for (int chainIndex = 0; chainIndex < chains.Length; chainIndex++) + { + normalized[chainIndex] = new double[chains[chainIndex].Length]; + for (int iteration = 0; iteration < chains[chainIndex].Length; iteration++) + { + double probability = (ranks[flatIndex] - RankOffset) / denominator; + normalized[chainIndex][iteration] = Normal.StandardZ(probability); + flatIndex++; + } + } + return normalized; + } + + /// + /// Folds pooled draws around their median. + /// + /// Original-scale chains. + /// Absolute deviations from the pooled median. + private static double[][] FoldAroundMedian(double[][] chains) + { + double median = Quantile(Flatten(chains), 0.5d); + var folded = new double[chains.Length][]; + for (int chainIndex = 0; chainIndex < chains.Length; chainIndex++) + { + folded[chainIndex] = new double[chains[chainIndex].Length]; + for (int iteration = 0; iteration < chains[chainIndex].Length; iteration++) + folded[chainIndex][iteration] = Math.Abs(chains[chainIndex][iteration] - median); + } + return folded; + } + + /// + /// Computes the basic between/within-chain R-hat for transformed split chains. + /// + /// Transformed split chains. + /// The basic R-hat, or for unusable input. + private static double ComputeBasicRhat(double[][] chains) + { + if (chains.Length < 2 || chains[0].Length < 2 || ShouldReturnNaN(chains)) + return double.NaN; + + int iterationCount = chains[0].Length; + var means = chains.Select(chain => chain.Average()).ToArray(); + double within = chains.Average(SampleVariance); + if (!Tools.IsFinite(within) || within <= 0d) + return double.NaN; + double between = iterationCount * SampleVariance(means); + return Math.Sqrt((between / within + iterationCount - 1d) / iterationCount); + } + + /// + /// Computes sample variance with denominator n-1. + /// + /// Values to evaluate. + /// The sample variance, or for fewer than two values. + private static double SampleVariance(double[] values) + { + if (values.Length < 2) + return double.NaN; + double mean = values.Average(); + double sum = 0d; + for (int index = 0; index < values.Length; index++) + sum += Tools.Sqr(values[index] - mean); + return sum / (values.Length - 1d); + } + + /// + /// Flattens chains in column-major chain order matching an iterations-by-chains R matrix. + /// + /// Chains to flatten. + /// All values with each chain contiguous. + private static double[] Flatten(double[][] chains) + { + int total = chains.Sum(chain => chain.Length); + var flattened = new double[total]; + int offset = 0; + foreach (double[] chain in chains) + { + Array.Copy(chain, 0, flattened, offset, chain.Length); + offset += chain.Length; + } + return flattened; + } + + /// + /// Computes the R type-7 sample quantile. + /// + /// Pooled sample values. + /// Probability in the closed unit interval. + /// The interpolated sample quantile. + private static double Quantile(double[] values, double probability) + { + var sorted = values.OrderBy(value => value).ToArray(); + double position = (sorted.Length - 1d) * probability; + int lower = (int)Math.Floor(position); + int upper = (int)Math.Ceiling(position); + if (lower == upper) + return sorted[lower]; + return sorted[lower] + (position - lower) * (sorted[upper] - sorted[lower]); + } + + /// + /// Determines whether pooled diagnostic input is nonfinite or effectively constant. + /// + /// Chains to inspect. + /// when the diagnostic must return NaN. + private static bool ShouldReturnNaN(double[][] chains) + { + double minimum = double.PositiveInfinity; + double maximum = double.NegativeInfinity; + foreach (double[] chain in chains) + { + foreach (double value in chain) + { + if (!Tools.IsFinite(value)) + return true; + minimum = Math.Min(minimum, value); + maximum = Math.Max(maximum, value); + } + } + return Math.Abs(maximum - minimum) < MachineEpsilon; } /// diff --git a/Test_Numerics/Sampling/MCMC/Test_MCMCDiagnostics.cs b/Test_Numerics/Sampling/MCMC/Test_MCMCDiagnostics.cs index 77c2eec2..9d01bd08 100644 --- a/Test_Numerics/Sampling/MCMC/Test_MCMCDiagnostics.cs +++ b/Test_Numerics/Sampling/MCMC/Test_MCMCDiagnostics.cs @@ -9,6 +9,7 @@ namespace Sampling.MCMC /// /// Unit tests for MCMCDiagnostics, particularly GelmanRubin R-hat with warmup. /// + /// Reference values use R posterior 1.7.0. [TestClass] public class Test_MCMCDiagnostics { @@ -58,6 +59,86 @@ public void Test_GelmanRubin_WithWarmup() "R-hat with warmup should be closer to 1.0 than without warmup"); } + /// + /// Verifies rank-normalized R-hat and conservative ESS against R posterior 1.7.0. + /// + /// + /// R reference: rhat=0.98937039497892809, bulk ESS=616.50943111983349, + /// lower-tail ESS=319.75259831061169, and upper-tail/scalar ESS=306.3117099147583. + /// + [TestMethod] + public void Test_ModernDiagnostics_MatchRPosteriorReference() + { + List> chains = CreateModuloFixture(); + + double actualRhat = MCMCDiagnostics.GelmanRubin(chains)[0]; + double actualEss = MCMCDiagnostics.EffectiveSampleSize(chains, out double[][,] averageAcf)[0]; + + Assert.AreEqual(0.98937039497892809, actualRhat, 1E-10, + "Rank-normalized split/folded R-hat must match R posterior 1.7.0."); + Assert.AreEqual(306.3117099147583, actualEss, 1E-8, + "The existing scalar ESS must equal min(bulk, lower-tail, upper-tail) from R posterior."); + Assert.AreEqual(51, averageAcf[0].GetLength(0)); + Assert.AreEqual(2, averageAcf[0].GetLength(1)); + Assert.AreEqual(1d, averageAcf[0][0, 1], 1E-12, + "The existing original-scale ACF plotting output must be preserved."); + } + + /// + /// Verifies folded rank-normalized R-hat detects equal-center scale disagreement. + /// + /// R posterior 1.7.0 returns 1.2810140532084646 for this fixture. + [TestMethod] + public void Test_GelmanRubin_FoldedRanksDetectScaleMismatch() + { + double[] scales = { 0.5d, 0.5d, 2d, 2d }; + var chains = new List>(); + for (int chainIndex = 0; chainIndex < scales.Length; chainIndex++) + { + var chain = new List(); + for (int iteration = 1; iteration <= 100; iteration++) + { + double centered = ((iteration * 37 + chainIndex * 13) % 101 - 50) / 10d; + chain.Add(new ParameterSet(new[] { scales[chainIndex] * centered }, 0d)); + } + chains.Add(chain); + } + + double actual = MCMCDiagnostics.GelmanRubin(chains)[0]; + + Assert.AreEqual(1.2810140532084646, actual, 1E-10); + Assert.IsGreaterThan(1.01, actual, + "Folded R-hat must flag scale disagreement even when chain centers agree."); + } + + /// + /// Verifies modern diagnostic edge and compatibility behavior. + /// + [TestMethod] + public void Test_ModernDiagnostics_EdgeCases() + { + var constantChains = new List>(); + for (int chainIndex = 0; chainIndex < 4; chainIndex++) + { + var chain = new List(); + for (int iteration = 0; iteration < 20; iteration++) + chain.Add(new ParameterSet(new[] { 1d }, 0d)); + constantChains.Add(chain); + } + + Assert.IsTrue(double.IsNaN(MCMCDiagnostics.GelmanRubin(constantChains)[0])); + Assert.IsTrue(double.IsNaN( + MCMCDiagnostics.EffectiveSampleSize(constantChains, out _)[0])); + Assert.IsTrue(double.IsNaN(MCMCDiagnostics.EffectiveSampleSize(new[] { 1d, 2d, 3d }))); + + List> fixture = CreateModuloFixture(); + double original = MCMCDiagnostics.EffectiveSampleSize(fixture, out _)[0]; + fixture.Reverse(); + double permuted = MCMCDiagnostics.EffectiveSampleSize(fixture, out _)[0]; + Assert.AreEqual(original, permuted, 1E-10, + "ESS must be invariant to chain ordering."); + } + /// /// Verify GelmanRubin handles edge cases. /// @@ -72,5 +153,26 @@ public void Test_GelmanRubin_EdgeCases() var result = MCMCDiagnostics.GelmanRubin(singleChain); Assert.IsTrue(double.IsNaN(result[0]), "Single chain should return NaN"); } + + /// + /// Creates the compact deterministic fixture evaluated by R posterior 1.7.0. + /// + /// Four 64-draw chains containing one parameter. + private static List> CreateModuloFixture() + { + var chains = new List>(); + for (int chainIndex = 0; chainIndex < 4; chainIndex++) + { + var chain = new List(); + for (int iteration = 1; iteration <= 64; iteration++) + { + double value = ((iteration * 17 + chainIndex * 11) % 31) / 10d + + chainIndex * 0.05d; + chain.Add(new ParameterSet(new[] { value }, 0d)); + } + chains.Add(chain); + } + return chains; + } } } From 556d70fd4cdef337177f92c60b8b9b291b74374e Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 27 Jul 2026 08:27:32 -0600 Subject: [PATCH 023/222] Fix composite configured-child evaluation --- Numerics/Functions/CompositeFunction.cs | 25 +++++++++++++++++-- .../Functions/Test_CompositeFunction.cs | 19 ++++++++++++++ Test_Numerics/Test_CorrectnessRepairs.cs | 10 +++++--- 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/Numerics/Functions/CompositeFunction.cs b/Numerics/Functions/CompositeFunction.cs index 7be0dadd..f5844da3 100644 --- a/Numerics/Functions/CompositeFunction.cs +++ b/Numerics/Functions/CompositeFunction.cs @@ -273,10 +273,12 @@ public double Function(double x) if (IsDeterministic == true || ConfidenceLevel < 0 || ConfidenceLevel > 1) { - // Evaluate each child using the shared mean convention. + // The parent mean sentinel preserves each child's configured realization. Locking + // prevents a concurrent confidence-driven composite from exposing its temporary + // child level to this evaluation. double mean = 0d; for (int i = 0; i < _functions.Length; i++) - mean += _weights[i] * EvaluateChildAt(i, -1d, x); + mean += _weights[i] * EvaluateConfiguredChild(i, x); return mean; } @@ -440,6 +442,25 @@ private int SelectMixtureChild(double u, out double remainder) return lastPositive; } + /// + /// Evaluates a child at its configured confidence level without changing that state. + /// + /// The child index. + /// The evaluation point. + /// The child's value at its configured realization. + private double EvaluateConfiguredChild(int index, double x) + { + var child = _functions[index]; + if (child.IsDeterministic) + return child.Function(x); + + object syncRoot = ChildLocks.GetValue(child, key => new object()); + lock (syncRoot) + { + return child.Function(x); + } + } + /// /// Evaluates a child at a given confidence level without disturbing the child's /// configured state (the level is restored after the call). diff --git a/Test_Numerics/Functions/Test_CompositeFunction.cs b/Test_Numerics/Functions/Test_CompositeFunction.cs index 8eceecaa..fcbb5b50 100644 --- a/Test_Numerics/Functions/Test_CompositeFunction.cs +++ b/Test_Numerics/Functions/Test_CompositeFunction.cs @@ -36,6 +36,25 @@ public void Test_WeightedAverage_Deterministic() Assert.AreEqual(5d, composite.InverseFunction(composite.Function(5d)), 1E-8); } + /// + /// Test that the parent mean sentinel combines independently configured child + /// realizations without replacing their confidence levels. + /// + [TestMethod] + public void Test_WeightedAverage_MeanSentinelUsesConfiguredChildLevels() + { + var first = new LinearFunction(0d, 1d, 2d) { ConfidenceLevel = 0.2d }; + var second = new LinearFunction(10d, 1d, 3d) { ConfidenceLevel = 0.8d }; + var composite = new CompositeFunction( + new IUnivariateFunction[] { first, second }, new[] { 0.25d, 0.75d }); + double expected = 0.25d * first.Function(5d) + 0.75d * second.Function(5d); + + Assert.AreEqual(expected, composite.Function(5d), 1E-12); + Assert.AreEqual(5d, composite.InverseFunction(expected), 1E-8); + Assert.AreEqual(0.2d, first.ConfidenceLevel, 0d); + Assert.AreEqual(0.8d, second.ConfidenceLevel, 0d); + } + /// /// Test the mixture composition: one uniform selects the child by cumulative weight and /// re-scales the remainder as the child's own draw; the mean convention (a confidence diff --git a/Test_Numerics/Test_CorrectnessRepairs.cs b/Test_Numerics/Test_CorrectnessRepairs.cs index fd52f2a1..b47d6ea2 100644 --- a/Test_Numerics/Test_CorrectnessRepairs.cs +++ b/Test_Numerics/Test_CorrectnessRepairs.cs @@ -70,12 +70,16 @@ public void Composite_SynchronizesOnlyStatefulChildren() var child = new ConfidenceProbeFunction { ConfidenceLevel = 0.5d }; var lower = new CompositeFunction(new IUnivariateFunction[] { child }) { ConfidenceLevel = 0.2d }; var upper = new CompositeFunction(new IUnivariateFunction[] { child }) { ConfidenceLevel = 0.8d }; + var configured = new CompositeFunction(new IUnivariateFunction[] { child }); int failures = 0; - Parallel.For(0, 400, i => + Parallel.For(0, 600, i => { - double expected = i % 2 == 0 ? 0.2d : 0.8d; - double actual = i % 2 == 0 ? lower.Function(0d) : upper.Function(0d); + int branch = i % 3; + double expected = branch == 0 ? 0.2d : branch == 1 ? 0.8d : 0.5d; + double actual = branch == 0 ? lower.Function(0d) + : branch == 1 ? upper.Function(0d) + : configured.Function(0d); if (actual != expected) Interlocked.Increment(ref failures); }); From 542faf3b4cd5521472357ab94cc950edadee3436 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 27 Jul 2026 08:50:09 -0600 Subject: [PATCH 024/222] Complete XML documentation contracts --- Numerics/Data/Statistics/Probability.cs | 15 ++++- Numerics/Data/Statistics/Statistics.cs | 4 ++ .../Multivariate/MultivariateNormal.cs | 59 ++++++++++--------- .../Univariate/EmpiricalDistribution.cs | 22 +++++++ .../Distributions/Univariate/KernelDensity.cs | 28 ++++++++- .../Uncertainty Analysis/BootstrapAnalysis.cs | 25 ++++++++ Numerics/Functions/CompositeFunction.cs | 7 ++- Numerics/Functions/EnsembleFunction.cs | 1 + Numerics/Functions/SegmentedPowerFunction.cs | 4 +- Numerics/Mathematics/Integration/Vegas.cs | 4 ++ .../Special Functions/Factorial.cs | 8 ++- Test_Numerics/Test_CorrectnessRepairs.cs | 47 +++++++++++++++ 12 files changed, 190 insertions(+), 34 deletions(-) diff --git a/Numerics/Data/Statistics/Probability.cs b/Numerics/Data/Statistics/Probability.cs index 5597c0f2..aa827702 100644 --- a/Numerics/Data/Statistics/Probability.cs +++ b/Numerics/Data/Statistics/Probability.cs @@ -1055,7 +1055,10 @@ public static double[] IndependentExclusive(IList probabilities) /// Output. A list of event indicators that correspond to the probabilities in the eventProbabilities list. /// The absolute tolerance for evaluation convergence of the inclusion-exclusion algorithm. Default = 1E-4. /// The relative tolerance for evaluation convergence of the inclusion-exclusion algorithm. Default = 1E-4. - /// Thrown if the probabilities array is null, empty, or if the lengths of the probabilities and indicators arrays do not match. + /// Thrown when the binomial-combination metadata is null. + /// Thrown when probabilities are missing or the combination and indicator metadata is structurally inconsistent. + /// Thrown when a probability or tolerance is outside its valid range. + /// Thrown when a required combination count exceeds . /// /// This method uses the inclusion-exclusion principle to compute the exclusive probability of each event combination. /// The result is added to a list, and convergence is monitored using the specified tolerances. @@ -1198,6 +1201,15 @@ void TrimToUsed() /// /// Validates the structural metadata used by the pooled exclusive-probability overload. /// + /// The event probabilities. + /// The expected combination count for each subset size. + /// The materialized indicator rows. + /// The absolute convergence tolerance. + /// The relative convergence tolerance. + /// Thrown when is null. + /// Thrown when the combination counts or indicator dimensions are inconsistent. + /// Thrown when a probability or tolerance is outside its valid range. + /// Thrown when a required combination count exceeds . private static void ValidatePooledExclusiveMetadata(IList probabilities, int[] binomialCombinations, int[,] indicators, double absoluteTolerance, double relativeTolerance) { @@ -1256,6 +1268,7 @@ public enum ExclusiveEnumerationStatus /// Whether the expansion completed, converged early, or hit the cap. /// Thrown when either output list is null. /// Thrown when the probabilities list is null or empty. + /// Thrown when a probability or tolerance is outside its valid range. /// /// Emits the same rows, in the same order, with the same probabilities as the dense /// overload. On convergence it closes with the same half-gap pseudo-row; at the cap it diff --git a/Numerics/Data/Statistics/Statistics.cs b/Numerics/Data/Statistics/Statistics.cs index 9f27321e..57a058a4 100644 --- a/Numerics/Data/Statistics/Statistics.cs +++ b/Numerics/Data/Statistics/Statistics.cs @@ -317,6 +317,8 @@ public static double Skewness(IList data) /// Sample data. /// The statistic evaluated on isolated sample arrays. /// The jackknife standard error. + /// Thrown when or is null. + /// A single-element sample returns zero without invoking . Parallel callbacks receive independent arrays. public static double JackKnifeStandardError(IList data, Func, double> statistic) { if (data == null) throw new ArgumentNullException(nameof(data)); @@ -360,6 +362,8 @@ public static double JackKnifeStandardError(IList data, FuncSample data. /// The statistic evaluated on isolated leave-one-out arrays. /// The statistic values, or for an empty input sample. + /// Thrown when or is null. + /// Each callback receives an independent leave-one-out array; a single-element sample therefore supplies one empty array. public static double[]? JackKnifeSample(IList data, Func, double> statistic) { if (data == null) throw new ArgumentNullException(nameof(data)); diff --git a/Numerics/Distributions/Multivariate/MultivariateNormal.cs b/Numerics/Distributions/Multivariate/MultivariateNormal.cs index d59a4c20..c9f2c879 100644 --- a/Numerics/Distributions/Multivariate/MultivariateNormal.cs +++ b/Numerics/Distributions/Multivariate/MultivariateNormal.cs @@ -26,6 +26,9 @@ namespace Numerics.Distributions [Serializable] public class MultivariateNormal : MultivariateDistribution { + /// + /// Initializes an empty instance for internal cloning and deserialization workflows. + /// private MultivariateNormal() { } /// @@ -95,6 +98,7 @@ public MultivariateNormal(double[] mean, double[,] covariance) /// generator to tie results to a caller's own seed. Not thread-safe — MVNDST advances the /// generator, so an instance shared across threads must be cloned per thread. /// + /// Thrown when the assigned generator is null. public Random MVNUNI { get { return _MVNUNI; } @@ -1073,34 +1077,13 @@ public static double bivnor(double ah, double ak, double r) return b; } - //****************************************************************************80 - + /// + /// Evaluates the standard normal cumulative distribution function. + /// + /// The standard normal variate. + /// The lower-tail probability at . + /// Adapted from the MIT-licensed normal-tail implementation by John Burkardt. private static double gauss(double t) - - //****************************************************************************80 - // - // Purpose: - // - // GAUSS returns the area of the lower tail of the normal curve. - // - // Licensing: - // - // This code is distributed under the MIT license. - // - // Modified: - // - // 13 April 2012 - // - // Author: - // - // John Burkardt - // - // Parameters: - // - // Input, double T, the evaluation point. - // - // Output, double GAUSS, the lower normal tail area. - // { double value; @@ -1585,6 +1568,14 @@ private void MVNDNT(int N, double[] CORREL, double[] LOWER, double[] UPPER, int[ } } + /// + /// Converts an interval's standardized limits to lower- and upper-tail probabilities. + /// + /// The standardized lower limit. + /// The standardized upper limit. + /// The bound flag: 0 for lower-infinite, 1 for upper-infinite, 2 for finite bounds, or a negative value for an unbounded interval. + /// Receives the lower cumulative probability. + /// Receives the upper cumulative probability, constrained to be at least . private void MVNLMS(double A, double B, int INFIN, ref double LOWER, ref double UPPER) { LOWER = 0; @@ -1988,6 +1979,20 @@ private void DKBVRC(int NDIM, int MINVLS, int MAXVLS, Func + /// Evaluates one randomized, antithetic lattice-rule estimate of a multidimensional integral. + /// + /// The integration dimension. + /// The maximum number of lattice-generator components to randomize. + /// Receives the running lattice-rule mean. + /// The number of lattice points. + /// The lattice-generator vector, randomized in place. + /// The transformed integrand. + /// The coordinate and random-shift workspace. + /// + /// must contain at least entries and + /// at least twice that many. Random shifts are drawn from . + /// private void DKSMRC(int NDIM, int KLIM, ref double SUMKRO, int PRIME, ref double[] VK, Func FUNCTN, ref double[] X) { double sampleValue; diff --git a/Numerics/Distributions/Univariate/EmpiricalDistribution.cs b/Numerics/Distributions/Univariate/EmpiricalDistribution.cs index 830c8c9e..4539fd01 100644 --- a/Numerics/Distributions/Univariate/EmpiricalDistribution.cs +++ b/Numerics/Distributions/Univariate/EmpiricalDistribution.cs @@ -694,6 +694,8 @@ public static void ConvolveDiscrete(IList values1, IList masses1 /// The atom masses. /// The reported parameter name. /// The finite positive total mass. + /// Thrown when or is null. + /// Thrown when the atom collections are empty, mismatched, non-finite, negative, or have no positive total mass. private static double ValidateAtoms(IList values, IList masses, string parameterName) { if (values == null || masses == null) throw new ArgumentNullException(parameterName); @@ -712,6 +714,13 @@ private static double ValidateAtoms(IList values, IList masses, return total; } + /// + /// Finds the smallest atom value carrying positive mass. + /// + /// The validated atom values. + /// The validated atom masses. + /// The smallest value whose corresponding mass is positive. + /// The atom collections must first pass . private static double MinimumPositiveMassValue(IList values, IList masses) { double minimum = double.MaxValue; @@ -720,6 +729,13 @@ private static double MinimumPositiveMassValue(IList values, IList + /// Finds the largest atom value carrying positive mass. + /// + /// The validated atom values. + /// The validated atom masses. + /// The largest value whose corresponding mass is positive. + /// The atom collections must first pass . private static double MaximumPositiveMassValue(IList values, IList masses) { double maximum = double.MinValue; @@ -728,6 +744,12 @@ private static double MaximumPositiveMassValue(IList values, IList + /// Finds the final lattice entry carrying positive mass. + /// + /// The lattice masses. + /// The index of the final positive mass. + /// Thrown when the lattice has no positive mass. private static int LastPositiveIndex(IList masses) { for (int i = masses.Count - 1; i >= 0; i--) diff --git a/Numerics/Distributions/Univariate/KernelDensity.cs b/Numerics/Distributions/Univariate/KernelDensity.cs index aec52f06..9181e16e 100644 --- a/Numerics/Distributions/Univariate/KernelDensity.cs +++ b/Numerics/Distributions/Univariate/KernelDensity.cs @@ -49,6 +49,9 @@ public KernelDensity() /// Constructs a Gaussian Kernel Density distribution from a sample of data using the default bandwidth. /// /// Sample of data, no sorting is assumed. + /// Thrown when is null. + /// Thrown when the sample is empty or contains a non-finite value. + /// Thrown when the sample produces a non-positive or non-finite default bandwidth. public KernelDensity(IList sampleData) { SetSampleData(sampleData); @@ -61,6 +64,9 @@ public KernelDensity(IList sampleData) /// /// Sample of data, no sorting is assumed. /// The kernel distribution type. + /// Thrown when is null. + /// Thrown when the sample is empty or contains a non-finite value. + /// Thrown when is undefined or the sample produces an invalid default bandwidth. public KernelDensity(IList sampleData, KernelType kernel) { SetSampleData(sampleData); @@ -74,6 +80,9 @@ public KernelDensity(IList sampleData, KernelType kernel) /// Sample of data, no sorting is assumed. /// The kernel distribution type. /// The bandwidth parameter. + /// Thrown when is null. + /// Thrown when the sample is empty or contains a non-finite value. + /// Thrown when is undefined or is not finite and strictly positive. public KernelDensity(IList sampleData, KernelType kernel, double bandwidthParameter) { SetSampleData(sampleData); @@ -88,8 +97,11 @@ public KernelDensity(IList sampleData, KernelType kernel, double bandwid /// Positive weights wᵢ (length must match sampleData). /// Kernel type (default Gaussian). /// - /// Optional bandwidth. If null we use Silverman’s rule with the weighted σ. + /// Optional bandwidth. If null, Silverman’s rule is applied using the weighted standard deviation. /// + /// Thrown when or is null. + /// Thrown when the sample and weights are empty, mismatched, non-finite, negative, or have no positive total weight. + /// Thrown when is undefined or the selected bandwidth is not finite and strictly positive. public KernelDensity(IList sampleData, IList weights, KernelType kernel = KernelType.Gaussian, double? bandwidthParameter = null) { if (sampleData == null) throw new ArgumentNullException(nameof(sampleData)); @@ -146,6 +158,7 @@ public enum KernelType /// /// Gets and sets the kernel distribution type. /// + /// Thrown when the assigned kernel type is undefined. public KernelType KernelDistribution { get { return _kernelDistribution; } @@ -178,6 +191,7 @@ public KernelType KernelDistribution /// /// Gets and sets the bandwidth parameter used in the kernel density estimation. /// + /// Thrown when the assigned bandwidth is not finite and strictly positive. public double Bandwidth { get { return _bandwidth; } @@ -558,8 +572,12 @@ public override void SetParameters(IList parameters) } /// - /// Validate the bandwidth parameter. + /// Validates a bandwidth parameter. /// + /// The bandwidth to validate. + /// Whether to throw the validation error immediately. + /// An error for an invalid bandwidth; otherwise, . + /// Thrown when is invalid and is . private ArgumentOutOfRangeException? ValidateParameters(double value, bool throwException) { if (double.IsNaN(value) || double.IsInfinity(value) || value <= 0d) @@ -575,6 +593,8 @@ public override void SetParameters(IList parameters) /// Set the sample data for the distribution. /// /// Sample of data, no sorting is assumed. + /// Thrown when is null. + /// Thrown when the sample is empty or contains a non-finite value. public void SetSampleData(IList sampleData) { ValidateSampleData(sampleData); @@ -590,6 +610,8 @@ public void SetSampleData(IList sampleData) /// /// Sample of data, no sorting is assumed. /// Weights associated with each data point. + /// Thrown when or is null. + /// Thrown when the sample and weights are empty, mismatched, non-finite, negative, or have no positive total weight. public void SetSampleData(IList sampleData, IList weights) { ValidateSampleData(sampleData); @@ -617,6 +639,8 @@ public void SetSampleData(IList sampleData, IList weights) /// Validates sample data before it is stored by the distribution. /// /// The sample values. + /// Thrown when is null. + /// Thrown when the sample is empty or contains a non-finite value. private static void ValidateSampleData(IList sampleData) { if (sampleData == null) throw new ArgumentNullException(nameof(sampleData)); diff --git a/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs b/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs index 93e9eacb..afc1e159 100644 --- a/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs +++ b/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs @@ -657,6 +657,11 @@ public double[] ComputeMinMaxQuantiles(double minProbability, double maxProbabil /// /// Estimates acceleration constants from successful leave-one-out fits. /// + /// The observed sample. + /// The non-exceedance probabilities. + /// The fitted population quantiles. + /// One acceleration constant per probability. + /// Thrown when every leave-one-out fit fails. private double[] AccelerationConstants(IList sampleData, IList probabilities, IList thetaHats) { int sampleCount = sampleData.Count; @@ -819,6 +824,13 @@ private double[] AccelerationConstants(IList sampleData, IList p /// /// Estimates quantile standard errors using successful inner bootstrap fits. /// + /// The fitted parent distribution used to generate inner samples. + /// The non-exceedance probabilities. + /// The number of inner bootstrap fits. + /// The deterministic random-number seed. + /// One standard error per probability. + /// Thrown when every inner bootstrap fit fails. + /// Thrown when fewer than two finite fits are available for a requested probability. private double[] BootstrapStandardError(UnivariateDistributionBase parentDist, IList probabilities, int replications = 300, int seed = 12345) { var random = new MersenneTwister(seed); @@ -859,6 +871,11 @@ private double[] BootstrapStandardError(UnivariateDistributionBase parentDist, I /// /// Estimates jackknife standard errors from successful leave-one-out fits. /// + /// The observed sample. + /// The non-exceedance probabilities. + /// The cube-root-transformed fitted population quantiles. + /// One jackknife standard error per probability. + /// Thrown when every leave-one-out fit fails. private double[] StandardError(IList sampleData, IList probabilities, IList thetaHats) { int sampleCount = sampleData.Count; @@ -922,6 +939,12 @@ private double[] StandardError(IList sampleData, IList probabili /// /// Returns finite results from successful fits and enforces a minimum sample count. /// + /// The fit results, with non-finite entries representing failed fits. + /// The operation name included in failure messages. + /// The minimum number of finite results required. + /// A new array containing only finite fit results. + /// Thrown when is null. + /// Thrown when fewer than fits succeeded. private static double[] FiniteValuesOrThrow(double[] values, string operation, int minimumCount = 1) { var successfulValues = values.Where(Tools.IsFinite).ToArray(); @@ -933,6 +956,8 @@ private static double[] FiniteValuesOrThrow(double[] values, string operation, i /// /// Computes the real cube root while preserving the sign of negative values. /// + /// The value whose real cube root is required. + /// The sign-preserving real cube root. private static double CubeRoot(double value) { if (value == 0d) return value; diff --git a/Numerics/Functions/CompositeFunction.cs b/Numerics/Functions/CompositeFunction.cs index f5844da3..a7a2f1ac 100644 --- a/Numerics/Functions/CompositeFunction.cs +++ b/Numerics/Functions/CompositeFunction.cs @@ -44,7 +44,7 @@ public class CompositeFunction : IUnivariateFunction /// /// The child functions. /// Thrown when is null. - /// Thrown when no child functions are supplied. + /// Thrown when no child functions are supplied or a child entry is null. public CompositeFunction(IList functions) { if (functions == null) throw new ArgumentNullException(nameof(functions)); @@ -68,7 +68,8 @@ public CompositeFunction(IList functions) /// The child functions. /// The non-negative weights; must sum to one. /// Thrown when either argument is null. - /// Thrown when the collections are empty or mismatched in length. + /// Thrown when the collections are empty or mismatched, or a child entry is null. + /// Thrown when a weight is non-finite or negative, or the weights do not sum to one. public CompositeFunction(IList functions, IList weights) { if (functions == null) throw new ArgumentNullException(nameof(functions)); @@ -103,6 +104,7 @@ public CompositeFunction(IList functions, IList wei /// /// The combination mode. Default = weighted average. /// + /// Thrown when the assigned mode is undefined. public CompositeFunctionMode Mode { get { return _mode; } @@ -418,6 +420,7 @@ public static CompositeFunction FromXElement(XElement xElement) /// The composite's uniform draw in [0, 1]. /// The re-scaled child draw. /// The selected child index. + /// Thrown when no child has positive weight. private int SelectMixtureChild(double u, out double remainder) { double cumulative = 0d; diff --git a/Numerics/Functions/EnsembleFunction.cs b/Numerics/Functions/EnsembleFunction.cs index d15e5499..afb805fd 100644 --- a/Numerics/Functions/EnsembleFunction.cs +++ b/Numerics/Functions/EnsembleFunction.cs @@ -178,6 +178,7 @@ public static EnsembleFunction FromXElement(XElement xElement) /// /// The serialized parameter set. /// The parsed parameter set. + /// Thrown when is null. /// Thrown when a value is missing, malformed, or non-finite. private static ParameterSet ParseParameterSet(XElement element) { diff --git a/Numerics/Functions/SegmentedPowerFunction.cs b/Numerics/Functions/SegmentedPowerFunction.cs index ab4db081..4fe12e3f 100644 --- a/Numerics/Functions/SegmentedPowerFunction.cs +++ b/Numerics/Functions/SegmentedPowerFunction.cs @@ -125,6 +125,7 @@ public double Minimum } /// + /// Thrown when the assigned maximum is not finite or is not greater than . public double Maximum { get { return _maximum; } @@ -351,7 +352,8 @@ public XElement ToXElement() /// The XElement to deserialize. /// A new . /// Thrown when is null. - /// Thrown when the element carries no parseable parameter vector. + /// Thrown when the serialized parameter vector is missing or malformed. + /// Thrown when the serialized parameters or support are outside the function's valid range. public static SegmentedPowerFunction FromXElement(XElement xElement) { if (xElement == null) throw new ArgumentNullException(nameof(xElement)); diff --git a/Numerics/Mathematics/Integration/Vegas.cs b/Numerics/Mathematics/Integration/Vegas.cs index 8c4d694e..9f844854 100644 --- a/Numerics/Mathematics/Integration/Vegas.cs +++ b/Numerics/Mathematics/Integration/Vegas.cs @@ -259,6 +259,8 @@ private void InitializeParameters() /// p' = 1 - (1-p)^γ concentrates samples in upper tail when γ > 1. /// When γ = 1, the transform is the identity. /// + /// The unit-interval probability to transform. + /// The tail-focused probability. private double ApplyPowerTransform(double p) { double gamma = TailFocusParameter; @@ -278,6 +280,8 @@ private double ApplyPowerTransform(double p) /// This weight correction ensures unbiased integration. /// When γ = 1, returns 1.0 (identity Jacobian). /// + /// The untransformed unit-interval probability. + /// The derivative of the power transform at . private double PowerTransformJacobian(double p) { double gamma = TailFocusParameter; diff --git a/Numerics/Mathematics/Special Functions/Factorial.cs b/Numerics/Mathematics/Special Functions/Factorial.cs index 3c059626..f68e852d 100644 --- a/Numerics/Mathematics/Special Functions/Factorial.cs +++ b/Numerics/Mathematics/Special Functions/Factorial.cs @@ -126,7 +126,8 @@ public static double BinomialCoefficient(int n, int k) /// An array to store the current combination being constructed /// The index of where the next element of the combination should be placed /// Index of where to start adding elements. Ensures each element is only included once - /// The last index (exclusive) to be inlcuded in the current combination + /// The exclusive upper bound for candidate indexes. + /// The combinations generated from the current buffer prefix. private static IEnumerable FindCombosRecursive(int[] buffer, int done, int begin, int end) { for (int i = begin; i < end; i++) @@ -146,6 +147,7 @@ private static IEnumerable FindCombosRecursive(int[] buffer, int done, in /// /// The combination size. /// The overall count. + /// All strictly increasing index combinations of size drawn from items. public static IEnumerable FindCombinations(int m, int n) { return FindCombosRecursive(new int[m], 0, 0, n); @@ -212,6 +214,10 @@ public static bool NextCombination(int[] combination, int n) /// /// Advances a combination that has already been validated. /// + /// The valid strictly increasing index tuple to advance in place. + /// The overall item count. + /// when the tuple is the final combination of its size; otherwise, . + /// This unchecked helper requires the caller to enforce the contract documented by before iteration. internal static bool NextCombinationUnchecked(int[] combination, int n) { int k = combination.Length; diff --git a/Test_Numerics/Test_CorrectnessRepairs.cs b/Test_Numerics/Test_CorrectnessRepairs.cs index b47d6ea2..5152d817 100644 --- a/Test_Numerics/Test_CorrectnessRepairs.cs +++ b/Test_Numerics/Test_CorrectnessRepairs.cs @@ -137,19 +137,42 @@ public void Ensemble_OwnsDeepCopiesAndValidatesXmlSets() Assert.Throws(() => EnsembleFunction.FromXElement(invalidFitness)); } + /// + /// Test function that exposes confidence-state changes and controlled evaluation failures. + /// private sealed class ConfidenceProbeFunction : IUnivariateFunction { + /// The configured confidence level. private double _confidenceLevel = -1d; + /// Gets or sets whether evaluations throw a controlled exception. public bool ThrowOnEvaluation { get; set; } + + /// Gets or sets whether confidence-level assignments throw a controlled exception. public bool ThrowOnConfidenceAssignment { get; set; } + + /// public int NumberOfParameters => 0; + + /// public bool ParametersValid => true; + + /// public double Minimum { get; set; } = double.MinValue; + + /// public double Maximum { get; set; } = double.MaxValue; + + /// public double[] MinimumOfParameters => Array.Empty(); + + /// public double[] MaximumOfParameters => Array.Empty(); + + /// public bool IsDeterministic { get; set; } + + /// public double ConfidenceLevel { get { return _confidenceLevel; } @@ -160,17 +183,35 @@ public double ConfidenceLevel } } + /// + /// Validates that no parameters are supplied to this parameterless test function. + /// + /// The parameter collection, which must be empty. + /// Thrown when is null. + /// Thrown when is not empty. public void SetParameters(IList parameters) { if (parameters == null) throw new ArgumentNullException(nameof(parameters)); if (parameters.Count != 0) throw new ArgumentException("This test function has no parameters.", nameof(parameters)); } + /// + /// Reports that the parameterless test function has no range-validation error. + /// + /// The parameter collection. + /// Ignored because this test function has no parameters. + /// . public ArgumentOutOfRangeException ValidateParameters(IList parameters, bool throwException) { return null; } + /// + /// Returns the configured confidence level after checking for concurrent state changes. + /// + /// The unused evaluation point. + /// The configured confidence level. + /// Thrown when controlled failure is enabled or confidence state changes during evaluation. public double Function(double x) { if (ThrowOnEvaluation) throw new InvalidOperationException("Test evaluation failure."); @@ -180,6 +221,12 @@ public double Function(double x) return ConfidenceLevel; } + /// + /// Returns the configured confidence level as the inverse result. + /// + /// The unused value to invert. + /// The configured confidence level. + /// Thrown when controlled failure is enabled. public double InverseFunction(double y) { if (ThrowOnEvaluation) throw new InvalidOperationException("Test inverse failure."); From 48b67d1661ca2df14330c7273eed5e773ac0109a Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 27 Jul 2026 09:48:16 -0600 Subject: [PATCH 025/222] Restore generic MCMC result contracts --- Numerics/Sampling/MCMC/Base/MCMCSampler.cs | 28 ++- Numerics/Sampling/MCMC/NUTS.cs | 66 ++++---- Numerics/Sampling/MCMC/Support/MCMCResults.cs | 59 +------ .../MCMC/Test_MCMCSamplerDiagnostics.cs | 159 +++++++++++++----- 4 files changed, 162 insertions(+), 150 deletions(-) diff --git a/Numerics/Sampling/MCMC/Base/MCMCSampler.cs b/Numerics/Sampling/MCMC/Base/MCMCSampler.cs index 19628e38..7be26fc0 100644 --- a/Numerics/Sampling/MCMC/Base/MCMCSampler.cs +++ b/Numerics/Sampling/MCMC/Base/MCMCSampler.cs @@ -295,27 +295,19 @@ public enum InitializationType /// /// The acceptance rate per chain. /// - public double[] AcceptanceRates => ComputeAcceptanceRates(); - - /// - /// Computes the sampler-appropriate acceptance statistic for each chain. - /// - /// The acceptance statistic for each chain. - /// - /// Metropolis samplers use accepted proposal counts. Hamiltonian samplers may - /// override this hook when their meaningful statistic is a mean trajectory - /// acceptance probability rather than a binary retained-state count. - /// - protected virtual double[] ComputeAcceptanceRates() + public double[] AcceptanceRates { - var acceptanceRates = new double[NumberOfChains]; - for (int i = 0; i < NumberOfChains; i++) + get { - acceptanceRates[i] = SampleCount[i] > 0 - ? (double)AcceptCount[i] / SampleCount[i] - : 0d; + var acceptanceRates = new double[NumberOfChains]; + for (int i = 0; i < NumberOfChains; i++) + { + acceptanceRates[i] = SampleCount[i] > 0 + ? (double)AcceptCount[i] / SampleCount[i] + : 0d; + } + return acceptanceRates; } - return acceptanceRates; } /// diff --git a/Numerics/Sampling/MCMC/NUTS.cs b/Numerics/Sampling/MCMC/NUTS.cs index f509d36c..1f462c34 100644 --- a/Numerics/Sampling/MCMC/NUTS.cs +++ b/Numerics/Sampling/MCMC/NUTS.cs @@ -129,17 +129,17 @@ public NUTS(List priorDistributions, LogLikelihood logL // Per-chain post-warmup diagnostics. These are streaming accumulators so // diagnostic collection adds no target/gradient evaluations or draw storage. - private double[] _hamiltonianAcceptanceSums = null!; - private int[] _diagnosticSampleCounts = null!; - private int[] _divergenceCounts = null!; - private int[] _maxTreeDepthHitCounts = null!; - private double[] _treeDepthSums = null!; - private double[] _leapfrogStepSums = null!; - private double[] _energyMeans = null!; - private double[] _energyM2 = null!; - private double[] _energySquaredDifferenceSums = null!; - private double[] _previousEnergy = null!; - private bool[] _hasPreviousEnergy = null!; + private double[] _hamiltonianAcceptanceSums = Array.Empty(); + private int[] _diagnosticSampleCounts = Array.Empty(); + private int[] _divergenceCounts = Array.Empty(); + private int[] _maxTreeDepthHitCounts = Array.Empty(); + private double[] _treeDepthSums = Array.Empty(); + private double[] _leapfrogStepSums = Array.Empty(); + private double[] _energyMeans = Array.Empty(); + private double[] _energyM2 = Array.Empty(); + private double[] _energySquaredDifferenceSums = Array.Empty(); + private double[] _previousEnergy = Array.Empty(); + private bool[] _hasPreviousEnergy = Array.Empty(); // Dual averaging hyperparameters (Hoffman & Gelman 2014, Section 3.2) private const double DELTA_TARGET = 0.80; @@ -193,10 +193,14 @@ public double[] HamiltonianAcceptanceRates { get { + if (_diagnosticSampleCounts.Length != NumberOfChains || + _hamiltonianAcceptanceSums.Length != NumberOfChains) + return Array.Empty(); + var values = new double[NumberOfChains]; for (int i = 0; i < NumberOfChains; i++) { - values[i] = _diagnosticSampleCounts == null || _diagnosticSampleCounts[i] == 0 + values[i] = _diagnosticSampleCounts[i] == 0 ? 0d : _hamiltonianAcceptanceSums[i] / _diagnosticSampleCounts[i]; } @@ -207,22 +211,22 @@ public double[] HamiltonianAcceptanceRates /// /// Gets the number of post-warmup transitions contributing diagnostics per chain. /// - public int[] DiagnosticSampleCounts => _diagnosticSampleCounts == null - ? new int[NumberOfChains] + public int[] DiagnosticSampleCounts => _diagnosticSampleCounts.Length != NumberOfChains + ? Array.Empty() : (int[])_diagnosticSampleCounts.Clone(); /// /// Gets the number of divergent post-warmup transitions per chain. /// - public int[] DivergenceCounts => _divergenceCounts == null - ? new int[NumberOfChains] + public int[] DivergenceCounts => _divergenceCounts.Length != NumberOfChains + ? Array.Empty() : (int[])_divergenceCounts.Clone(); /// /// Gets the number of post-warmup transitions that exhausted per chain. /// - public int[] MaxTreeDepthHitCounts => _maxTreeDepthHitCounts == null - ? new int[NumberOfChains] + public int[] MaxTreeDepthHitCounts => _maxTreeDepthHitCounts.Length != NumberOfChains + ? Array.Empty() : (int[])_maxTreeDepthHitCounts.Clone(); /// @@ -239,7 +243,7 @@ public double[] HamiltonianAcceptanceRates /// Gets the current adapted leapfrog step size for each chain. /// public double[] StepSizes => _chainStepSizes == null - ? new double[NumberOfChains] + ? Array.Empty() : (double[])_chainStepSizes.Clone(); /// @@ -253,25 +257,23 @@ public double[] EnergyBayesianFractionOfMissingInformation { get { + if (_diagnosticSampleCounts.Length != NumberOfChains || + _energyM2.Length != NumberOfChains || + _energySquaredDifferenceSums.Length != NumberOfChains) + return Array.Empty(); + var values = new double[NumberOfChains]; for (int i = 0; i < NumberOfChains; i++) { values[i] = ComputeEnergyBayesianFractionOfMissingInformation( - _diagnosticSampleCounts == null ? 0 : _diagnosticSampleCounts[i], - _energyM2 == null ? 0d : _energyM2[i], - _energySquaredDifferenceSums == null ? 0d : _energySquaredDifferenceSums[i]); + _diagnosticSampleCounts[i], + _energyM2[i], + _energySquaredDifferenceSums[i]); } return values; } } - /// - protected override double[] ComputeAcceptanceRates() - { - if (_diagnosticSampleCounts == null || _diagnosticSampleCounts.Length != NumberOfChains) - return base.ComputeAcceptanceRates(); - return HamiltonianAcceptanceRates; - } /// /// Computes per-chain diagnostic means from streaming sums. @@ -280,10 +282,10 @@ protected override double[] ComputeAcceptanceRates() /// The corresponding per-chain means. private double[] ComputeDiagnosticMeans(double[] sums) { - var values = new double[NumberOfChains]; - if (sums == null || _diagnosticSampleCounts == null) - return values; + if (sums.Length != NumberOfChains || _diagnosticSampleCounts.Length != NumberOfChains) + return Array.Empty(); + var values = new double[NumberOfChains]; for (int i = 0; i < NumberOfChains; i++) { if (_diagnosticSampleCounts[i] > 0) diff --git a/Numerics/Sampling/MCMC/Support/MCMCResults.cs b/Numerics/Sampling/MCMC/Support/MCMCResults.cs index 795bc255..1e15fec3 100644 --- a/Numerics/Sampling/MCMC/Support/MCMCResults.cs +++ b/Numerics/Sampling/MCMC/Support/MCMCResults.cs @@ -40,18 +40,9 @@ public MCMCResults(MCMCSampler sampler, double alpha = 0.1) MarkovChains[i] = sampler.MarkovChains[i].ToList(); Output.AddRange(sampler.Output[i].ToList()); } - AcceptanceRates = sampler.AcceptanceRates.ToArray(); - if (sampler is NUTS nuts) - { - NUTSDiagnosticSampleCounts = nuts.DiagnosticSampleCounts; - NUTSDivergenceCounts = nuts.DivergenceCounts; - NUTSMaxTreeDepthHitCounts = nuts.MaxTreeDepthHitCounts; - NUTSMeanTreeDepths = nuts.MeanTreeDepths; - NUTSMeanLeapfrogSteps = nuts.MeanLeapfrogSteps; - NUTSStepSizes = nuts.StepSizes; - NUTSEnergyBayesianFractionOfMissingInformation = - nuts.EnergyBayesianFractionOfMissingInformation; - } + AcceptanceRates = sampler is NUTS nuts + ? nuts.HamiltonianAcceptanceRates.ToArray() + : sampler.AcceptanceRates.ToArray(); MeanLogLikelihood = sampler.MeanLogLikelihood.ToList(); MAP = sampler.MAP.Clone(); ProcessParameterResults(sampler, alpha); @@ -95,47 +86,6 @@ public MCMCResults(ParameterSet map, IList parameterSets, double a [JsonInclude] public double[] AcceptanceRates { get; private set; } = null!; - /// - /// Gets the number of post-warmup NUTS transitions contributing diagnostics per chain. - /// - [JsonInclude] - public int[]? NUTSDiagnosticSampleCounts { get; private set; } - - /// - /// Gets the number of divergent post-warmup NUTS transitions per chain. - /// - [JsonInclude] - public int[]? NUTSDivergenceCounts { get; private set; } - - /// - /// Gets the number of post-warmup NUTS transitions that exhausted maximum tree depth per chain. - /// - [JsonInclude] - public int[]? NUTSMaxTreeDepthHitCounts { get; private set; } - - /// - /// Gets the mean post-warmup NUTS tree depth per chain. - /// - [JsonInclude] - public double[]? NUTSMeanTreeDepths { get; private set; } - - /// - /// Gets the mean post-warmup NUTS leapfrog-step count per transition and chain. - /// - [JsonInclude] - public double[]? NUTSMeanLeapfrogSteps { get; private set; } - - /// - /// Gets the final adapted NUTS leapfrog step size per chain. - /// - [JsonInclude] - public double[]? NUTSStepSizes { get; private set; } - - /// - /// Gets the post-warmup NUTS energy Bayesian fraction of missing information per chain. - /// - [JsonInclude] - public double[]? NUTSEnergyBayesianFractionOfMissingInformation { get; private set; } /// /// Parameter results using the output posterior parameter sets. @@ -206,8 +156,7 @@ private void ProcessParameterResults(double alpha = 0.1) /// /// Recompute parameter summary statistics at a new credible-interval level /// (alpha) without rerunning the chain. Preserves Rhat, ESS, autocorrelation, - /// MarkovChains, acceptance and sampler diagnostics, MeanLogLikelihood, MAP, - /// and Output. + /// MarkovChains, AcceptanceRates, MeanLogLikelihood, MAP, and Output. /// /// /// The new significance level (e.g., 0.05 for 95% credible intervals, diff --git a/Test_Numerics/Sampling/MCMC/Test_MCMCSamplerDiagnostics.cs b/Test_Numerics/Sampling/MCMC/Test_MCMCSamplerDiagnostics.cs index 90f8904a..d8598837 100644 --- a/Test_Numerics/Sampling/MCMC/Test_MCMCSamplerDiagnostics.cs +++ b/Test_Numerics/Sampling/MCMC/Test_MCMCSamplerDiagnostics.cs @@ -68,45 +68,50 @@ public void ARWMH_RejectedPostWarmupTransitionsContinueEnteringCovariance() } /// - /// Confirms that NUTS exposes post-warmup Hamiltonian acceptance and additive sampler diagnostics. + /// Confirms that NUTS diagnostic arrays are non-null and empty before sampling initializes them. /// [TestMethod] - public void NUTS_AcceptanceRatesAndDiagnosticsUsePostWarmupHamiltonianStatistics() + public void NUTS_DiagnosticArraysAreNonNullAndEmptyBeforeSampling() { - var priors = new List - { - new Uniform(-20d, 20d), - new Uniform(-20d, 20d) - }; - double LogTarget(double[] values) => -0.5d * (values[0] * values[0] + values[1] * values[1]); - Vector Gradient(IList values) => new Vector(new[] { -values[0], -values[1] }); - var sampler = new SeedableNUTS(priors, LogTarget, Gradient) - { - NumberOfChains = 2, - InitialIterations = 2, - WarmupIterations = 50, - Iterations = 100, - OutputLength = 100, - ThinningInterval = 1, - PRNGSeed = 8675309, - ParallelizeChains = false - }; + SeedableNUTS sampler = CreateNutsSampler(); + + Assert.IsEmpty(sampler.HamiltonianAcceptanceRates); + Assert.IsEmpty(sampler.DiagnosticSampleCounts); + Assert.IsEmpty(sampler.DivergenceCounts); + Assert.IsEmpty(sampler.MaxTreeDepthHitCounts); + Assert.IsEmpty(sampler.MeanTreeDepths); + Assert.IsEmpty(sampler.MeanLeapfrogSteps); + Assert.IsEmpty(sampler.StepSizes); + Assert.IsEmpty(sampler.EnergyBayesianFractionOfMissingInformation); + } + + /// + /// Confirms that generic acceptance retains accepted-transition semantics while + /// NUTS results persist the Hamiltonian acceptance statistic used by BestFit. + /// + [TestMethod] + public void NUTS_AcceptanceContractsRemainSeparatedAndResultsPersistHamiltonianRates() + { + SeedableNUTS sampler = CreateNutsSampler(); sampler.Seed(new ParameterSet(new[] { 0d, 0d }, 0d)); sampler.Sample(); for (int chainIndex = 0; chainIndex < sampler.NumberOfChains; chainIndex++) { + double expectedTransitionRate = (double)sampler.AcceptCount[chainIndex] / + sampler.SampleCount[chainIndex]; Assert.AreEqual(sampler.SampleCount[chainIndex], sampler.AcceptCount[chainIndex]); + Assert.AreEqual(expectedTransitionRate, sampler.AcceptanceRates[chainIndex], 0d); + Assert.AreEqual(1d, sampler.AcceptanceRates[chainIndex], 0d); Assert.AreEqual( sampler.SampleCount[chainIndex] - sampler.WarmupIterations, sampler.DiagnosticSampleCounts[chainIndex]); - Assert.AreEqual( - sampler.HamiltonianAcceptanceRates[chainIndex], + Assert.IsGreaterThan(0d, sampler.HamiltonianAcceptanceRates[chainIndex]); + Assert.IsLessThan(1d, sampler.HamiltonianAcceptanceRates[chainIndex]); + Assert.AreNotEqual( sampler.AcceptanceRates[chainIndex], - 0d); - Assert.IsGreaterThan(0d, sampler.AcceptanceRates[chainIndex]); - Assert.IsLessThan(1d, sampler.AcceptanceRates[chainIndex]); + sampler.HamiltonianAcceptanceRates[chainIndex]); Assert.IsGreaterThanOrEqualTo(1d, sampler.MeanTreeDepths[chainIndex]); Assert.IsLessThanOrEqualTo(4d, sampler.MeanTreeDepths[chainIndex]); Assert.IsGreaterThanOrEqualTo(1d, sampler.MeanLeapfrogSteps[chainIndex]); @@ -115,32 +120,70 @@ public void NUTS_AcceptanceRatesAndDiagnosticsUsePostWarmupHamiltonianStatistics } var results = new MCMCResults(sampler); - CollectionAssert.AreEqual(sampler.AcceptanceRates, results.AcceptanceRates); - CollectionAssert.AreEqual(sampler.DiagnosticSampleCounts, results.NUTSDiagnosticSampleCounts); - CollectionAssert.AreEqual(sampler.DivergenceCounts, results.NUTSDivergenceCounts); - CollectionAssert.AreEqual(sampler.MaxTreeDepthHitCounts, results.NUTSMaxTreeDepthHitCounts); - CollectionAssert.AreEqual(sampler.MeanTreeDepths, results.NUTSMeanTreeDepths); - CollectionAssert.AreEqual(sampler.MeanLeapfrogSteps, results.NUTSMeanLeapfrogSteps); - CollectionAssert.AreEqual(sampler.StepSizes, results.NUTSStepSizes); - CollectionAssert.AreEqual( - sampler.EnergyBayesianFractionOfMissingInformation, - results.NUTSEnergyBayesianFractionOfMissingInformation); + CollectionAssert.AreEqual(sampler.HamiltonianAcceptanceRates, results.AcceptanceRates); byte[] serialized = MCMCResults.ToByteArray(results); - MCMCResults restored = MCMCResults.FromByteArray(serialized); + string currentJson = System.Text.Encoding.UTF8.GetString(serialized); + Assert.IsFalse(currentJson.Contains("NUTSDivergenceCounts", StringComparison.Ordinal)); + string staleJson = currentJson.Insert(1, + "\"NUTSDiagnosticSampleCounts\":[100,100]," + + "\"NUTSDivergenceCounts\":[0,0]," + + "\"NUTSMaxTreeDepthHitCounts\":[0,0]," + + "\"NUTSMeanTreeDepths\":[2.0,2.0]," + + "\"NUTSMeanLeapfrogSteps\":[4.0,4.0]," + + "\"NUTSStepSizes\":[0.1,0.1]," + + "\"NUTSEnergyBayesianFractionOfMissingInformation\":[0.8,0.8],"); + MCMCResults restored = MCMCResults.FromByteArray(System.Text.Encoding.UTF8.GetBytes(staleJson)); Assert.IsNotNull(restored); CollectionAssert.AreEqual(results.AcceptanceRates, restored.AcceptanceRates); - CollectionAssert.AreEqual(results.NUTSDiagnosticSampleCounts, restored.NUTSDiagnosticSampleCounts); - CollectionAssert.AreEqual(results.NUTSDivergenceCounts, restored.NUTSDivergenceCounts); - CollectionAssert.AreEqual(results.NUTSMaxTreeDepthHitCounts, restored.NUTSMaxTreeDepthHitCounts); - CollectionAssert.AreEqual(results.NUTSMeanTreeDepths, restored.NUTSMeanTreeDepths); - CollectionAssert.AreEqual(results.NUTSMeanLeapfrogSteps, restored.NUTSMeanLeapfrogSteps); - CollectionAssert.AreEqual(results.NUTSStepSizes, restored.NUTSStepSizes); - CollectionAssert.AreEqual( - results.NUTSEnergyBayesianFractionOfMissingInformation, - restored.NUTSEnergyBayesianFractionOfMissingInformation); } + /// + /// Confirms that MCMCResults retains its baseline nullability metadata and has no + /// sampler-specific NUTS result properties. + /// + [TestMethod] + public void MCMCResults_RetainsBaselineNullabilityAndOmitsNutsDiagnostics() + { + var nullabilityContext = new NullabilityInfoContext(); + (string Name, NullabilityState State)[] expectedStates = + { + (nameof(MCMCResults.MarkovChains), NullabilityState.Nullable), + (nameof(MCMCResults.MeanLogLikelihood), NullabilityState.Nullable), + (nameof(MCMCResults.Output), NullabilityState.NotNull), + (nameof(MCMCResults.AcceptanceRates), NullabilityState.NotNull), + (nameof(MCMCResults.ParameterResults), NullabilityState.NotNull), + (nameof(MCMCResults.MAP), NullabilityState.NotNull), + (nameof(MCMCResults.PosteriorMean), NullabilityState.NotNull) + }; + + foreach ((string propertyName, NullabilityState expectedState) in expectedStates) + { + PropertyInfo property = typeof(MCMCResults).GetProperty(propertyName); + Assert.IsNotNull(property, $"Missing baseline MCMCResults property {propertyName}."); + Assert.AreEqual( + expectedState, + nullabilityContext.Create(property).ReadState, + $"Unexpected nullability metadata for MCMCResults.{propertyName}."); + } + + string[] removedProperties = + { + "NUTSDiagnosticSampleCounts", + "NUTSDivergenceCounts", + "NUTSMaxTreeDepthHitCounts", + "NUTSMeanTreeDepths", + "NUTSMeanLeapfrogSteps", + "NUTSStepSizes", + "NUTSEnergyBayesianFractionOfMissingInformation" + }; + foreach (string propertyName in removedProperties) + { + Assert.IsNull( + typeof(MCMCResults).GetProperty(propertyName), + $"NUTS diagnostic {propertyName} must remain on NUTS rather than MCMCResults."); + } + } /// /// Confirms the streaming E-BFMI calculation matches Stan's /// mean(diff(E)^2) / var(E) convention. @@ -158,6 +201,32 @@ public void NUTS_EnergyBayesianFractionOfMissingInformationMatchesStanFormula() NUTS.ComputeEnergyBayesianFractionOfMissingInformation(1, 0d, 0d))); } + /// + /// Creates the deterministic two-chain NUTS fixture used by acceptance-contract tests. + /// + /// The configured sampler before sampling or user-defined seeding. + private static SeedableNUTS CreateNutsSampler() + { + var priors = new List + { + new Uniform(-20d, 20d), + new Uniform(-20d, 20d) + }; + double LogTarget(double[] values) => + -0.5d * (values[0] * values[0] + values[1] * values[1]); + Vector Gradient(IList values) => new Vector(new[] { -values[0], -values[1] }); + return new SeedableNUTS(priors, LogTarget, Gradient) + { + NumberOfChains = 2, + InitialIterations = 2, + WarmupIterations = 50, + Iterations = 100, + OutputLength = 100, + ThinningInterval = 1, + PRNGSeed = 8675309, + ParallelizeChains = false + }; + } /// /// Creates a deterministic ARWMH sampler whose only finite-density state is the origin. /// From 17b00b9c15d6a240240b9c791675d9f39e8f58f6 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 27 Jul 2026 09:48:36 -0600 Subject: [PATCH 026/222] Handle degenerate kernel density samples --- .../Distributions/Univariate/KernelDensity.cs | 44 ++++++++++- .../Univariate/Test_KernelDensity.cs | 73 +++++++++++++++++++ 2 files changed, 115 insertions(+), 2 deletions(-) diff --git a/Numerics/Distributions/Univariate/KernelDensity.cs b/Numerics/Distributions/Univariate/KernelDensity.cs index 9181e16e..9f8aeae3 100644 --- a/Numerics/Distributions/Univariate/KernelDensity.cs +++ b/Numerics/Distributions/Univariate/KernelDensity.cs @@ -535,7 +535,8 @@ public double Function(double x) public double BandwidthRule(IList sampleData) { double sigma = Statistics.StandardDeviation(sampleData); - return sigma * Math.Pow(4.0d / (3.0d * sampleData.Count), 1.0d / 5.0d); + double factor = Math.Pow(4.0d / (3.0d * sampleData.Count), 1.0d / 5.0d); + return EnsurePositiveBandwidth(sigma, factor, sampleData); } /// @@ -548,7 +549,46 @@ public double BandwidthRule(IList sample, IList? w = null) w ??= Enumerable.Repeat(1.0, sample.Count).ToArray(); double m = w.Zip(sample, (wi, xi) => wi * xi).Sum() / w.Sum(); double sd = Math.Sqrt(w.Zip(sample, (wi, xi) => wi * (xi - m) * (xi - m)).Sum() / w.Sum()); - return sd * Math.Pow(4.0 / (3.0 * sample.Count), 0.2); + double factor = Math.Pow(4.0 / (3.0 * sample.Count), 0.2); + return EnsurePositiveBandwidth(sd, factor, sample); + } + + /// + /// Produces a finite, strictly positive automatic bandwidth when the sample dispersion is zero or non-finite. + /// + /// The sample dispersion used by the bandwidth rule. + /// The sample-size factor used by the bandwidth rule. + /// The finite, nonempty sample used to obtain a fallback scale. + /// A finite, strictly positive bandwidth. + /// + /// A constant sample has zero dispersion but remains a valid empirical sample. In that case, the + /// largest absolute observation supplies a scale; an all-zero sample uses unit scale. The lower + /// bound prevents underflow for subnormal sample values, while the upper guard prevents overflow. + /// + private static double EnsurePositiveBandwidth(double dispersion, double factor, IList sample) + { + double scale = dispersion; + if (!Tools.IsFinite(scale) || scale <= 0d) + { + scale = 0d; + for (int i = 0; i < sample.Count; i++) + { + scale = Math.Max(scale, Math.Abs(sample[i])); + } + + if (scale <= 0d) + { + scale = 1d; + } + } + + if (factor > 1d && scale > double.MaxValue / factor) + { + return double.MaxValue; + } + + double bandwidth = scale * factor; + return bandwidth > 0d && Tools.IsFinite(bandwidth) ? bandwidth : double.Epsilon; } diff --git a/Test_Numerics/Distributions/Univariate/Test_KernelDensity.cs b/Test_Numerics/Distributions/Univariate/Test_KernelDensity.cs index 98efa111..a4d531fd 100644 --- a/Test_Numerics/Distributions/Univariate/Test_KernelDensity.cs +++ b/Test_Numerics/Distributions/Univariate/Test_KernelDensity.cs @@ -73,6 +73,79 @@ public void Test_KernelDensity_CDF() Assert.AreEqual(28708.74, KDE.InverseCDF(KDE.CDF(x3)), 1E-2); } + /// + /// Verifies that automatic bandwidth selection supports a constant nonzero sample. + /// + [TestMethod] + public void ConstantNonzeroSample_UsesPositiveScaleAwareBandwidth() + { + double[] constantSample = { 5d, 5d, 5d, 5d }; + double expected = 5d * Math.Pow(4d / (3d * constantSample.Length), 0.2d); + + var distribution = new KernelDensity(constantSample); + + Assert.AreEqual(expected, distribution.Bandwidth, 1E-12); + Assert.IsTrue(double.IsFinite(distribution.PDF(5d))); + Assert.IsGreaterThan(0d, distribution.PDF(5d)); + } + /// + /// Verifies that automatic bandwidth selection supports a single finite observation. + /// + [TestMethod] + public void SingleObservation_UsesPositiveScaleAwareBandwidth() + { + double[] sample = { 7d }; + double expected = 7d * Math.Pow(4d / 3d, 0.2d); + + var distribution = new KernelDensity(sample); + + Assert.AreEqual(expected, distribution.Bandwidth, 1E-12); + Assert.IsTrue(double.IsFinite(distribution.PDF(7d))); + Assert.IsGreaterThan(0d, distribution.PDF(7d)); + } + + /// + /// Verifies that automatic bandwidth selection supplies a positive unit-scale fallback for an all-zero sample. + /// + [TestMethod] + public void ConstantZeroSample_UsesPositiveUnitScaleBandwidth() + { + double[] constantSample = { 0d, 0d, 0d, 0d }; + double expected = Math.Pow(4d / (3d * constantSample.Length), 0.2d); + + var distribution = new KernelDensity(constantSample); + + Assert.AreEqual(expected, distribution.Bandwidth, 1E-12); + Assert.IsTrue(double.IsFinite(distribution.PDF(0d))); + Assert.IsGreaterThan(0d, distribution.PDF(0d)); + } + + /// + /// Verifies that weighted automatic bandwidth selection supports a constant sample. + /// + [TestMethod] + public void WeightedConstantSample_UsesPositiveScaleAwareBandwidth() + { + double[] constantSample = { -3d, -3d, -3d, -3d }; + double[] weights = { 1d, 2d, 3d, 4d }; + double expected = 3d * Math.Pow(4d / (3d * constantSample.Length), 0.2d); + + var distribution = new KernelDensity(constantSample, weights); + + Assert.AreEqual(expected, distribution.Bandwidth, 1E-12); + Assert.IsTrue(double.IsFinite(distribution.PDF(-3d))); + Assert.IsGreaterThan(0d, distribution.PDF(-3d)); + } + + /// + /// Verifies that an explicitly supplied zero bandwidth remains invalid. + /// + [TestMethod] + public void ExplicitZeroBandwidth_RemainsInvalid() + { + Assert.Throws(() => + new KernelDensity(new[] { -1d, 0d, 1d }, KernelDensity.KernelType.Gaussian, 0d)); + } From c5ef58d2d7878e23907a993d826dbaaf194989d5 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 27 Jul 2026 09:56:09 -0600 Subject: [PATCH 027/222] Establish algorithm change authority --- .gitignore | 4 -- AGENTS.md | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++ CLAUDE.md | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 242 insertions(+), 4 deletions(-) create mode 100644 AGENTS.md create mode 100644 CLAUDE.md diff --git a/.gitignore b/.gitignore index 69cc224c..cd5624c4 100644 --- a/.gitignore +++ b/.gitignore @@ -372,9 +372,5 @@ MigrationBackup/ /TestResults # Local developer guidance and settings. These files are for developer machines only. -/CLAUDE.md -/Claude.md -/AGENTS.md -/Agents.md /.claude/ /.agents/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..62fca984 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,121 @@ +# Numerics Local Development Rules + +## Technical Authority and Algorithm Change Control (NON-NEGOTIABLE) + +Haden Smith is the final technical and numerical authority for this repository. AI agents, +including Codex and Claude, must never change an established algorithm, formula, +probability-combination rule, numerical method, convergence rule, default tolerance, +random-seed behavior, clipping or normalization policy, or reference-result contract without +Haden Smith's explicit prior approval. A failing test authorizes diagnosis and a proposed fix +only; it does not authorize tuning or replacing the algorithm. When an agent's reasoning +conflicts with Haden Smith's direction or domain judgment, the agent must stop, present the +evidence, and request a decision. AI confidence is not technical authority. + +## Quality Gate + +- No compiler errors. +- No compiler warnings. +- Do not suppress warnings to make a build pass; fix the code, XML documentation, or invalid reference. +- Empty catch blocks are not allowed. Preserve exception context when rethrowing or wrapping. + +## XML Documentation + +- XML documentation is mandatory for all public API additions and changes. +- Public classes, structs, interfaces, enums, constructors, properties, methods, events, and fields require accurate XML documentation. +- Private or internal methods with non-obvious algorithms should also be documented. +- Include ``, ``, ``, ``, and `` whenever they apply. +- Use `` for numerical method notes, assumptions, references, and domain-specific constraints. +- XML documentation warnings `CS1570`, `CS1571`, `CS1572`, `CS1573`, `CS1574`, `CS1584`, `CS1587`, `CS1589`, and `CS1591` are build errors when `EnforceXmlDocumentation=true`. + +## Unit Tests + +- Every behavior change must include unit tests or an explicit reason no test is useful. +- Every new public class requires corresponding coverage in `Test_Numerics`. +- Statistical and numerical methods must cover known values, edge cases, invalid inputs, and regression cases. +- Use small inline fixtures in tests unless a shared fixture already exists for the exact scenario. + +## Required Validation + +Run validation before committing: + +```powershell +dotnet build -c Release +dotnet test -c Release --no-build +``` + +Both commands must complete with zero errors, zero warnings, and zero failed tests. If validation fails, fix the issue before committing. + +## Git Workflow + +- Commit after successful build and test validation for the completed logical change. +- Stage only files that belong to the validated change. +- Do not include unrelated tracked changes or untracked local files. +- Use concise commit messages that describe the change. +- Do not push unless explicitly asked. + +## PR and Release Workflow + +Use this workflow for release-preparation pull requests and public package releases. + +### Release PR Planning + +- Base release PRs on `bug-fixes-and-enhancements` targeting `main` unless the user gives a different branch plan. +- Identify the last merged PR from the release branch and list every commit since that merge. +- Merge `origin/main` into the release branch before editing so current README, JOSS, citation, and metadata updates are preserved. +- Define the release outcome up front, including the exact `RMC.Numerics` version that NuGet.org must show as the latest public package after publishing. + +### Version and Metadata Checklist + +Update and cross-check every version-bearing file for each release: + +- `Numerics/Numerics.csproj`: set `Version`, `AssemblyVersion`, and `PackageReleaseNotes`. +- `CITATION.cff`: set `version` and `date-released`. +- `codemeta.json`: set `version` and `dateModified`. +- `.github/workflows/Snapshot.yml`: advance the snapshot version to the next patch after the release. +- Keep `PackageReleaseNotes` concise because NuGet.org displays this field on the package page. + +### PR Message Checklist + +Use a title like `Prepare vX.Y.Z release`. + +Include these sections in the PR body: + +- `Summary`: package, citation, CodeMeta, and snapshot metadata updates. +- `Summary`: user-facing fixes, reliability changes, API changes, and test coverage added since the previous release branch merge. +- `Validation`: required commands and package inspection results. + +Use this validation checklist: + +```markdown +- [ ] `dotnet restore` +- [ ] `dotnet build Numerics/Numerics.csproj -c Release /p:Version=X.Y.Z` +- [ ] `dotnet test -c Release` with `VSTEST_CONNECTION_TIMEOUT=600` +- [ ] `dotnet pack Numerics/Numerics.csproj -c Release /p:Version=X.Y.Z --no-build -o ./packages` +- [ ] Inspect the `.nupkg` metadata and confirm version `X.Y.Z` and release notes are correct. +``` + +### Release Message Checklist + +Create a GitHub Release titled `Numerics vX.Y.Z`. + +Include these sections in the release body: + +- `Highlights`: short release overview. +- `Reliability and Fixes`: behavior fixes and reliability improvements. +- Domain-specific sections for API, statistics, time-series, numerical methods, or other notable changes. +- `Install`: package install command. + +Use this install command format: + +```bash +dotnet add package RMC.Numerics --version X.Y.Z +``` + +### Publish Workflow + +- Merge the validated PR to `main`. +- Tag the merge commit as `vX.Y.Z`. +- Push the tag so `.github/workflows/Release.yml` publishes to the internal Nexus feed. +- Create and publish a non-draft, non-prerelease GitHub Release for `vX.Y.Z`. +- Confirm `.github/workflows/NuGetPublish.yml` publishes `RMC.Numerics.X.Y.Z.nupkg` to NuGet.org. +- Verify NuGet.org shows `RMC.Numerics X.Y.Z` as the latest public package. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..62fca984 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,121 @@ +# Numerics Local Development Rules + +## Technical Authority and Algorithm Change Control (NON-NEGOTIABLE) + +Haden Smith is the final technical and numerical authority for this repository. AI agents, +including Codex and Claude, must never change an established algorithm, formula, +probability-combination rule, numerical method, convergence rule, default tolerance, +random-seed behavior, clipping or normalization policy, or reference-result contract without +Haden Smith's explicit prior approval. A failing test authorizes diagnosis and a proposed fix +only; it does not authorize tuning or replacing the algorithm. When an agent's reasoning +conflicts with Haden Smith's direction or domain judgment, the agent must stop, present the +evidence, and request a decision. AI confidence is not technical authority. + +## Quality Gate + +- No compiler errors. +- No compiler warnings. +- Do not suppress warnings to make a build pass; fix the code, XML documentation, or invalid reference. +- Empty catch blocks are not allowed. Preserve exception context when rethrowing or wrapping. + +## XML Documentation + +- XML documentation is mandatory for all public API additions and changes. +- Public classes, structs, interfaces, enums, constructors, properties, methods, events, and fields require accurate XML documentation. +- Private or internal methods with non-obvious algorithms should also be documented. +- Include ``, ``, ``, ``, and `` whenever they apply. +- Use `` for numerical method notes, assumptions, references, and domain-specific constraints. +- XML documentation warnings `CS1570`, `CS1571`, `CS1572`, `CS1573`, `CS1574`, `CS1584`, `CS1587`, `CS1589`, and `CS1591` are build errors when `EnforceXmlDocumentation=true`. + +## Unit Tests + +- Every behavior change must include unit tests or an explicit reason no test is useful. +- Every new public class requires corresponding coverage in `Test_Numerics`. +- Statistical and numerical methods must cover known values, edge cases, invalid inputs, and regression cases. +- Use small inline fixtures in tests unless a shared fixture already exists for the exact scenario. + +## Required Validation + +Run validation before committing: + +```powershell +dotnet build -c Release +dotnet test -c Release --no-build +``` + +Both commands must complete with zero errors, zero warnings, and zero failed tests. If validation fails, fix the issue before committing. + +## Git Workflow + +- Commit after successful build and test validation for the completed logical change. +- Stage only files that belong to the validated change. +- Do not include unrelated tracked changes or untracked local files. +- Use concise commit messages that describe the change. +- Do not push unless explicitly asked. + +## PR and Release Workflow + +Use this workflow for release-preparation pull requests and public package releases. + +### Release PR Planning + +- Base release PRs on `bug-fixes-and-enhancements` targeting `main` unless the user gives a different branch plan. +- Identify the last merged PR from the release branch and list every commit since that merge. +- Merge `origin/main` into the release branch before editing so current README, JOSS, citation, and metadata updates are preserved. +- Define the release outcome up front, including the exact `RMC.Numerics` version that NuGet.org must show as the latest public package after publishing. + +### Version and Metadata Checklist + +Update and cross-check every version-bearing file for each release: + +- `Numerics/Numerics.csproj`: set `Version`, `AssemblyVersion`, and `PackageReleaseNotes`. +- `CITATION.cff`: set `version` and `date-released`. +- `codemeta.json`: set `version` and `dateModified`. +- `.github/workflows/Snapshot.yml`: advance the snapshot version to the next patch after the release. +- Keep `PackageReleaseNotes` concise because NuGet.org displays this field on the package page. + +### PR Message Checklist + +Use a title like `Prepare vX.Y.Z release`. + +Include these sections in the PR body: + +- `Summary`: package, citation, CodeMeta, and snapshot metadata updates. +- `Summary`: user-facing fixes, reliability changes, API changes, and test coverage added since the previous release branch merge. +- `Validation`: required commands and package inspection results. + +Use this validation checklist: + +```markdown +- [ ] `dotnet restore` +- [ ] `dotnet build Numerics/Numerics.csproj -c Release /p:Version=X.Y.Z` +- [ ] `dotnet test -c Release` with `VSTEST_CONNECTION_TIMEOUT=600` +- [ ] `dotnet pack Numerics/Numerics.csproj -c Release /p:Version=X.Y.Z --no-build -o ./packages` +- [ ] Inspect the `.nupkg` metadata and confirm version `X.Y.Z` and release notes are correct. +``` + +### Release Message Checklist + +Create a GitHub Release titled `Numerics vX.Y.Z`. + +Include these sections in the release body: + +- `Highlights`: short release overview. +- `Reliability and Fixes`: behavior fixes and reliability improvements. +- Domain-specific sections for API, statistics, time-series, numerical methods, or other notable changes. +- `Install`: package install command. + +Use this install command format: + +```bash +dotnet add package RMC.Numerics --version X.Y.Z +``` + +### Publish Workflow + +- Merge the validated PR to `main`. +- Tag the merge commit as `vX.Y.Z`. +- Push the tag so `.github/workflows/Release.yml` publishes to the internal Nexus feed. +- Create and publish a non-draft, non-prerelease GitHub Release for `vX.Y.Z`. +- Confirm `.github/workflows/NuGetPublish.yml` publishes `RMC.Numerics.X.Y.Z.nupkg` to NuGet.org. +- Verify NuGet.org shows `RMC.Numerics X.Y.Z` as the latest public package. From bf50fbe084faa21ef0bbf39eb920878ade8822ef Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 27 Jul 2026 09:58:56 -0600 Subject: [PATCH 028/222] Handle degenerate KDE bandwidth samples --- .../Distributions/Univariate/Test_KernelDensity.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Test_Numerics/Distributions/Univariate/Test_KernelDensity.cs b/Test_Numerics/Distributions/Univariate/Test_KernelDensity.cs index a4d531fd..12d7e83d 100644 --- a/Test_Numerics/Distributions/Univariate/Test_KernelDensity.cs +++ b/Test_Numerics/Distributions/Univariate/Test_KernelDensity.cs @@ -1,4 +1,5 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics; using Numerics.Distributions; namespace Distributions.Univariate @@ -85,7 +86,7 @@ public void ConstantNonzeroSample_UsesPositiveScaleAwareBandwidth() var distribution = new KernelDensity(constantSample); Assert.AreEqual(expected, distribution.Bandwidth, 1E-12); - Assert.IsTrue(double.IsFinite(distribution.PDF(5d))); + Assert.IsTrue(Tools.IsFinite(distribution.PDF(5d))); Assert.IsGreaterThan(0d, distribution.PDF(5d)); } /// @@ -100,7 +101,7 @@ public void SingleObservation_UsesPositiveScaleAwareBandwidth() var distribution = new KernelDensity(sample); Assert.AreEqual(expected, distribution.Bandwidth, 1E-12); - Assert.IsTrue(double.IsFinite(distribution.PDF(7d))); + Assert.IsTrue(Tools.IsFinite(distribution.PDF(7d))); Assert.IsGreaterThan(0d, distribution.PDF(7d)); } @@ -116,7 +117,7 @@ public void ConstantZeroSample_UsesPositiveUnitScaleBandwidth() var distribution = new KernelDensity(constantSample); Assert.AreEqual(expected, distribution.Bandwidth, 1E-12); - Assert.IsTrue(double.IsFinite(distribution.PDF(0d))); + Assert.IsTrue(Tools.IsFinite(distribution.PDF(0d))); Assert.IsGreaterThan(0d, distribution.PDF(0d)); } @@ -133,7 +134,7 @@ public void WeightedConstantSample_UsesPositiveScaleAwareBandwidth() var distribution = new KernelDensity(constantSample, weights); Assert.AreEqual(expected, distribution.Bandwidth, 1E-12); - Assert.IsTrue(double.IsFinite(distribution.PDF(-3d))); + Assert.IsTrue(Tools.IsFinite(distribution.PDF(-3d))); Assert.IsGreaterThan(0d, distribution.PDF(-3d)); } From 5db695ce8f7d6f194146b712e5faac485bf7ee5a Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 27 Jul 2026 10:00:33 -0600 Subject: [PATCH 029/222] Keep MCMC diagnostics tests multi-target compatible --- Test_Numerics/Sampling/MCMC/Test_MCMCSamplerDiagnostics.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Test_Numerics/Sampling/MCMC/Test_MCMCSamplerDiagnostics.cs b/Test_Numerics/Sampling/MCMC/Test_MCMCSamplerDiagnostics.cs index d8598837..24b57743 100644 --- a/Test_Numerics/Sampling/MCMC/Test_MCMCSamplerDiagnostics.cs +++ b/Test_Numerics/Sampling/MCMC/Test_MCMCSamplerDiagnostics.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; using Numerics; using Numerics.Data.Statistics; using Numerics.Distributions; @@ -145,6 +145,7 @@ public void NUTS_AcceptanceContractsRemainSeparatedAndResultsPersistHamiltonianR [TestMethod] public void MCMCResults_RetainsBaselineNullabilityAndOmitsNutsDiagnostics() { +#if NET6_0_OR_GREATER var nullabilityContext = new NullabilityInfoContext(); (string Name, NullabilityState State)[] expectedStates = { @@ -167,6 +168,8 @@ public void MCMCResults_RetainsBaselineNullabilityAndOmitsNutsDiagnostics() $"Unexpected nullability metadata for MCMCResults.{propertyName}."); } +#endif + string[] removedProperties = { "NUTSDiagnosticSampleCounts", From 2f6bf240879a47adcfebd13be0ec06ab5581657c Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 27 Jul 2026 10:23:49 -0600 Subject: [PATCH 030/222] Add lazy dependent probability enumeration --- Numerics/Data/Statistics/Probability.cs | 442 +++++++++++++++--- .../Test_ProbabilityLazyExclusive.cs | 241 ++++++++++ 2 files changed, 621 insertions(+), 62 deletions(-) diff --git a/Numerics/Data/Statistics/Probability.cs b/Numerics/Data/Statistics/Probability.cs index aa827702..44c28e48 100644 --- a/Numerics/Data/Statistics/Probability.cs +++ b/Numerics/Data/Statistics/Probability.cs @@ -57,11 +57,11 @@ public enum DependencyType public static double AAndB(double A, double B, double rho = 0d) { if (A == 0d || B == 0d) return 0d; - if (A == 1d) return B; - if (B == 1d) return A; - if (rho <= -0.999) return Math.Max(0d, A + B - 1); - if (rho >= 0.999) return Math.Min(A, B); - if (Math.Abs(rho) <= 1E-3) return A * B; + if (A == 1d) return Tools.Clamp(B, 0d, 1d); + if (B == 1d) return Tools.Clamp(A, 0d, 1d); + if (rho <= -0.999) return Tools.Clamp(A + B - 1d, 0d, 1d); + if (rho >= 0.999) return Tools.Clamp(Math.Min(A, B), 0d, 1d); + if (Math.Abs(rho) <= 1E-3) return Tools.Clamp(A * B, 0d, 1d); return Tools.Clamp(MultivariateNormal.BivariateCDF(Normal.StandardZ(1 - A), Normal.StandardZ(1 - B), rho), 0, 1); } @@ -249,7 +249,7 @@ public static double NegativeJointProbability(IList probabilities) // Validation Checks if (probabilities == null || probabilities.Count == 0) throw new ArgumentException("The probabilities array must have a length greater than 0.", nameof(probabilities)); - return Math.Max(0, Math.Min(1, Tools.Sum(probabilities)) - 1); + return Tools.Clamp(Math.Min(1d, Tools.Sum(probabilities)) - 1d, 0d, 1d); } /// @@ -266,7 +266,7 @@ public static double NegativeJointProbability(IList probabilities, int[] throw new ArgumentException("The indicators array must have at least one row.", nameof(indicators)); if (probabilities.Count != indicators.Length) throw new ArgumentException("The probabilities and indicators arrays must have the same length.", nameof(probabilities)); - return Math.Max(0, Math.Min(1, Tools.Sum(probabilities, indicators)) - 1); + return Tools.Clamp(Math.Min(1d, Tools.Sum(probabilities, indicators)) - 1d, 0d, 1d); } /// @@ -335,7 +335,7 @@ public static double JointProbabilityHPCM(IList probabilities, int[] ind r12 = R[0, k]; r12 = Math.Abs(r12) < 1E-3 ? 0: r12; p21 = MultivariateNormal.BivariateCDF(-z1, -z2, r12) / cdf; - p21 = Math.Max(0, Math.Min(1, p21)); + p21 = Tools.Clamp(p21, 0d, 1d); z21 = Tools.Clamp(Normal.StandardZ(p21), zMin, zMax); R[k, 0] = z21; } @@ -362,7 +362,7 @@ public static double JointProbabilityHPCM(IList probabilities, int[] ind r12 = R[j, k]; r12 = Math.Abs(r12) < 1E-3 ? 0 : r12; p21 = MultivariateNormal.BivariateCDF(-z1, -z2, r12) / cdf; - p21 = Math.Max(0, Math.Min(1, p21)); + p21 = Tools.Clamp(p21, 0d, 1d); z21 = Tools.Clamp(Normal.StandardZ(p21), zMin, zMax); R[k, j] = z21; @@ -379,15 +379,15 @@ public static double JointProbabilityHPCM(IList probabilities, int[] ind // Calculate the product of conditional marginals (PCM) jp = Math.Log(Normal.StandardCDF(R[0, 0])); if (conditionalProbabilities != null && conditionalProbabilities.Length == n) - conditionalProbabilities[0] = Normal.StandardCDF(R[0, 0]); + conditionalProbabilities[0] = Tools.Clamp(Normal.StandardCDF(R[0, 0]), 0d, 1d); for (i = 1; i < n; i++) { jp += Math.Log(Normal.StandardCDF(R[i, i - 1])); if (conditionalProbabilities != null && conditionalProbabilities.Length == n) - conditionalProbabilities[i] = Normal.StandardCDF(R[i, i - 1]); + conditionalProbabilities[i] = Tools.Clamp(Normal.StandardCDF(R[i, i - 1]), 0d, 1d); } jp = Math.Exp(jp); - jp = Math.Min(1, Math.Max(0, jp)); + jp = Tools.Clamp(jp, 0d, 1d); if (double.IsNaN(jp)) jp = 0; return jp; } @@ -491,15 +491,15 @@ public static double JointProbabilityPCM(IList probabilities, int[] indi // Calculate the product of conditional marginals (PCM) double jp = Math.Log(Normal.StandardCDF(R[0, 0])); if (conditionalProbabilities != null && conditionalProbabilities.Length == n) - conditionalProbabilities[0] = Normal.StandardCDF(R[0, 0]); + conditionalProbabilities[0] = Tools.Clamp(Normal.StandardCDF(R[0, 0]), 0d, 1d); for (i = 1; i < n; i++) { jp += Math.Log(Normal.StandardCDF(R[i, i - 1])); if (conditionalProbabilities != null && conditionalProbabilities.Length == n) - conditionalProbabilities[i] = Normal.StandardCDF(R[i, i - 1]); + conditionalProbabilities[i] = Tools.Clamp(Normal.StandardCDF(R[i, i - 1]), 0d, 1d); } jp = Math.Exp(jp); - jp = Math.Min(1, Math.Max(0, jp)); + jp = Tools.Clamp(jp, 0d, 1d); if (double.IsNaN(jp)) jp = 0; return jp; } @@ -526,7 +526,7 @@ public static double[] JointProbabilitiesPCM(IList probabilities, int[,] { if (idx < probabilities.Count) { - result[idx] = probabilities[idx]; + result[idx] = Tools.Clamp(probabilities[idx], 0d, 1d); } else { @@ -565,7 +565,7 @@ public static double JointProbabilityMVN(IList probabilities, int[] indi } } var p = multivariateNormal.CDF(zVals); - p = Math.Max(0, Math.Min(1, p)); + p = Tools.Clamp(p, 0d, 1d); return p; } @@ -591,7 +591,7 @@ public static double[] JointProbabilitiesMVN(IList probabilities, int[,] { if (idx < probabilities.Count) { - result[idx] = probabilities[idx]; + result[idx] = Tools.Clamp(probabilities[idx], 0d, 1d); } else { @@ -635,7 +635,7 @@ public static double IndependentUnion(IList probabilities) { if (probabilities == null || probabilities.Count == 0) throw new ArgumentException("The probabilities list must be non-null and contain at least one element."); - if (probabilities.Count == 1) return probabilities[0]; + if (probabilities.Count == 1) return Tools.Clamp(probabilities[0], 0d, 1d); double numerator = 1d; for (int i = 0; i < probabilities.Count; i++) @@ -644,7 +644,7 @@ public static double IndependentUnion(IList probabilities) if (q == 0d) return 1d; // any event certain -> union = 1 numerator *= q; } - return 1d - numerator; + return Tools.Clamp(1d - numerator, 0d, 1d); } /// @@ -655,7 +655,7 @@ public static double PositivelyDependentUnion(IList probabilities) { if (probabilities == null || probabilities.Count == 0) throw new ArgumentException("The probabilities list must be non-null and contain at least one element."); - if (probabilities.Count == 1) return probabilities[0]; + if (probabilities.Count == 1) return Tools.Clamp(probabilities[0], 0d, 1d); return Tools.Clamp(Tools.Max(probabilities), 0, 1); } @@ -667,12 +667,12 @@ public static double NegativelyDependentUnion(IList probabilities) { if (probabilities == null || probabilities.Count == 0) throw new ArgumentException("The probabilities list must be non-null and contain at least one element."); - if (probabilities.Count == 1) return probabilities[0]; + if (probabilities.Count == 1) return Tools.Clamp(probabilities[0], 0d, 1d); return Tools.Clamp(Tools.Sum(probabilities), 0, 1); } /// - /// Returns the probability of union using the inclusion-exclusion method. Dependence between events is captured with the multivariate normal distribution. + /// Returns the probability of union using the inclusion-exclusion method. Dependence between events is captured with the PCM method. /// /// List of probabilities. /// The correlation matrix defining the dependency. @@ -686,22 +686,77 @@ public static double UnionPCM(IList probabilities, double[,] correlation throw new ArgumentException("Input arrays must be non-empty and correlation matrix must not be null."); } - // Get number of unique combinations by subset - int N = probabilities.Count; - var binomialCombinations = new int[N]; - for (int i = 1; i <= N; i++) + return UnionPCMLazy(probabilities, correlationMatrix, out _, absoluteTolerance, relativeTolerance); + } + + /// + /// Lazily returns the probability of union using inclusion-exclusion with Product of Conditional Marginals (PCM) dependence. + /// + /// List of marginal event probabilities. + /// The correlation matrix defining the dependency. + /// The enumeration completion status. + /// The absolute tolerance for evaluation convergence of the inclusion-exclusion algorithm. Default = 1E-4. + /// The relative tolerance for evaluation convergence of the inclusion-exclusion algorithm. Default = 1E-4. + /// The probability of the union of the events. + /// Thrown when the probability collection is null or empty, or the correlation matrix is null. + /// + /// Combinations are generated in the subset-size and lexicographic order used by + /// . The PCM calculation, alternating + /// inclusion-exclusion signs, dual convergence predicate, and half-gap closure are + /// identical to the dense overload. + /// + public static double UnionPCMLazy(IList probabilities, double[,] correlationMatrix, + out ExclusiveEnumerationStatus status, double absoluteTolerance = 1E-4, double relativeTolerance = 1E-4) + { + ValidateLazyPCMInputs(probabilities, correlationMatrix, absoluteTolerance, relativeTolerance); + + int n = probabilities.Count; + var row = new int[n]; + double union = 0d; + double sign = 1d; + double inclusion = double.NaN; + double exclusion = double.NaN; + int previousSubsetSize = 0; + + foreach (int[] combination in Factorial.AllCombinationsLazy(n)) { - binomialCombinations[i - 1] = (int)Factorial.BinomialCoefficient(N, i); - } + int subsetSize = combination.Length; + if (subsetSize != previousSubsetSize) + { + previousSubsetSize = subsetSize; + if (subsetSize >= 2) + { + int block = subsetSize - 2; + if (block > 0) + { + if (sign == 1d) inclusion = union; + else if (sign == -1d) exclusion = union; + } + + double difference = Math.Abs(inclusion - exclusion); + if (block > 0 && block < n && + difference <= absoluteTolerance && + difference <= relativeTolerance * Math.Min(inclusion, exclusion)) + { + status = ExclusiveEnumerationStatus.Converged; + return Tools.Clamp(union + 0.5d * difference, 0d, 1d); + } + + sign *= -1d; + } + } - // Get combination indicators - var indicators = Factorial.AllCombinations(N); + Array.Clear(row, 0, row.Length); + for (int i = 0; i < combination.Length; i++) row[combination[i]] = 1; + double jointProbability = subsetSize == 1 + ? probabilities[combination[0]] + : JointProbability(probabilities, row, correlationMatrix); + union += sign * jointProbability; + } - // Return Union - return UnionPCM(probabilities, binomialCombinations, indicators, correlationMatrix, absoluteTolerance, relativeTolerance); - + status = ExclusiveEnumerationStatus.Complete; + return Tools.Clamp(union, 0d, 1d); } - /// /// Returns the probability of union using the inclusion-exclusion method. Dependence between events is captured with the PCM method. /// @@ -744,7 +799,7 @@ public static double UnionPCM(IList probabilities, int[] binomialCombina double diff = Math.Abs(inc - exc); if (j > 0 && j < binomialCombinations.Length && diff <= absoluteTolerance && diff <= relativeTolerance * Math.Min(inc, exc)) { - return result + 0.5 * diff; // Converged, return the result with half of the difference + return Tools.Clamp(result + 0.5d * diff, 0d, 1d); // Converged, return the result with half of the difference } s *= -1; // Alternate sign for inclusion-exclusion @@ -769,7 +824,7 @@ public static double UnionPCM(IList probabilities, int[] binomialCombina } - return result; + return Tools.Clamp(result, 0d, 1d); } /// @@ -822,8 +877,8 @@ public static double UnionPCM(IList probabilities, int[] binomialCombina if (j > 0 && j < binomialCombinations.Length && diff <= absoluteTolerance && diff <= relativeTolerance * Math.Min(inc, exc)) { eventIndicators.Add(indicators.GetRow(indicators.GetLength(0) - 1)); // Add the last row for event indicators - eventProbabilities.Add(0.5 * diff); // Add the averaged difference - return union + 0.5 * diff; // Converged, return the result with half of the difference + eventProbabilities.Add(Tools.Clamp(0.5d * diff, 0d, 1d)); // Add the averaged difference + return Tools.Clamp(union + 0.5d * diff, 0d, 1d); // Converged, return the result with half of the difference } s *= -1; // Alternate the sign for inclusion-exclusion @@ -843,18 +898,18 @@ public static double UnionPCM(IList probabilities, int[] binomialCombina if (i < probabilities.Count) { union += s * probabilities[i]; // If the event is within the range of probabilities, add directly - eventProbabilities.Add(probabilities[i]); // Store the probability + eventProbabilities.Add(Tools.Clamp(probabilities[i], 0d, 1d)); // Store the probability } else { var jp = JointProbability(probabilities, indicators.GetRow(i), correlationMatrix); // Otherwise, calculate the joint probability union += s * jp; // Add the joint probability contribution - eventProbabilities.Add(jp); // Store the joint probability + eventProbabilities.Add(Tools.Clamp(jp, 0d, 1d)); // Store the joint probability } } - return union; + return Tools.Clamp(union, 0d, 1d); } /// @@ -942,7 +997,7 @@ public static double UnionMVN(IList probabilities, int[] binomialCombina } } - return result; + return Tools.Clamp(result, 0d, 1d); } #endregion @@ -983,7 +1038,7 @@ public static double IndependentExclusive(IList probabilities, int[] ind result *= (1 - probabilities[i]); } } - return result; + return Tools.Clamp(result, 0d, 1d); } /// @@ -1161,7 +1216,7 @@ void TrimToUsed() if (j > 0 && j < binomialCombinations.Length && diff <= absoluteTolerance && diff <= relativeTolerance * Math.Min(inc, exc)) { PlaceRow(indicators.GetLength(0) - 1); // Add last indicator row - eventProbabilities.Add(0.5 * diff); // Add the average of the difference to the event probabilities + eventProbabilities.Add(Tools.Clamp(0.5d * diff, 0d, 1d)); // Add the average of the difference to the event probabilities TrimToUsed(); return true; // Report that convergence ended the expansion early. } @@ -1180,7 +1235,7 @@ void TrimToUsed() var currentRow = PlaceRow(i); // Compute the exclusive event probability and add to the list - eventProbabilities.Add(IndependentExclusive(probabilities, currentRow)); + eventProbabilities.Add(Tools.Clamp(IndependentExclusive(probabilities, currentRow), 0d, 1d)); // Calculate the union of probabilities (inclusion-exclusion) if (i < probabilities.Count) @@ -1198,6 +1253,26 @@ void TrimToUsed() return false; } + /// + /// Validates the common inputs for lazy PCM enumeration without changing the legacy tolerance or marginal-probability handling. + /// + /// The marginal event probabilities. + /// The PCM correlation matrix. + /// The absolute convergence tolerance. + /// The relative convergence tolerance. + /// Thrown when the probability collection is null or empty, or the correlation matrix is null. + private static void ValidateLazyPCMInputs(IList probabilities, double[,] correlationMatrix, + double absoluteTolerance, double relativeTolerance) + { + if (probabilities == null || probabilities.Count == 0 || correlationMatrix == null) + throw new ArgumentException("Input arrays must be non-empty and correlation matrix must not be null."); + + // The established dense PCM overloads do not reject negative or non-finite tolerances. + // Preserve that validation behavior; these parameters are accepted here only to keep + // the common lazy call contract explicit. + _ = absoluteTolerance; + _ = relativeTolerance; + } /// /// Validates the structural metadata used by the pooled exclusive-probability overload. /// @@ -1322,7 +1397,7 @@ ExclusiveEnumerationStatus Close(double mass, ExclusiveEnumerationStatus status) { var row = Row(); for (int column = 0; column < n; column++) row[column] = 1; - eventProbabilities.Add(mass); + eventProbabilities.Add(Tools.Clamp(mass, 0d, 1d)); TrimToUsed(); return status; } @@ -1334,7 +1409,7 @@ ExclusiveEnumerationStatus Close(double mass, ExclusiveEnumerationStatus status) if (includeNoEventRow) { Row(); - eventProbabilities.Add(noEventMass); + eventProbabilities.Add(Tools.Clamp(noEventMass, 0d, 1d)); emittedMass += noEventMass; } @@ -1370,14 +1445,14 @@ ExclusiveEnumerationStatus Close(double mass, ExclusiveEnumerationStatus status) { if (maxEmittedCombinations > 0 && emitted >= maxEmittedCombinations) { - return Close(Math.Max(0d, totalOutputMass - emittedMass), ExclusiveEnumerationStatus.Capped); + return Close(Tools.Clamp(totalOutputMass - emittedMass, 0d, 1d), ExclusiveEnumerationStatus.Capped); } var row = Row(); for (int t = 0; t < k; t++) row[combination[t]] = 1; double exclusive = IndependentExclusive(probabilities, row); - eventProbabilities.Add(exclusive); + eventProbabilities.Add(Tools.Clamp(exclusive, 0d, 1d)); emittedMass += exclusive; emitted++; @@ -1426,7 +1501,7 @@ public static double PositivelyDependentExclusive(IList probabilities, i if (probabilities[i] > max) max = probabilities[i]; } } - return Math.Max(min - max, 0); + return Tools.Clamp(min - max, 0d, 1d); } /// @@ -1542,7 +1617,7 @@ public static void PositivelyDependentExclusive(IList probabilities, int if (j > 0 && j < binomialCombinations.Length && diff <= absoluteTolerance && diff <= relativeTolerance * Math.Min(inc, exc)) { eventIndicators.Add(indicators.GetRow(indicators.GetLength(0) - 1)); // Add last indicator row - eventProbabilities.Add(0.5 * diff); // Add the average of the difference to the event probabilities + eventProbabilities.Add(Tools.Clamp(0.5d * diff, 0d, 1d)); // Add the average of the difference to the event probabilities return; // Exit early when convergence is reached } @@ -1560,7 +1635,7 @@ public static void PositivelyDependentExclusive(IList probabilities, int eventIndicators.Add(indicators.GetRow(i)); // Compute the exclusive event probability and add to the list - eventProbabilities.Add(PositivelyDependentExclusive(probabilities, eventIndicators.Last())); + eventProbabilities.Add(Tools.Clamp(PositivelyDependentExclusive(probabilities, eventIndicators.Last()), 0d, 1d)); // Calculate the union of probabilities (inclusion-exclusion) if (i < probabilities.Count) @@ -1576,6 +1651,111 @@ public static void PositivelyDependentExclusive(IList probabilities, int } + /// + /// Lazily enumerates mutually exclusive event probabilities under perfect positive dependence. + /// + /// The marginal event probabilities. + /// The caller-owned output list of exclusive event probabilities; cleared and refilled. + /// The caller-owned output list of indicator rows; matching rows are reused. + /// The absolute tolerance for evaluation convergence. Default = 1E-4. + /// The relative tolerance for evaluation convergence. Default = 1E-4. + /// Whether every row was enumerated or the established convergence rule closed the expansion early. + /// Thrown when the probability collection is null or empty. + /// Thrown when an output list is null. + /// + /// Row ordering, joint-probability association, sign transitions, the dual convergence + /// predicate, and the all-ones half-gap closing row match the dense overload exactly. + /// + public static ExclusiveEnumerationStatus PositivelyDependentExclusiveLazy(IList probabilities, + List eventProbabilities, List eventIndicators, + double absoluteTolerance = 1E-4, double relativeTolerance = 1E-4) + { + if (probabilities == null || probabilities.Count == 0) + throw new ArgumentException("The probabilities array must have a length greater than 0.", nameof(probabilities)); + if (eventProbabilities == null) throw new ArgumentNullException(nameof(eventProbabilities)); + if (eventIndicators == null) throw new ArgumentNullException(nameof(eventIndicators)); + + int n = probabilities.Count; + int used = 0; + eventProbabilities.Clear(); + + int[] Row() + { + int[] row; + if (used < eventIndicators.Count && eventIndicators[used] != null && eventIndicators[used].Length == n) + { + row = eventIndicators[used]; + Array.Clear(row, 0, n); + } + else + { + row = new int[n]; + if (used < eventIndicators.Count) eventIndicators[used] = row; + else eventIndicators.Add(row); + } + + used++; + return row; + } + + void TrimToUsed() + { + while (eventIndicators.Count > used) eventIndicators.RemoveAt(eventIndicators.Count - 1); + } + + ExclusiveEnumerationStatus Close(double mass) + { + int[] row = Row(); + for (int i = 0; i < n; i++) row[i] = 1; + eventProbabilities.Add(Tools.Clamp(mass, 0d, 1d)); + TrimToUsed(); + return ExclusiveEnumerationStatus.Converged; + } + + double union = 0d; + double sign = 1d; + double inclusion = double.NaN; + double exclusion = double.NaN; + + for (int subsetSize = 1; subsetSize <= n; subsetSize++) + { + if (subsetSize >= 2) + { + int block = subsetSize - 2; + if (block > 0) + { + if (sign == 1d) inclusion = union; + else if (sign == -1d) exclusion = union; + } + + double difference = Math.Abs(inclusion - exclusion); + if (block > 0 && block < n && + difference <= absoluteTolerance && + difference <= relativeTolerance * Math.Min(inclusion, exclusion)) + { + return Close(0.5d * difference); + } + + sign *= -1d; + } + + var combination = new int[subsetSize]; + for (int i = 0; i < subsetSize; i++) combination[i] = i; + do + { + int[] row = Row(); + for (int i = 0; i < combination.Length; i++) row[combination[i]] = 1; + eventProbabilities.Add(Tools.Clamp(PositivelyDependentExclusive(probabilities, row), 0d, 1d)); + union += sign * (subsetSize == 1 + ? probabilities[combination[0]] + : PositiveJointProbability(probabilities, row)); + } + while (Factorial.NextCombinationUnchecked(combination, n)); + } + + TrimToUsed(); + return ExclusiveEnumerationStatus.Complete; + } #endregion #region Any Dependency @@ -1613,7 +1793,7 @@ public static double[] ExclusivePCM(IList probabilities, double[,] corre /// /// Returns an array of exclusive probabilities of multiple events using the inclusion-exclusion method. - /// Dependence between events is captured with the multivariate normal distribution. + /// Dependence between events is captured with the PCM method. /// /// A list of probabilities for each event. /// An array of binomial combinations representing the number of possible event combinations for each subset. @@ -1670,7 +1850,7 @@ public static double[] ExclusivePCM(IList probabilities, int[] binomialC } // Correct for floating point issues - if (result[i] < 0d) result[i] = 0d; + result[i] = Tools.Clamp(result[i], 0d, 1d); } return result; @@ -1687,7 +1867,7 @@ public static double[] ExclusivePCM(IList probabilities, int[] binomialC /// The correlation matrix defining the dependency between events. /// Output. A list of exclusive event probabilities for each event combination. /// Output. A list of event indicators corresponding to each event combination. - /// The absolute tolerance for convergence of the inclusion-exclusion algorithm. Default is 1E-8. + /// The absolute tolerance for convergence of the inclusion-exclusion algorithm. Default is 1E-4. /// The relative tolerance for convergence of the inclusion-exclusion algorithm. Default is 1E-4. /// A list of exclusive probabilities of the events based on the inclusion-exclusion method with early convergence checks. /// Thrown if any array is invalid or if their lengths do not match. @@ -1741,11 +1921,149 @@ public static void ExclusivePCM(IList probabilities, int[] binomialCombi } // Correct for floating point issues - if (result[i] < 0d) result[i] = 0d; + result[i] = Tools.Clamp(result[i], 0d, 1d); } eventProbabilities = result.ToList(); } + /// + /// Lazily enumerates mutually exclusive event probabilities using PCM joint probabilities and inclusion-exclusion. + /// + /// The marginal event probabilities. + /// The correlation matrix defining the dependency. + /// The caller-owned output list of exclusive event probabilities; cleared and refilled. + /// The caller-owned output list of indicator rows; matching rows are reused. + /// The absolute tolerance for evaluation convergence. Default = 1E-4. + /// The relative tolerance for evaluation convergence. Default = 1E-4. + /// Whether every row was enumerated or the established convergence rule closed the expansion early. + /// Thrown when the probability collection is null or empty, or the correlation matrix is null. + /// Thrown when an output list is null. + /// + /// This method streams the same joint rows as the dense overload, then applies its + /// exclusive inclusion-exclusion transform over the emitted rows. It does not renormalize + /// the resulting probabilities. + /// + public static ExclusiveEnumerationStatus ExclusivePCMLazy(IList probabilities, + double[,] correlationMatrix, List eventProbabilities, List eventIndicators, + double absoluteTolerance = 1E-4, double relativeTolerance = 1E-4) + { + ValidateLazyPCMInputs(probabilities, correlationMatrix, absoluteTolerance, relativeTolerance); + if (eventProbabilities == null) throw new ArgumentNullException(nameof(eventProbabilities)); + if (eventIndicators == null) throw new ArgumentNullException(nameof(eventIndicators)); + + int n = probabilities.Count; + int used = 0; + eventProbabilities.Clear(); + var jointProbabilities = new List(); + var cumulativeCombinations = new List(); + + int[] Row() + { + int[] row; + if (used < eventIndicators.Count && eventIndicators[used] != null && eventIndicators[used].Length == n) + { + row = eventIndicators[used]; + Array.Clear(row, 0, n); + } + else + { + row = new int[n]; + if (used < eventIndicators.Count) eventIndicators[used] = row; + else eventIndicators.Add(row); + } + + used++; + return row; + } + + void TrimToUsed() + { + while (eventIndicators.Count > used) eventIndicators.RemoveAt(eventIndicators.Count - 1); + } + + ExclusiveEnumerationStatus status = ExclusiveEnumerationStatus.Complete; + double union = 0d; + double sign = 1d; + double inclusion = double.NaN; + double exclusion = double.NaN; + + for (int subsetSize = 1; subsetSize <= n; subsetSize++) + { + if (subsetSize >= 2) + { + int block = subsetSize - 2; + if (block > 0) + { + if (sign == 1d) inclusion = union; + else if (sign == -1d) exclusion = union; + } + + double difference = Math.Abs(inclusion - exclusion); + if (block > 0 && block < n && + difference <= absoluteTolerance && + difference <= relativeTolerance * Math.Min(inclusion, exclusion)) + { + int[] closingRow = Row(); + for (int i = 0; i < n; i++) closingRow[i] = 1; + jointProbabilities.Add(Tools.Clamp(0.5d * difference, 0d, 1d)); + status = ExclusiveEnumerationStatus.Converged; + break; + } + + sign *= -1d; + } + + var combination = new int[subsetSize]; + for (int i = 0; i < subsetSize; i++) combination[i] = i; + do + { + int[] row = Row(); + for (int i = 0; i < combination.Length; i++) row[combination[i]] = 1; + double jointProbability = subsetSize == 1 + ? probabilities[combination[0]] + : JointProbability(probabilities, row, correlationMatrix); + jointProbabilities.Add(Tools.Clamp(jointProbability, 0d, 1d)); + union += sign * jointProbability; + } + while (Factorial.NextCombinationUnchecked(combination, n)); + + if (subsetSize < n) cumulativeCombinations.Add(jointProbabilities.Count); + } + + int combinationBlock = 0; + int nextBlockStart = cumulativeCombinations.Count == 0 + ? int.MaxValue + : cumulativeCombinations[0]; + + for (int i = 0; i < used; i++) + { + if (i == nextBlockStart) + { + combinationBlock++; + nextBlockStart = combinationBlock < cumulativeCombinations.Count + ? cumulativeCombinations[combinationBlock] + : int.MaxValue; + } + + double exclusiveProbability = jointProbabilities[i]; + double association = 1d; + for (int block = combinationBlock; block < cumulativeCombinations.Count; block++) + { + association *= -1d; + int start = cumulativeCombinations[block]; + int end = block == cumulativeCombinations.Count - 1 + ? cumulativeCombinations[block] + 1 + : cumulativeCombinations[block + 1]; + exclusiveProbability += association * + SumSearch(jointProbabilities, eventIndicators[i], eventIndicators, start, end); + } + + eventProbabilities.Add(Tools.Clamp(exclusiveProbability, 0d, 1d)); + } + + TrimToUsed(); + return status; + } /// /// Returns an array of exclusive probabilities of multiple events using the inclusion-exclusion method. /// Dependence between events is captured with the multivariate normal distribution. @@ -1807,7 +2125,7 @@ public static double[] ExclusiveMVN(IList probabilities, MultivariateNor } // Correct small negative values due to floating point precision issues - if (result[i] < 0d) result[i] = 0d; + result[i] = Tools.Clamp(result[i], 0d, 1d); } return result; @@ -1872,7 +2190,7 @@ public static double[] ExclusiveMVN(IList probabilities, int[] binomialC } // Correct small negative values due to floating point precision issues - if (result[i] < 0d) result[i] = 0d; + result[i] = Tools.Clamp(result[i], 0d, 1d); } return result; @@ -1888,7 +2206,7 @@ public static double[] ExclusiveMVN(IList probabilities, int[] binomialC /// The multivariate normal distribution used to compute joint probabilities. /// Output. A list of exclusive event probabilities. /// Output. A list of exclusive event indicators that were evaluated. - /// The absolute tolerance for convergence evaluation. Default is 1E-8. + /// The absolute tolerance for convergence evaluation. Default is 1E-4. /// The relative tolerance for convergence evaluation. Default is 1E-4. /// Thrown if any input parameter is invalid. public static void ExclusiveMVN(IList probabilities, int[] binomialCombinations, int[,] indicators, MultivariateNormal multivariateNormal, out List eventProbabilities, out List eventIndicators, double absoluteTol = 1E-4, double relativeTol = 1E-4) @@ -1944,7 +2262,7 @@ public static void ExclusiveMVN(IList probabilities, int[] binomialCombi if (j > 0 && j < binomialCombinations.Length && diff <= tol) { eventIndicators.Add(indicators.GetRow(indicators.GetLength(0) - 1)); - jointProbabilities.Add(0.5 * diff); + jointProbabilities.Add(0.5d * diff); goto Exclusive; } @@ -2005,7 +2323,7 @@ public static void ExclusiveMVN(IList probabilities, int[] binomialCombi } // Correct small negative values due to floating point precision issues - if (prob < 0d) prob = 0d; + prob = Tools.Clamp(prob, 0d, 1d); eventProbabilities.Add(prob); } } diff --git a/Test_Numerics/Data/Statistics/Test_ProbabilityLazyExclusive.cs b/Test_Numerics/Data/Statistics/Test_ProbabilityLazyExclusive.cs index 012fefb2..046ec90f 100644 --- a/Test_Numerics/Data/Statistics/Test_ProbabilityLazyExclusive.cs +++ b/Test_Numerics/Data/Statistics/Test_ProbabilityLazyExclusive.cs @@ -182,5 +182,246 @@ public void Test_LazyExclusive_ReusesIndicatorRows() Assert.AreSame(first[i], eventIndicators[i], $"Row {i} was reallocated."); } } + + /// Builds a positive-definite equicorrelation matrix. + private static double[,] CorrelationMatrix(int n, double correlation) + { + var matrix = new double[n, n]; + for (int i = 0; i < n; i++) + { + for (int j = 0; j < n; j++) matrix[i, j] = i == j ? 1d : correlation; + } + + return matrix; + } + + /// + /// Verifies complete lazy PCM union and exclusive enumeration are bit-identical to the + /// established dense calculations, including indicator order. + /// + [TestMethod] + public void Test_LazyPCM_MatchesDense_WhenComplete() + { + var probabilities = new double[] { 0.32d, 0.27d, 0.19d, 0.11d }; + int n = probabilities.Length; + int[] counts = BinomialCounts(n); + int[,] indicators = Factorial.AllCombinations(n); + double[,] correlation = CorrelationMatrix(n, 0.25d); + + double denseUnion = Probability.UnionPCM( + probabilities, counts, indicators, correlation, 0d, 0d); + double lazyUnion = Probability.UnionPCMLazy( + probabilities, correlation, out var unionStatus, 0d, 0d); + + Assert.AreEqual(Probability.ExclusiveEnumerationStatus.Complete, unionStatus); + Assert.AreEqual(BitConverter.DoubleToInt64Bits(denseUnion), + BitConverter.DoubleToInt64Bits(lazyUnion)); + + Probability.ExclusivePCM(probabilities, counts, indicators, correlation, + out List denseProbabilities, out List denseIndicators, 0d, 0d); + var lazyProbabilities = new List(); + var lazyIndicators = new List(); + var exclusiveStatus = Probability.ExclusivePCMLazy( + probabilities, correlation, lazyProbabilities, lazyIndicators, 0d, 0d); + + Assert.AreEqual(Probability.ExclusiveEnumerationStatus.Complete, exclusiveStatus); + Assert.HasCount(denseProbabilities.Count, lazyProbabilities); + for (int i = 0; i < denseProbabilities.Count; i++) + { + Assert.AreEqual(BitConverter.DoubleToInt64Bits(denseProbabilities[i]), + BitConverter.DoubleToInt64Bits(lazyProbabilities[i]), $"probability at row {i}"); + CollectionAssert.AreEqual(denseIndicators[i], lazyIndicators[i], $"indicators at row {i}"); + } + } + + /// + /// Verifies early-converged lazy PCM preserves the dense row order, half-gap row, + /// exclusive values, union value, and completion status. + /// + [TestMethod] + public void Test_LazyPCM_MatchesDense_WhenConverged() + { + var probabilities = new double[] { 0.005d, 0.004d, 0.003d, 0.002d, 0.001d, 0.0005d }; + int n = probabilities.Length; + int[] counts = BinomialCounts(n); + int[,] indicators = Factorial.AllCombinations(n); + double[,] correlation = CorrelationMatrix(n, 0.1d); + + double denseUnion = Probability.UnionPCM( + probabilities, counts, indicators, correlation, 1E-4, 1E-4); + double lazyUnion = Probability.UnionPCMLazy( + probabilities, correlation, out var unionStatus); + + Probability.ExclusivePCM(probabilities, counts, indicators, correlation, + out List denseProbabilities, out List denseIndicators); + var lazyProbabilities = new List(); + var lazyIndicators = new List(); + var exclusiveStatus = Probability.ExclusivePCMLazy( + probabilities, correlation, lazyProbabilities, lazyIndicators); + + Assert.AreEqual(Probability.ExclusiveEnumerationStatus.Converged, unionStatus); + Assert.AreEqual(Probability.ExclusiveEnumerationStatus.Converged, exclusiveStatus); + Assert.AreEqual(BitConverter.DoubleToInt64Bits(denseUnion), + BitConverter.DoubleToInt64Bits(lazyUnion)); + Assert.HasCount(denseProbabilities.Count, lazyProbabilities); + for (int i = 0; i < denseProbabilities.Count; i++) + { + Assert.AreEqual(BitConverter.DoubleToInt64Bits(denseProbabilities[i]), + BitConverter.DoubleToInt64Bits(lazyProbabilities[i]), $"probability at row {i}"); + CollectionAssert.AreEqual(denseIndicators[i], lazyIndicators[i], $"indicators at row {i}"); + } + } + + /// + /// Verifies lazy perfectly-positive enumeration preserves the dense algorithm and reuses + /// caller-owned indicator rows. + /// + [TestMethod] + public void Test_PositiveLazy_MatchesDenseAndReusesRows() + { + var probabilities = new double[] { 0.4d, 0.3d, 0.2d, 0.1d }; + int n = probabilities.Length; + Probability.PositivelyDependentExclusive(probabilities, BinomialCounts(n), + Factorial.AllCombinations(n), out List denseProbabilities, + out List denseIndicators, 0d, 0d); + + var lazyProbabilities = new List(); + var lazyIndicators = new List(); + var status = Probability.PositivelyDependentExclusiveLazy( + probabilities, lazyProbabilities, lazyIndicators, 0d, 0d); + + Assert.AreEqual(Probability.ExclusiveEnumerationStatus.Complete, status); + Assert.HasCount(denseProbabilities.Count, lazyProbabilities); + for (int i = 0; i < denseProbabilities.Count; i++) + { + Assert.AreEqual(BitConverter.DoubleToInt64Bits(denseProbabilities[i]), + BitConverter.DoubleToInt64Bits(lazyProbabilities[i]), $"probability at row {i}"); + CollectionAssert.AreEqual(denseIndicators[i], lazyIndicators[i], $"indicators at row {i}"); + } + + int[][] firstRows = lazyIndicators.ToArray(); + Probability.PositivelyDependentExclusiveLazy( + probabilities, lazyProbabilities, lazyIndicators, 0d, 0d); + for (int i = 0; i < firstRows.Length; i++) + { + Assert.AreSame(firstRows[i], lazyIndicators[i], $"Row {i} was reallocated."); + } + } + + /// + /// Verifies dependent lazy enumeration supports more than twenty events, while the + /// independent lazy path separately operates above the dense matrix limit. + /// + [TestMethod] + public void Test_DependentLazyEnumeration_ExceedsTwentyEvents() + { + const int dimension = 24; + var probabilities = new double[dimension]; + probabilities[0] = 7E-4d; + probabilities[1] = 6E-4d; + probabilities[2] = 5E-4d; + + var eventProbabilities = new List(); + var eventIndicators = new List(); + var status = Probability.PositivelyDependentExclusiveLazy( + probabilities, eventProbabilities, eventIndicators); + + Assert.AreEqual(Probability.ExclusiveEnumerationStatus.Converged, status); + Assert.IsGreaterThan(0, eventProbabilities.Count); + Assert.IsLessThan(Math.Pow(2d, dimension), eventProbabilities.Count); + } + + /// + /// Pins every new lazy API's default convergence tolerances at 1E-4. + /// + [TestMethod] + public void Test_LazyProbability_DefaultTolerancesRemainOneEminusFour() + { + string[] methodNames = + { + nameof(Probability.UnionPCMLazy), + nameof(Probability.ExclusivePCMLazy), + nameof(Probability.PositivelyDependentExclusiveLazy), + }; + + foreach (string methodName in methodNames) + { + var method = typeof(Probability).GetMethod(methodName); + Assert.IsNotNull(method, methodName); + var parameters = method.GetParameters(); + Assert.AreEqual(1E-4, parameters[parameters.Length - 2].DefaultValue, methodName); + Assert.AreEqual(1E-4, parameters[parameters.Length - 1].DefaultValue, methodName); + } + } + + /// + /// Verifies the lazy PCM output rows are reused across calls. + /// + [TestMethod] + public void Test_LazyPCM_ReusesIndicatorRows() + { + var probabilities = new double[] { 0.2d, 0.15d, 0.1d, 0.05d }; + var eventProbabilities = new List(); + var eventIndicators = new List(); + double[,] correlation = CorrelationMatrix(probabilities.Length, 0.2d); + + Probability.ExclusivePCMLazy( + probabilities, correlation, eventProbabilities, eventIndicators, 0d, 0d); + int[][] firstRows = eventIndicators.ToArray(); + + Probability.ExclusivePCMLazy( + probabilities, correlation, eventProbabilities, eventIndicators, 0d, 0d); + + Assert.HasCount(firstRows.Length, eventIndicators); + for (int i = 0; i < firstRows.Length; i++) + { + Assert.AreSame(firstRows[i], eventIndicators[i], $"Row {i} was reallocated."); + } + } + + /// + /// Verifies finite scalar and array probability outputs are clipped while NaN signaling is + /// retained, and proves clipping is cellwise rather than proportional normalization. + /// + [TestMethod] + public void Test_ProbabilityOutputs_AreClippedWithoutNormalization() + { + static void AssertProbability(double value, string label) + { + Assert.IsTrue(double.IsNaN(value) || value >= 0d && value <= 1d, + $"{label} returned {value:R}."); + } + + AssertProbability(Probability.AAndB(1d, 2d), nameof(Probability.AAndB)); + AssertProbability(Probability.IndependentJointProbability(new[] { 2d, 2d }), + nameof(Probability.IndependentJointProbability)); + AssertProbability(Probability.PositiveJointProbability(new[] { 2d, 3d }), + nameof(Probability.PositiveJointProbability)); + AssertProbability(Probability.NegativeJointProbability(new[] { 2d, 3d }), + nameof(Probability.NegativeJointProbability)); + AssertProbability(Probability.IndependentUnion(new[] { -1d, -1d }), + nameof(Probability.IndependentUnion)); + AssertProbability(Probability.PositivelyDependentUnion(new[] { 2d, 3d }), + nameof(Probability.PositivelyDependentUnion)); + AssertProbability(Probability.NegativelyDependentUnion(new[] { 2d, 3d }), + nameof(Probability.NegativelyDependentUnion)); + + var adversarial = new[] { 2d, 0.5d }; + int[,] indicators = Factorial.AllCombinations(adversarial.Length); + double[] independent = Probability.IndependentExclusive(adversarial, indicators); + double[] positive = Probability.PositivelyDependentExclusive(adversarial, indicators); + foreach (double probability in independent) AssertProbability(probability, "IndependentExclusive"); + foreach (double probability in positive) AssertProbability(probability, "PositivelyDependentExclusive"); + + Assert.AreEqual(1d, independent[0], 0d); + Assert.AreEqual(0d, independent[1], 0d); + Assert.AreEqual(1d, independent[2], 0d); + Assert.AreEqual(2d, independent[0] + independent[1] + independent[2], 0d, + "Cellwise clipping must not proportionally renormalize an exclusive partition."); + + double nan = Probability.IndependentExclusive( + new[] { double.NaN, 0.5d }, new[] { 1, 0 }); + Assert.IsTrue(double.IsNaN(nan), "Tools.Clamp must preserve the established NaN signal."); + } } } From 4a9cb3e4fe8c7ab2342c15a33d64ae261331feb0 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 29 Jul 2026 10:28:30 -0600 Subject: [PATCH 031/222] Fix the perfectly negative joint probability to the Frechet-Hoeffding bound NegativeJointProbability computed min(1, sum(p)) - 1 clamped to [0, 1], which is zero for every input: two events at 0.8 and 0.9 returned a joint probability of 0 where the countermonotonic overlap must be at least 0.7. Both overloads now return the Frechet-Hoeffding lower bound max(0, sum(p) - (n - 1)); the indicator overload applies the bound over the k indicated events, matching the subset semantics of the other joint-probability overloads. Six tests pin the bound above and below the disjoint regime, the single-event identity, the indicator subsets, and the PerfectlyNegative dependency routing. All four target frameworks pass 1998/1998. --- Numerics/Data/Statistics/Probability.cs | 23 ++++++-- .../Data/Statistics/Test_Probability.cs | 58 ++++++++++++++++++- 2 files changed, 76 insertions(+), 5 deletions(-) diff --git a/Numerics/Data/Statistics/Probability.cs b/Numerics/Data/Statistics/Probability.cs index 44c28e48..ba59fdbc 100644 --- a/Numerics/Data/Statistics/Probability.cs +++ b/Numerics/Data/Statistics/Probability.cs @@ -241,22 +241,34 @@ public static double PositiveJointProbability(IList probabilities, int[] } /// - /// Returns the joint probability assuming perfect negative dependence. + /// Returns the joint probability assuming perfect negative dependence. /// /// List of probabilities. + /// The Fréchet–Hoeffding lower bound max(0, Σpᵢ − (n − 1)), where n is the number of events. + /// + /// For two events with probabilities 0.8 and 0.9 the joint probability is 0.7: under perfect + /// negative dependence the events overlap only by the amount their total probability exceeds one. + /// When the probabilities sum to one or less, perfectly negatively dependent events are disjoint + /// and the joint probability is zero. + /// public static double NegativeJointProbability(IList probabilities) { // Validation Checks if (probabilities == null || probabilities.Count == 0) throw new ArgumentException("The probabilities array must have a length greater than 0.", nameof(probabilities)); - return Tools.Clamp(Math.Min(1d, Tools.Sum(probabilities)) - 1d, 0d, 1d); + return Tools.Clamp(Tools.Sum(probabilities) - (probabilities.Count - 1d), 0d, 1d); } /// - /// Returns the joint probability assuming perfect negative dependence. + /// Returns the joint probability assuming perfect negative dependence. /// /// An array of probabilities for each event. /// An array of indicators, 0 means the event did not occur, 1 means the event did occur. + /// The Fréchet–Hoeffding lower bound max(0, Σpᵢ − (k − 1)) over the indicated events, where k is the number of indicated events. + /// + /// Only the events whose indicator is 1 participate, matching the other joint-probability + /// overloads. With no indicated events the joint probability of the empty intersection is one. + /// public static double NegativeJointProbability(IList probabilities, int[] indicators) { // Validation Checks @@ -266,7 +278,10 @@ public static double NegativeJointProbability(IList probabilities, int[] throw new ArgumentException("The indicators array must have at least one row.", nameof(indicators)); if (probabilities.Count != indicators.Length) throw new ArgumentException("The probabilities and indicators arrays must have the same length.", nameof(probabilities)); - return Tools.Clamp(Math.Min(1d, Tools.Sum(probabilities, indicators)) - 1d, 0d, 1d); + int indicated = 0; + for (int i = 0; i < indicators.Length; i++) + if (indicators[i] == 1) indicated++; + return Tools.Clamp(Tools.Sum(probabilities, indicators) - (indicated - 1d), 0d, 1d); } /// diff --git a/Test_Numerics/Data/Statistics/Test_Probability.cs b/Test_Numerics/Data/Statistics/Test_Probability.cs index 8b95c3c0..77c4d430 100644 --- a/Test_Numerics/Data/Statistics/Test_Probability.cs +++ b/Test_Numerics/Data/Statistics/Test_Probability.cs @@ -205,7 +205,7 @@ public void Test_JointABCD_PositivelyDependent_PCM() } /// - /// Test joint probability of ABCD assuming perfect negative dependence. + /// Test joint probability of ABCD assuming perfect negative dependence. /// [TestMethod] public void Test_JointABCD_NegativelyDependent() @@ -218,6 +218,62 @@ public void Test_JointABCD_NegativelyDependent() Assert.AreEqual(0.0, joint, 1E-6); } + /// + /// Test the Fréchet–Hoeffding lower bound when the probabilities sum past one: two events at + /// 0.8 and 0.9 must overlap by at least 0.7 under perfect negative dependence. + /// + [TestMethod] + public void Test_TwoEvents_NegativelyDependent_FrechetLowerBound() + { + var joint = Probability.NegativeJointProbability(new double[] { 0.8, 0.9 }); + Assert.AreEqual(0.7, joint, 1E-12); + } + + /// + /// Test the Fréchet–Hoeffding lower bound for more than two events: max(0, Σp − (n − 1)). + /// + [TestMethod] + public void Test_FourEvents_NegativelyDependent_FrechetLowerBound() + { + var joint = Probability.NegativeJointProbability(new double[] { 0.9, 0.9, 0.9, 0.8 }); + Assert.AreEqual(0.5, joint, 1E-12); + } + + /// + /// Test that a single event's negatively dependent joint probability is the event probability itself. + /// + [TestMethod] + public void Test_SingleEvent_NegativelyDependent_IsIdentity() + { + var joint = Probability.NegativeJointProbability(new double[] { 0.6 }); + Assert.AreEqual(0.6, joint, 1E-12); + } + + /// + /// Test the indicator overload: only the indicated events participate, with the bound + /// max(0, Σp − (k − 1)) over the k indicated events. + /// + [TestMethod] + public void Test_NegativelyDependent_IndicatorSubset() + { + var probabilities = new double[] { 0.8, 0.9, 0.5 }; + var joint = Probability.NegativeJointProbability(probabilities, new int[] { 1, 1, 0 }); + Assert.AreEqual(0.7, joint, 1E-12); + + var disjoint = Probability.NegativeJointProbability(probabilities, new int[] { 1, 0, 1 }); + Assert.AreEqual(0.3, disjoint, 1E-12); + } + + /// + /// Test that the perfectly negative dependency type routes through the Fréchet–Hoeffding lower bound. + /// + [TestMethod] + public void Test_JointProbability_PerfectlyNegative_RoutesToFrechetBound() + { + var joint = Probability.JointProbability(new double[] { 0.8, 0.9 }, Probability.DependencyType.PerfectlyNegative); + Assert.AreEqual(0.7, joint, 1E-12); + } + /// /// Test joint probability of ABCD using assuming perfect negative dependence using the Multivariate Normal (MVN). /// From 7e5fda05c14a18f1c23d49cd7059c9cbf16320d3 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 29 Jul 2026 10:39:43 -0600 Subject: [PATCH 032/222] Restore the plotting-position bias-correction proportion The bias-corrected and BCa quantile intervals computed the bias-correction proportion as (count + 1) / (B + 1), which shifts every limit and matches no published estimator: Efron and Tibshirani (1993, eq. 14.14) count strict exceedances over B, the boot package computes qnorm(sum(t < t0) / R) and errors on an infinite correction, and SciPy uses the mid-rank count over B. Both methods now use count(quantile <= estimate) / (B + 1) over the B successful replicates - the plotting-position form this library published through v2.1.4, retaining the successful-count denominator that corrects the failed-fit bias. Two tests pin the proportion: the zero-bias identity at the bootstrap median (bias-corrected limits equal percentile limits) and the full limit formula at an asymmetric 2/10 proportion. All four target frameworks pass 2000/2000. --- .../Uncertainty Analysis/BootstrapAnalysis.cs | 18 ++++++- .../Univariate/Test_BootstrapAnalysis.cs | 48 +++++++++++++++++++ 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs b/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs index afc1e159..c7dac474 100644 --- a/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs +++ b/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs @@ -532,6 +532,13 @@ public double[] ComputeMinMaxQuantiles(double minProbability, double maxProbabil /// The excluded two-sided probability. /// Optional precomputed bootstrap distributions. /// The lower and upper confidence limits for each probability. + /// + /// The bias-correction proportion is count(θ*ᵢ ≤ θ̂) / (B + 1) over the B successful bootstrap + /// replicates — the plotting-position form of Efron's estimator, which keeps the proportion + /// below one when every replicate falls at or below the estimate. When every replicate exceeds + /// the estimate the proportion is zero, the bias correction is −∞, and the adjusted limits + /// collapse to the smallest replicate. + /// public double[,] BiasCorrectedQuantileCI(IList probabilities, double alpha = 0.1, IUnivariateDistribution[]? distributions = null) { var populationValues = new double[probabilities.Count]; @@ -556,7 +563,7 @@ public double[] ComputeMinMaxQuantiles(double minProbability, double maxProbabil for (int index = 0; index < successfulValues.Length; index++) if (successfulValues[index] <= populationValues[i]) lessOrEqual++; - double proportion = (lessOrEqual + 1d) / (successfulValues.Length + 1d); + double proportion = lessOrEqual / (successfulValues.Length + 1d); Array.Sort(successfulValues); double bias = Normal.StandardZ(proportion); for (int j = 0; j < confidenceProbabilities.Length; j++) @@ -613,6 +620,13 @@ public double[] ComputeMinMaxQuantiles(double minProbability, double maxProbabil /// The non-exceedance probabilities. /// The excluded two-sided probability. /// The lower and upper confidence limits for each probability. + /// + /// The bias-correction proportion is count(θ*ᵢ ≤ θ̂) / (B + 1) over the B successful bootstrap + /// replicates — the plotting-position form of Efron's estimator, which keeps the proportion + /// below one when every replicate falls at or below the estimate. When every replicate exceeds + /// the estimate the proportion is zero and the bias correction is −∞; the adjusted limits then + /// collapse to the smallest replicate, or are undefined when the acceleration is nonzero. + /// public double[,] BCaQuantileCI(IList sampleData, IList probabilities, double alpha = 0.1) { var confidenceProbabilities = new[] { alpha / 2d, 1d - alpha / 2d }; @@ -641,7 +655,7 @@ public double[] ComputeMinMaxQuantiles(double minProbability, double maxProbabil for (int index = 0; index < successfulValues.Length; index++) if (successfulValues[index] <= populationValues[i]) lessOrEqual++; - double proportion = (lessOrEqual + 1d) / (successfulValues.Length + 1d); + double proportion = lessOrEqual / (successfulValues.Length + 1d); double bias = Normal.StandardZ(proportion); Array.Sort(successfulValues); for (int j = 0; j < confidenceProbabilities.Length; j++) diff --git a/Test_Numerics/Distributions/Univariate/Test_BootstrapAnalysis.cs b/Test_Numerics/Distributions/Univariate/Test_BootstrapAnalysis.cs index 7654d59f..52828e38 100644 --- a/Test_Numerics/Distributions/Univariate/Test_BootstrapAnalysis.cs +++ b/Test_Numerics/Distributions/Univariate/Test_BootstrapAnalysis.cs @@ -250,6 +250,54 @@ public void Test_ExpectedProbabilities_IsThreadCountIndependent() } } + /// + /// Pins the bias-correction proportion count(θ* ≤ θ̂) / (B + 1). With the estimate at the + /// bootstrap median of nine replicates the proportion is 5/10, the bias correction is zero, + /// and the bias-corrected limits equal the unadjusted percentile limits — any shift of the + /// count numerator breaks this identity. + /// + [TestMethod] + public void Test_BiasCorrected_ZeroBias_MatchesPercentileLimits() + { + var parent = new Normal(5d, 1d); + var boot = new BootstrapAnalysis(parent, ParameterEstimationMethod.MethodOfMoments, 10, 100); + var values = new double[] { 1d, 2d, 3d, 4d, 4.5, 6d, 7d, 8d, 9d }; + var replicates = new IUnivariateDistribution[values.Length]; + for (int i = 0; i < values.Length; i++) + replicates[i] = new Deterministic(values[i]); + + double alpha = 0.1; + var ci = boot.BiasCorrectedQuantileCI(new double[] { 0.5 }, alpha, replicates); + + Assert.AreEqual(Statistics.Percentile(values, alpha / 2d, true), ci[0, 0], 1E-12); + Assert.AreEqual(Statistics.Percentile(values, 1d - alpha / 2d, true), ci[0, 1], 1E-12); + } + + /// + /// Pins the full bias-corrected limit formula at an asymmetric proportion: two of nine + /// replicates at or below the estimate give proportion 2/10, and the limits follow + /// Φ(2·Φ⁻¹(0.2) + z) exactly. A shifted numerator (3/10) or a B denominator (2/9) breaks it. + /// + [TestMethod] + public void Test_BiasCorrected_AsymmetricProportion_MatchesFormula() + { + var parent = new Normal(2.5, 1d); + var boot = new BootstrapAnalysis(parent, ParameterEstimationMethod.MethodOfMoments, 10, 100); + var values = new double[] { 1d, 2d, 3d, 4d, 4.5, 6d, 7d, 8d, 9d }; + var replicates = new IUnivariateDistribution[values.Length]; + for (int i = 0; i < values.Length; i++) + replicates[i] = new Deterministic(values[i]); + + double alpha = 0.1; + var ci = boot.BiasCorrectedQuantileCI(new double[] { 0.5 }, alpha, replicates); + + double bias = Normal.StandardZ(2d / 10d); + double lower = Statistics.Percentile(values, Normal.StandardCDF(2d * bias + Normal.StandardZ(alpha / 2d)), true); + double upper = Statistics.Percentile(values, Normal.StandardCDF(2d * bias + Normal.StandardZ(1d - alpha / 2d)), true); + Assert.AreEqual(lower, ci[0, 0], 1E-12); + Assert.AreEqual(upper, ci[0, 1], 1E-12); + } + /// /// A sampled distribution that failed to fit contributes a NaN parameter set rather than a /// null entry, matching BootstrapAnalysis.ParameterSets. Consumers index the array directly. From d0679248fb9dab0ccb5a406c7f33a08ac6beff1d Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 29 Jul 2026 10:52:29 -0600 Subject: [PATCH 033/222] Pin the reading contract for version 2.1.4 distribution XML Golden fixtures captured verbatim from the 2.1.4 writers pin how the strict factory treats files produced by earlier releases: well-formed scalar and competing-risks payloads load with their exact values, while NaN parameters, missing attributes, unknown types, truncated or unparseable correlation matrices, and tableless empirical or kernel-density elements are rejected loudly instead of loading degraded values. Capturing the fixtures also established that the 2.1.4 scalar writer faulted on the table-bearing types, so no legacy scalar-writer empirical or kernel-density files exist. All four target frameworks pass 2007/2007. --- .../Univariate/Test_LegacyDistributionXml.cs | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 Test_Numerics/Distributions/Univariate/Test_LegacyDistributionXml.cs diff --git a/Test_Numerics/Distributions/Univariate/Test_LegacyDistributionXml.cs b/Test_Numerics/Distributions/Univariate/Test_LegacyDistributionXml.cs new file mode 100644 index 00000000..e07d0baa --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Test_LegacyDistributionXml.cs @@ -0,0 +1,145 @@ +using System.Xml.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Data.Statistics; +using Numerics.Distributions; +using System; +using System.Linq; + +namespace Distributions.Univariate +{ + /// + /// Golden-fixture tests reading distribution XML exactly as version 2.1.4 wrote it. + /// + /// + /// + /// Authors: + /// + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + /// + /// The fixture strings below were captured verbatim from the 2.1.4 writers. They pin the + /// factory's contract for files produced by earlier releases: well-formed payloads load with + /// their exact values, while payloads carrying non-finite parameters, missing attributes, + /// unknown types, or damaged correlation matrices are rejected loudly instead of loading + /// degraded values. The 2.1.4 scalar writer could not serialize the table-bearing + /// EmpiricalDistribution or KernelDensity at all (it faulted on their parameter arrays), so no + /// legacy scalar-writer files exist for those types; the tableless-element tests cover + /// externally authored payloads. + /// + /// + [TestClass] + public class Test_LegacyDistributionXml + { + private const string NormalAllAttributes = + ""; + + private const string GevAllAttributes = + ""; + + private const string NormalNaNParameters = + ""; + + private const string CompetingRisksCorrelationMatrix = + "1|0.5" + + "0.5|1"; + + /// + /// A well-formed 2.1.4 scalar payload loads with its exact parameter values. + /// + [TestMethod] + public void LegacyScalarXml_AllAttributes_Loads() + { + var normal = (Normal)UnivariateDistributionFactory.CreateDistribution(XElement.Parse(NormalAllAttributes)); + Assert.AreEqual(3.5, normal.Mu, 0d); + Assert.AreEqual(0.75, normal.Sigma, 0d); + + var gev = (GeneralizedExtremeValue)UnivariateDistributionFactory.CreateDistribution(XElement.Parse(GevAllAttributes)); + Assert.AreEqual(100d, gev.Xi, 0d); + Assert.AreEqual(25d, gev.Alpha, 0d); + Assert.AreEqual(-0.1, gev.Kappa, 0d); + } + + /// + /// A 2.1.4 payload carrying NaN parameters (a failed-fit artifact the old writer emitted + /// silently) is rejected instead of loading a degraded distribution. + /// + [TestMethod] + public void LegacyScalarXml_NaNParameters_Rejected() + { + Assert.Throws( + () => UnivariateDistributionFactory.CreateDistribution(XElement.Parse(NormalNaNParameters))); + } + + /// + /// A legacy payload missing a parameter attribute is rejected instead of defaulting the + /// parameter to zero. + /// + [TestMethod] + public void LegacyScalarXml_MissingParameterAttribute_Rejected() + { + var element = XElement.Parse(NormalAllAttributes); + element.Attribute("Sigma")!.Remove(); + Assert.Throws(() => UnivariateDistributionFactory.CreateDistribution(element)); + } + + /// + /// A legacy payload whose type is not a defined distribution is rejected instead of + /// defaulting to a deterministic distribution. + /// + [TestMethod] + public void LegacyScalarXml_UnknownType_Rejected() + { + var element = XElement.Parse(NormalAllAttributes); + element.SetAttributeValue("Type", "NotADistribution"); + Assert.Throws(() => UnivariateDistributionFactory.CreateDistribution(element)); + } + + /// + /// A well-formed 2.1.4 competing-risks payload loads with its children, dependency, and + /// correlation matrix intact. + /// + [TestMethod] + public void LegacyCompetingRisksXml_WellFormed_Loads() + { + var element = XElement.Parse(CompetingRisksCorrelationMatrix); + var risks = (CompetingRisks)UnivariateDistributionFactory.CreateDistribution(element); + Assert.HasCount(2, risks.Distributions); + Assert.AreEqual(Probability.DependencyType.CorrelationMatrix, risks.Dependency); + Assert.AreEqual(0.5, risks.CorrelationMatrix[0, 1], 0d); + Assert.AreEqual(10d, ((Normal)risks.Distributions[0]).Mu, 0d); + Assert.AreEqual(3d, ((Normal)risks.Distributions[1]).Sigma, 0d); + } + + /// + /// A competing-risks payload with a truncated or unparseable correlation matrix is rejected + /// instead of loading NaN matrix entries. + /// + [TestMethod] + public void LegacyCompetingRisksXml_DamagedCorrelationMatrix_Rejected() + { + var truncated = XElement.Parse(CompetingRisksCorrelationMatrix); + truncated.Element("CorrelationMatrix")!.Elements("Correlation_Row").Last().Remove(); + Assert.Throws(() => UnivariateDistributionFactory.CreateDistribution(truncated)); + + var corrupted = XElement.Parse(CompetingRisksCorrelationMatrix); + corrupted.Element("CorrelationMatrix")!.Elements("Correlation_Row").First().Value = "1|abc"; + Assert.Throws(() => UnivariateDistributionFactory.CreateDistribution(corrupted)); + } + + /// + /// Tableless empirical and kernel-density elements are rejected: the table attributes are + /// required, and no legacy writer ever produced a valid tableless payload. + /// + [TestMethod] + public void LegacyTableBearingXml_TablelessElements_Rejected() + { + Assert.Throws(() => UnivariateDistributionFactory.CreateDistribution( + XElement.Parse(""))); + Assert.Throws(() => UnivariateDistributionFactory.CreateDistribution( + XElement.Parse(""))); + } + } +} From c2643927056da7421b95a941274f1765226413ad Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 29 Jul 2026 11:04:02 -0600 Subject: [PATCH 034/222] Represent a constant kernel-density sample as a near-point mass A zero-dispersion sample previously received a bandwidth of its own magnitude times the Silverman factor, silently converting an exactly repeated observation into a spread proportional to its value and units: a constant sample at 5 acquired a standard deviation near 3.8, and one at 1e6 near 421,000. A constant sample - including a single observation, whose sample dispersion is undefined - now takes a bandwidth of its magnitude times 1e-9 (1e-9 absolute for an all-zero constant), representing the observed value as a near-point mass without inventing uncertainty. The variance-overflow path for genuinely spread samples keeps the magnitude-scaled rule. The four degenerate-sample tests pin the new convention; all four target frameworks pass 2007/2007 (one external USGS service outage re-run green). --- .../Distributions/Univariate/KernelDensity.cs | 45 +++++++++++++++++-- .../Univariate/Test_KernelDensity.cs | 35 ++++++++------- 2 files changed, 60 insertions(+), 20 deletions(-) diff --git a/Numerics/Distributions/Univariate/KernelDensity.cs b/Numerics/Distributions/Univariate/KernelDensity.cs index 9f8aeae3..4f7cb6d4 100644 --- a/Numerics/Distributions/Univariate/KernelDensity.cs +++ b/Numerics/Distributions/Univariate/KernelDensity.cs @@ -553,6 +553,18 @@ public double BandwidthRule(IList sample, IList? w = null) return EnsurePositiveBandwidth(sd, factor, sample); } + /// + /// The relative bandwidth assigned to a zero-dispersion (constant) sample, applied to the + /// constant's magnitude so the density represents a near-point mass. + /// + public const double DegenerateRelativeBandwidth = 1E-9; + + /// + /// The absolute bandwidth assigned when a zero-dispersion sample supplies no usable + /// magnitude (an all-zero or subnormal constant). + /// + public const double DegenerateAbsoluteBandwidth = 1E-9; + /// /// Produces a finite, strictly positive automatic bandwidth when the sample dispersion is zero or non-finite. /// @@ -561,14 +573,39 @@ public double BandwidthRule(IList sample, IList? w = null) /// The finite, nonempty sample used to obtain a fallback scale. /// A finite, strictly positive bandwidth. /// - /// A constant sample has zero dispersion but remains a valid empirical sample. In that case, the - /// largest absolute observation supplies a scale; an all-zero sample uses unit scale. The lower - /// bound prevents underflow for subnormal sample values, while the upper guard prevents overflow. + /// A constant sample — including a single observation, whose sample dispersion is undefined — + /// supports no spread estimate, so the density must not invent one from the constant's + /// magnitude: the bandwidth is the magnitude times , + /// a near-point mass at the observed value, falling back to + /// when the constant is zero or so small the product + /// underflows. A non-finite dispersion on a genuinely spread sample arises only from variance + /// overflow; the largest absolute observation then supplies the scale for the standard + /// bandwidth rule, with guards against overflow and underflow. /// private static double EnsurePositiveBandwidth(double dispersion, double factor, IList sample) { + if (!Tools.IsFinite(dispersion) || dispersion <= 0d) + { + bool constant = true; + for (int i = 1; i < sample.Count; i++) + { + if (sample[i] != sample[0]) + { + constant = false; + break; + } + } + + if (constant) + { + double magnitude = Math.Abs(sample[0]); + double degenerate = magnitude * DegenerateRelativeBandwidth; + return degenerate > 0d && Tools.IsFinite(degenerate) ? degenerate : DegenerateAbsoluteBandwidth; + } + } + double scale = dispersion; - if (!Tools.IsFinite(scale) || scale <= 0d) + if (!Tools.IsFinite(scale)) { scale = 0d; for (int i = 0; i < sample.Count; i++) diff --git a/Test_Numerics/Distributions/Univariate/Test_KernelDensity.cs b/Test_Numerics/Distributions/Univariate/Test_KernelDensity.cs index 12d7e83d..132da67d 100644 --- a/Test_Numerics/Distributions/Univariate/Test_KernelDensity.cs +++ b/Test_Numerics/Distributions/Univariate/Test_KernelDensity.cs @@ -75,65 +75,68 @@ public void Test_KernelDensity_CDF() } /// - /// Verifies that automatic bandwidth selection supports a constant nonzero sample. + /// Verifies that a constant nonzero sample yields a near-point-mass bandwidth proportional + /// to the constant's magnitude rather than a fabricated spread. /// [TestMethod] - public void ConstantNonzeroSample_UsesPositiveScaleAwareBandwidth() + public void ConstantNonzeroSample_UsesNearPointMassBandwidth() { double[] constantSample = { 5d, 5d, 5d, 5d }; - double expected = 5d * Math.Pow(4d / (3d * constantSample.Length), 0.2d); + double expected = 5d * KernelDensity.DegenerateRelativeBandwidth; var distribution = new KernelDensity(constantSample); - Assert.AreEqual(expected, distribution.Bandwidth, 1E-12); + Assert.AreEqual(expected, distribution.Bandwidth, 0d); Assert.IsTrue(Tools.IsFinite(distribution.PDF(5d))); Assert.IsGreaterThan(0d, distribution.PDF(5d)); } /// - /// Verifies that automatic bandwidth selection supports a single finite observation. + /// Verifies that a single finite observation yields a near-point-mass bandwidth at the + /// observed value: a one-point sample supports no dispersion estimate. /// [TestMethod] - public void SingleObservation_UsesPositiveScaleAwareBandwidth() + public void SingleObservation_UsesNearPointMassBandwidth() { double[] sample = { 7d }; - double expected = 7d * Math.Pow(4d / 3d, 0.2d); + double expected = 7d * KernelDensity.DegenerateRelativeBandwidth; var distribution = new KernelDensity(sample); - Assert.AreEqual(expected, distribution.Bandwidth, 1E-12); + Assert.AreEqual(expected, distribution.Bandwidth, 0d); Assert.IsTrue(Tools.IsFinite(distribution.PDF(7d))); Assert.IsGreaterThan(0d, distribution.PDF(7d)); } /// - /// Verifies that automatic bandwidth selection supplies a positive unit-scale fallback for an all-zero sample. + /// Verifies that an all-zero sample yields the absolute degenerate bandwidth: the constant + /// supplies no magnitude, and no spread is invented for it. /// [TestMethod] - public void ConstantZeroSample_UsesPositiveUnitScaleBandwidth() + public void ConstantZeroSample_UsesAbsoluteDegenerateBandwidth() { double[] constantSample = { 0d, 0d, 0d, 0d }; - double expected = Math.Pow(4d / (3d * constantSample.Length), 0.2d); var distribution = new KernelDensity(constantSample); - Assert.AreEqual(expected, distribution.Bandwidth, 1E-12); + Assert.AreEqual(KernelDensity.DegenerateAbsoluteBandwidth, distribution.Bandwidth, 0d); Assert.IsTrue(Tools.IsFinite(distribution.PDF(0d))); Assert.IsGreaterThan(0d, distribution.PDF(0d)); } /// - /// Verifies that weighted automatic bandwidth selection supports a constant sample. + /// Verifies that a weighted constant sample yields the near-point-mass bandwidth from the + /// constant's magnitude, independent of the weights. /// [TestMethod] - public void WeightedConstantSample_UsesPositiveScaleAwareBandwidth() + public void WeightedConstantSample_UsesNearPointMassBandwidth() { double[] constantSample = { -3d, -3d, -3d, -3d }; double[] weights = { 1d, 2d, 3d, 4d }; - double expected = 3d * Math.Pow(4d / (3d * constantSample.Length), 0.2d); + double expected = 3d * KernelDensity.DegenerateRelativeBandwidth; var distribution = new KernelDensity(constantSample, weights); - Assert.AreEqual(expected, distribution.Bandwidth, 1E-12); + Assert.AreEqual(expected, distribution.Bandwidth, 0d); Assert.IsTrue(Tools.IsFinite(distribution.PDF(-3d))); Assert.IsGreaterThan(0d, distribution.PDF(-3d)); } From 016a0b480ea58a8fc75d78dc07b175505d0bafd7 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 29 Jul 2026 11:11:19 -0600 Subject: [PATCH 035/222] Harden the default MVN quadrature seed and document per-sampler acceptance DefaultMVNUNISeed becomes a static readonly field so the published value can be revised without stale inlined copies in compiled consumers, and its documentation states the aggregation caveat: instances left at the shared default replay identical lattice shifts, so their quadrature errors are correlated and do not average out - callers aggregating many CDF evaluations should derive per-instance seeds. A three-dimensional test pins bit-identical CDF values across default-seeded instances and their equivalence to explicit seeding (two dimensions use the closed bivariate form and cannot detect a seeding regression). MCMCResults.AcceptanceRates now documents its per-sampler meaning: the Hamiltonian acceptance statistic for NUTS, the accepted-transition fraction for every other sampler. All four target frameworks pass 2008/2008. --- .../Multivariate/MultivariateNormal.cs | 16 ++++++-- Numerics/Sampling/MCMC/Support/MCMCResults.cs | 4 +- .../Multivariate/Test_MultivariateNormal.cs | 37 +++++++++++++++++++ 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/Numerics/Distributions/Multivariate/MultivariateNormal.cs b/Numerics/Distributions/Multivariate/MultivariateNormal.cs index c9f2c879..63d51872 100644 --- a/Numerics/Distributions/Multivariate/MultivariateNormal.cs +++ b/Numerics/Distributions/Multivariate/MultivariateNormal.cs @@ -84,9 +84,16 @@ public MultivariateNormal(double[] mean, double[,] covariance) private bool _covSRTed = false; /// - /// The constant default seed used for reproducible evaluations. + /// The default seed used for reproducible evaluations. /// - public const int DefaultMVNUNISeed = 12345; + /// + /// Every instance left at the default replays the identical lattice shifts, so the small + /// quadrature errors of separate instances are correlated rather than independent — they do + /// not average out when many CDF evaluations are aggregated. Callers aggregating across many + /// instances should derive per-instance seeds (for example, from model content) through + /// or a consumer-level seed property. + /// + public static readonly int DefaultMVNUNISeed = 12345; /// /// The uniform(0,1) random number generator required to compute the multivariate CDF for dimensions greater than 2. @@ -95,8 +102,9 @@ public MultivariateNormal(double[] mean, double[,] covariance) /// MVNDST is a randomized lattice rule and draws from this generator, so the CDF above two /// dimensions carries a small stochastic error and only reproduces when the generator is /// seeded. The default is the fixed ; assign a seeded - /// generator to tie results to a caller's own seed. Not thread-safe — MVNDST advances the - /// generator, so an instance shared across threads must be cloned per thread. + /// generator to tie results to a caller's own seed and to decorrelate the quadrature error + /// across instances. Not thread-safe — MVNDST advances the generator, so an instance shared + /// across threads must be cloned per thread. /// /// Thrown when the assigned generator is null. public Random MVNUNI diff --git a/Numerics/Sampling/MCMC/Support/MCMCResults.cs b/Numerics/Sampling/MCMC/Support/MCMCResults.cs index 1e15fec3..04c2b83a 100644 --- a/Numerics/Sampling/MCMC/Support/MCMCResults.cs +++ b/Numerics/Sampling/MCMC/Support/MCMCResults.cs @@ -81,7 +81,9 @@ public MCMCResults(ParameterSet map, IList parameterSets, double a public List? MeanLogLikelihood { get; private set; } /// - /// The acceptance rate for each chain. + /// The acceptance rate for each chain. For NUTS samplers this is the mean Hamiltonian + /// acceptance statistic (the quantity step-size adaptation targets); for every other + /// sampler it is the fraction of iterations whose proposal was accepted. /// [JsonInclude] public double[] AcceptanceRates { get; private set; } = null!; diff --git a/Test_Numerics/Distributions/Multivariate/Test_MultivariateNormal.cs b/Test_Numerics/Distributions/Multivariate/Test_MultivariateNormal.cs index b67b5dd8..050042e0 100644 --- a/Test_Numerics/Distributions/Multivariate/Test_MultivariateNormal.cs +++ b/Test_Numerics/Distributions/Multivariate/Test_MultivariateNormal.cs @@ -231,6 +231,43 @@ public void Test_MultivariateNormalCDF_Fortran() } + /// + /// Verifies that two identically constructed instances left at the default generator produce + /// bit-identical CDF values above two dimensions, and that the default equals explicit + /// seeding with the published default seed. Three dimensions are required: two dimensions + /// use a closed bivariate form that cannot detect a seeding regression. + /// + [TestMethod] + public void Test_CDF_DefaultSeed_IsBitReproducibleAcrossInstances() + { + Assert.AreEqual(12345, MultivariateNormal.DefaultMVNUNISeed); + + var mean = new[] { 0d, 0d, 0d }; + var covariance = new[,] + { + { 1.0, 0.5, 0.3 }, + { 0.5, 1.0, 0.4 }, + { 0.3, 0.4, 1.0 } + }; + var point = new[] { 0.5, 0.2, -0.3 }; + + // MVNDST advances the generator on every evaluation, so each instance is evaluated + // exactly once and the captured values are compared. + double firstValue = new MultivariateNormal(mean, covariance).CDF(point); + double secondValue = new MultivariateNormal(mean, covariance).CDF(point); + Assert.AreEqual( + BitConverter.DoubleToInt64Bits(firstValue), + BitConverter.DoubleToInt64Bits(secondValue)); + + var explicitlySeeded = new MultivariateNormal(mean, covariance) + { + MVNUNI = new MersenneTwister(MultivariateNormal.DefaultMVNUNISeed) + }; + Assert.AreEqual( + BitConverter.DoubleToInt64Bits(firstValue), + BitConverter.DoubleToInt64Bits(explicitlySeeded.CDF(point))); + } + /// /// Verifies the documented invalid-dimension termination status without entering MVNDNT initialization. /// From 3e69a9388bbecaad89960679301b3bd8d685b88d Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 31 Jul 2026 10:47:15 -0600 Subject: [PATCH 036/222] Fix mixture simplex and positive-hurdle behavior --- Numerics/Distributions/Univariate/Mixture.cs | 783 +++++++++++------- .../Univariate/Test_Mixture_Phase4.cs | 246 ++++++ 2 files changed, 707 insertions(+), 322 deletions(-) create mode 100644 Test_Numerics/Distributions/Univariate/Test_Mixture_Phase4.cs diff --git a/Numerics/Distributions/Univariate/Mixture.cs b/Numerics/Distributions/Univariate/Mixture.cs index 070ced25..ee49871b 100644 --- a/Numerics/Distributions/Univariate/Mixture.cs +++ b/Numerics/Distributions/Univariate/Mixture.cs @@ -15,6 +15,13 @@ namespace Numerics.Distributions /// /// /// + /// When zero inflation is enabled, the distribution is a positive-hurdle mixture: + /// the zero atom has probability , component weights sum to + /// the remaining mass, and every component is conditioned on a value strictly greater + /// than zero. at zero reports the atom probability under the + /// mixed Lebesgue-plus-Dirac reference measure. + /// + /// /// Authors: /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil /// @@ -148,6 +155,133 @@ private void NormalizeComponentWeights() } } + /// + /// Determines whether a value is finite on every target framework. + /// + /// The value to inspect. + /// when the value is neither NaN nor infinite. + private static bool IsFinite(double value) + { + return !double.IsNaN(value) && !double.IsInfinity(value); + } + + /// + /// Restricts a value to an inclusive interval on every target framework. + /// + /// The value to restrict. + /// The inclusive lower bound. + /// The inclusive upper bound. + /// The restricted value. + private static double Clamp(double value, double minimum, double maximum) + { + return value < minimum ? minimum : value > maximum ? maximum : value; + } + + /// + /// Returns the next representable value greater than the supplied value. + /// + /// The starting value. + /// The adjacent representable value toward positive infinity. + private static double BitIncrement(double value) + { + if (double.IsNaN(value) || value == double.PositiveInfinity) return value; + if (value == 0.0) return double.Epsilon; + long bits = BitConverter.DoubleToInt64Bits(value); + return BitConverter.Int64BitsToDouble(value > 0.0 ? bits + 1 : bits - 1); + } + + /// + /// Returns the next representable value less than the supplied value. + /// + /// The starting value. + /// The adjacent representable value toward negative infinity. + private static double BitDecrement(double value) + { + if (double.IsNaN(value) || value == double.NegativeInfinity) return value; + if (value == 0.0) return -double.Epsilon; + long bits = BitConverter.DoubleToInt64Bits(value); + return BitConverter.Int64BitsToDouble(value > 0.0 ? bits - 1 : bits + 1); + } + + /// + /// Converts a unit-interval draw to a component CDF probability conditional on a positive value. + /// + /// The zero-based component index. + /// The probability on the positive-conditional scale. + /// A component CDF probability strictly above the CDF at zero and strictly below one. + private double PositiveConditionalQuantileProbability(int componentIndex, double conditionalProbability) + { + TryGetPositiveMass(componentIndex, out double positiveMass); + double cdfAtZero = Distributions[componentIndex].CDF(0.0); + double probability = cdfAtZero + conditionalProbability * positiveMass; + if (probability <= cdfAtZero) probability = BitIncrement(cdfAtZero); + if (probability >= 1.0) probability = BitDecrement(1.0); + return probability; + } + /// + /// Gets the probability that a component produces a strictly positive value. + /// + /// The zero-based component index. + /// The strictly positive probability mass. + /// when the mass is finite and positive; otherwise, . + private bool TryGetPositiveMass(int componentIndex, out double positiveMass) + { + positiveMass = Distributions[componentIndex].CCDF(0.0); + return IsFinite(positiveMass) && positiveMass > 0.0; + } + + /// + /// Evaluates a component density conditional on a strictly positive value. + /// + /// The zero-based component index. + /// The value at which to evaluate the density. + /// The positive-conditional density. + private double PositiveConditionalPDF(int componentIndex, double x) + { + return x > 0.0 && TryGetPositiveMass(componentIndex, out double positiveMass) + ? Distributions[componentIndex].PDF(x) / positiveMass + : 0.0; + } + + /// + /// Evaluates a component log density conditional on a strictly positive value. + /// + /// The zero-based component index. + /// The value at which to evaluate the log density. + /// The positive-conditional log density. + private double PositiveConditionalLogPDF(int componentIndex, double x) + { + return x > 0.0 && TryGetPositiveMass(componentIndex, out double positiveMass) + ? Distributions[componentIndex].LogPDF(x) - Math.Log(positiveMass) + : double.NegativeInfinity; + } + + /// + /// Evaluates a component distribution function conditional on a strictly positive value. + /// + /// The zero-based component index. + /// The value at which to evaluate the distribution function. + /// The positive-conditional cumulative probability. + private double PositiveConditionalCDF(int componentIndex, double x) + { + if (x <= 0.0 || !TryGetPositiveMass(componentIndex, out double positiveMass)) return 0.0; + double probability = (Distributions[componentIndex].CDF(x) - Distributions[componentIndex].CDF(0.0)) / positiveMass; + return Clamp(probability, 0.0, 1.0); + } + + /// + /// Evaluates a component survival function conditional on a strictly positive value. + /// + /// The zero-based component index. + /// The value at which to evaluate the survival function. + /// The positive-conditional survival probability. + private double PositiveConditionalCCDF(int componentIndex, double x) + { + if (x < 0.0) return 1.0; + if (!TryGetPositiveMass(componentIndex, out double positiveMass)) return double.NaN; + double probability = Distributions[componentIndex].CCDF(x) / positiveMass; + return Clamp(probability, 0.0, 1.0); + } /// /// Refreshes validity and cached results after zero-inflation configuration changes. /// @@ -381,7 +515,7 @@ public override double Kurtosis /// public override double Minimum { - get { return Distributions.Min(p => p.Minimum); } + get { return IsZeroInflated ? 0.0 : Distributions.Min(p => p.Minimum); } } /// @@ -464,8 +598,8 @@ public void SetParameters(double[] weights, UnivariateDistributionBase[] distrib if (weights.Length != distributions.Length) throw new ArgumentException("The weight and distribution arrays must have the same length.", nameof(Weights)); - _weights = weights; - _distributions = distributions; + _weights = weights.ToArray(); + _distributions = distributions.ToArray(); _parametersValid = ValidateParameters(GetParameters, false) is null; _momentsComputed = false; _empiricalCDFCreated = false; @@ -483,7 +617,7 @@ public void SetParameters(double[] weights, IUnivariateDistribution[] distributi if (weights.Length != distributions.Length) throw new ArgumentException("The weight and distribution arrays must have the same length.", nameof(Weights)); - _weights = weights; + _weights = weights.ToArray(); _distributions = new UnivariateDistributionBase[distributions.Length]; for (int i = 0; i < distributions.Length; i++) { @@ -502,6 +636,7 @@ public void SetParameters(double[] weights, IUnivariateDistribution[] distributi public void SetParameters(double[] weights, double[] parameters) { if (weights == null) throw new ArgumentNullException(nameof(Weights)); + if (parameters == null) throw new ArgumentNullException(nameof(parameters)); if (weights.Length != Distributions.Length) throw new ArgumentException("The weight and distribution arrays must have the same length.", nameof(Weights)); if (parameters.Length != Distributions.Sum(x => x.NumberOfParameters)) @@ -509,6 +644,8 @@ public void SetParameters(double[] weights, double[] parameters) throw new ArgumentException("The length of the parameter array is invalid.", nameof(parameters)); } + double[] parameterCopy = parameters.ToArray(); + // Set weights _weights = weights.ToArray(); // Set distribution parameters @@ -518,7 +655,7 @@ public void SetParameters(double[] weights, double[] parameters) var parms = new List(); for (int j = t; j < t + Distributions[i].NumberOfParameters; j++) { - parms.Add(parameters[j]); + parms.Add(parameterCopy[j]); } Distributions[i].SetParameters(parms); t += Distributions[i].NumberOfParameters; @@ -531,112 +668,89 @@ public void SetParameters(double[] weights, double[] parameters) /// public override void SetParameters(IList parameters) { + if (parameters == null) throw new ArgumentNullException(nameof(parameters)); if (parameters.Count != NumberOfParameters) { throw new ArgumentException("The length of the parameter array is invalid.", nameof(parameters)); } - // Set the weights - int t = 0; + double[] parameterCopy = parameters.ToArray(); + + // Set the weights. + int parameterIndex = 0; for (int i = 0; i < Distributions.Count(); i++) { - Weights[i] = parameters[i]; - t++; + Weights[i] = parameterCopy[parameterIndex++]; } - // Set the distribution parameters + // Set the distribution parameters. for (int i = 0; i < Distributions.Count(); i++) { - var parms = new List(); - for (int j = t; j < t + Distributions[i].NumberOfParameters; j++) - { - parms.Add(parameters[j]); - } - Distributions[i].SetParameters(parms); - t += Distributions[i].NumberOfParameters; + double[] distributionParameters = parameterCopy + .Skip(parameterIndex) + .Take(Distributions[i].NumberOfParameters) + .ToArray(); + Distributions[i].SetParameters(distributionParameters); + parameterIndex += Distributions[i].NumberOfParameters; } - // Validate parameters - _parametersValid = ValidateParameters(parameters, false) is null; + + _parametersValid = ValidateParameters(GetParameters, false) is null; _momentsComputed = false; _empiricalCDFCreated = false; } /// - /// Set the distribution parameters from a referenced array. Weights are normalized to sum to 1. + /// Set the distribution parameters from a referenced array. Weights are normalized to the configured simplex. /// - /// The array of parameters. + /// The array of parameters. The caller's array is not modified. public void SetParameters(ref double[] parameters) { if (parameters == null) return; if (Weights == null || Weights.Length == 0) return; if (Distributions == null || Distributions.Count() == 0) return; - if (Distributions.Count() == 1 && parameters.Length == Distributions[0].NumberOfParameters) + + double[] parameterCopy = parameters.ToArray(); + if (Distributions.Count() == 1 && parameterCopy.Length == Distributions[0].NumberOfParameters) { - if (IsZeroInflated) - { - Weights[0] = 1 - ZeroWeight; - } - else - { - Weights[0] = 1; - } - Distributions[0].SetParameters(parameters); + Weights[0] = IsZeroInflated ? 1.0 - ZeroWeight : 1.0; + Distributions[0].SetParameters(parameterCopy); } else { - - // Get the weights - int K = Distributions.Count(); - int t = 0; // keep track of parameter index - - // Get weights - double sum = 0.0; - for (int i = 0; i < K; i++) + int componentCount = Distributions.Count(); + int parameterIndex = componentCount; + double weightSum = 0.0; + + for (int i = 0; i < componentCount; i++) { - Weights[i] = parameters[i]; - sum += Weights[i]; - t++; + Weights[i] = parameterCopy[i]; + weightSum += Weights[i]; } - // Check if weights need to be normalized - if (sum <= 0.0) + double componentMass = IsZeroInflated ? 1.0 - ZeroWeight : 1.0; + if (weightSum <= 0.0 || !IsFinite(weightSum)) { - // If weights sum to 0, reset to be uniformly distributed - double w = IsZeroInflated ? (1d - ZeroWeight) / K : 1d / K; - for (int i = 0; i < K; i++) - { - Weights[i] = w; - parameters[i] = Weights[i]; - } - } + double uniformWeight = componentMass / componentCount; + for (int i = 0; i < componentCount; i++) Weights[i] = uniformWeight; + } else { - // Normalize weights to sum to 1. - var c = IsZeroInflated ? (1d - ZeroWeight) / sum : 1d / sum; - // Normalize weights - for (int i = 0; i < K; i++) - { - Weights[i] *= c; - parameters[i] = Weights[i]; - } + double scale = componentMass / weightSum; + for (int i = 0; i < componentCount; i++) Weights[i] *= scale; } - - // Set distribution parameters - for (int i = 0; i < Distributions.Count(); i++) + for (int i = 0; i < componentCount; i++) { - var parms = new List(); - for (int j = t; j < t + Distributions[i].NumberOfParameters; j++) - { - parms.Add(parameters[j]); - } - Distributions[i].SetParameters(parms); - t += Distributions[i].NumberOfParameters; + double[] distributionParameters = parameterCopy + .Skip(parameterIndex) + .Take(Distributions[i].NumberOfParameters) + .ToArray(); + Distributions[i].SetParameters(distributionParameters); + parameterIndex += Distributions[i].NumberOfParameters; } } - // Validate parameters - _parametersValid = ValidateParameters(parameters, false) is null; + _parametersValid = ValidateParameters(GetParameters, false) is null; _momentsComputed = false; _empiricalCDFCreated = false; } @@ -644,42 +758,61 @@ public void SetParameters(ref double[] parameters) /// public override ArgumentOutOfRangeException? ValidateParameters(IList parameters, bool throwException) { - // Check if weights are between 0 and 1. - if (IsZeroInflated && (ZeroWeight < 0.0 || ZeroWeight > 1.0)) + if (IsZeroInflated && (!IsFinite(ZeroWeight) || ZeroWeight < 0.0 || ZeroWeight >= 1.0)) { - if (throwException) - throw new ArgumentOutOfRangeException(nameof(ZeroWeight), "The zero value weight must be between 0 and 1."); - return new ArgumentOutOfRangeException(nameof(ZeroWeight), "The zero value weight must be between 0 and 1."); + var exception = new ArgumentOutOfRangeException( + nameof(ZeroWeight), + "The zero value weight must be finite and greater than or equal to 0 and less than 1."); + if (throwException) throw exception; + return exception; } + for (int i = 0; i < Distributions.Count(); i++) { - if (Weights[i] < 0.0 || Weights[i] > 1.0) + if (!IsFinite(Weights[i]) || Weights[i] < 0.0 || Weights[i] > 1.0) { - if (throwException) - throw new ArgumentOutOfRangeException(nameof(Weights), "The weights must be between 0 and 1."); - return new ArgumentOutOfRangeException(nameof(Weights), "The weights must be between 0 and 1."); + var exception = new ArgumentOutOfRangeException( + nameof(Weights), + "The weights must be finite and between 0 and 1."); + if (throwException) throw exception; + return exception; } } - // Check if weights sum to 1. - double sum = IsZeroInflated ? ZeroWeight : 0.0; - for (int i = 0; i < Distributions.Count(); i++) - sum += Weights[i]; - if (sum.AlmostEquals(1d, 1E-8) == false) + + double totalMass = IsZeroInflated ? ZeroWeight : 0.0; + for (int i = 0; i < Distributions.Count(); i++) totalMass += Weights[i]; + if (!IsFinite(totalMass) || !totalMass.AlmostEquals(1.0, 1E-8)) { - if (throwException) - throw new ArgumentOutOfRangeException(nameof(Weights), "The weights must sum to 1.0."); - return new ArgumentOutOfRangeException(nameof(Weights), "The weights must sum to 1.0."); + var exception = new ArgumentOutOfRangeException( + nameof(Weights), + IsZeroInflated + ? "The component weights must sum to 1 minus the zero value weight." + : "The weights must sum to 1.0."); + if (throwException) throw exception; + return exception; } - // Check if distributions are valid + for (int i = 0; i < Distributions.Count(); i++) { - if (Distributions[i].ParametersValid == false) + if (!Distributions[i].ParametersValid) + { + var exception = new ArgumentOutOfRangeException( + nameof(Distributions), + "Distribution " + (i + 1).ToString() + " has invalid parameters."); + if (throwException) throw exception; + return exception; + } + + if (IsZeroInflated && !TryGetPositiveMass(i, out _)) { - if (throwException) - throw new ArgumentOutOfRangeException(nameof(Distributions), "Distribution " + (i + 1).ToString() + " has invalid parameters."); - return new ArgumentOutOfRangeException(nameof(Distributions), "Distribution " + (i + 1).ToString() + " has invalid parameters."); + var exception = new ArgumentOutOfRangeException( + nameof(Distributions), + "Distribution " + (i + 1).ToString() + " must have finite, positive probability above zero."); + if (throwException) throw exception; + return exception; } } + return null; } @@ -721,338 +854,342 @@ public Tuple GetParameterConstraints(IList /// public double[] MLE(IList sample) { + ValidateParameters(GetParameters, true); - int N = sample.Count; - int Np = Distributions.Sum(x => x.NumberOfParameters); - int K = Distributions.Count(); + int observationCount = sample.Count; + int distributionParameterCount = Distributions.Sum(x => x.NumberOfParameters); + int componentCount = Distributions.Count(); - // Set constraints - var tuple = GetParameterConstraints(sample); - var Initials = tuple.Item1.Subset(K); - var Lowers = tuple.Item2.Subset(K); - var Uppers = tuple.Item3.Subset(K); + if (IsZeroInflated) + { + for (int rowIndex = 0; rowIndex < observationCount; rowIndex++) + { + if (sample[rowIndex] < 0.0) + { + throw new InvalidOperationException( + "Mixture EM row " + rowIndex.ToString(CultureInfo.InvariantCulture) + + " has negative exact value " + sample[rowIndex].ToString("R", CultureInfo.InvariantCulture) + + " in a zero-inflated model."); + } + } + } + + Tuple constraints = GetParameterConstraints(sample); + double[] initialParameters = constraints.Item1.Subset(componentCount); + double[] lowerParameters = constraints.Item2.Subset(componentCount); + double[] upperParameters = constraints.Item3.Subset(componentCount); - // Set up EM parameters - var mleWeights = tuple.Item1.Subset(0 , K - 1); - var mleParameters = Initials; - var likelihood = new double[N, K]; - double oldLogLH = double.MinValue, newLogLH = double.MinValue; + double[] mleWeights = constraints.Item1.Subset(0, componentCount - 1); + double[] mleParameters = initialParameters; + var responsibilities = new double[observationCount, componentCount]; + double oldLogLikelihood = double.MinValue; + double newLogLikelihood = double.MinValue; - // The Expectation step. - double EStep(double[] x) + double EStep(double[] parameters) { - var dist = (Mixture)Clone(); - dist.SetParameters(mleWeights, x); - // Outer loop for computing the likelihoods - for (int k = 0; k < K; k++) + var distribution = (Mixture)Clone(); + distribution.SetParameters(mleWeights, parameters); + double logLikelihood = 0.0; + + for (int rowIndex = 0; rowIndex < observationCount; rowIndex++) { - for (int i = 0; i < N; i++) + double value = sample[rowIndex]; + if (IsZeroInflated && value == 0.0) { - if (IsZeroInflated && sample[i] <= 0.0) + if (!IsFinite(ZeroWeight) || ZeroWeight <= 0.0) { - likelihood[i, k] = Math.Log(ZeroWeight); + throw CreateImpossibleRowException(rowIndex, value); } - else + + for (int componentIndex = 0; componentIndex < componentCount; componentIndex++) { - likelihood[i, k] = Math.Log(mleWeights[k]) + dist.Distributions[k].LogPDF(sample[i]); + responsibilities[rowIndex, componentIndex] = 0.0; } + logLikelihood += Math.Log(ZeroWeight); + continue; } - } - // At this point we have unnormalized log likelihoods. - // We need to normalize using log-sum-exp and compute the true log-likelihoods. - double logLH = 0; - for (int i = 0; i < N; i++) - { - // Get max likelihood - double max = double.NegativeInfinity; - for (int k = 0; k < K; k++) + + double maximumLogProbability = double.NegativeInfinity; + for (int componentIndex = 0; componentIndex < componentCount; componentIndex++) { - if (likelihood[i, k] > max) - { - max = likelihood[i, k]; - } + double componentLogDensity = IsZeroInflated + ? distribution.PositiveConditionalLogPDF(componentIndex, value) + : distribution.Distributions[componentIndex].LogPDF(value); + double logProbability = Math.Log(mleWeights[componentIndex]) + componentLogDensity; + responsibilities[rowIndex, componentIndex] = logProbability; + if (logProbability > maximumLogProbability) maximumLogProbability = logProbability; + } + + if (!IsFinite(maximumLogProbability)) + { + throw CreateImpossibleRowException(rowIndex, value); } - if (double.IsNegativeInfinity(max)) + + double scaledProbabilitySum = 0.0; + for (int componentIndex = 0; componentIndex < componentCount; componentIndex++) + { + scaledProbabilitySum += Math.Exp(responsibilities[rowIndex, componentIndex] - maximumLogProbability); + } + if (!IsFinite(scaledProbabilitySum) || scaledProbabilitySum <= 0.0) + { + throw CreateImpossibleRowException(rowIndex, value); + } + + double rowLogProbability = maximumLogProbability + Math.Log(scaledProbabilitySum); + if (!IsFinite(rowLogProbability)) { - for (int k = 0; k < K; k++) - likelihood[i, k] = 0.0; - return double.NegativeInfinity; + throw CreateImpossibleRowException(rowIndex, value); } - // log-sum-exp trick begins here - double sum = 0; - for (int k = 0; k < K; k++) - sum += Math.Exp(likelihood[i, k] - max); - double tmp = max + Math.Log(sum); - for (int k = 0; k < K; k++) - likelihood[i, k] = Math.Exp(likelihood[i, k] - tmp); - logLH += tmp; + for (int componentIndex = 0; componentIndex < componentCount; componentIndex++) + { + responsibilities[rowIndex, componentIndex] = + Math.Exp(responsibilities[rowIndex, componentIndex] - rowLogProbability); + } + logLikelihood += rowLogProbability; } - return logLH; + + return logLikelihood; } - // The Maximization step - double[] MStep(double[] x) + double[] MStep(double[] parameters) { - // Get updated weights - for (int k = 0; k < K; k++) + for (int componentIndex = 0; componentIndex < componentCount; componentIndex++) { - double wgt = 0d; - for (int i = 0; i < N; i++) + double weight = 0.0; + for (int rowIndex = 0; rowIndex < observationCount; rowIndex++) { - if (!IsZeroInflated || sample[i] > 0.0 ) + if (!IsZeroInflated || sample[rowIndex] > 0.0) { - wgt += likelihood[i, k]; + weight += responsibilities[rowIndex, componentIndex]; } } - mleWeights[k] = wgt / N; + mleWeights[componentIndex] = weight; } - // Keep component weights on the configured simplex. For a zero-inflated - // mixture, finite-sample zero counts need not equal the fixed ZeroWeight. double componentWeightSum = mleWeights.Sum(); - double componentWeightTarget = IsZeroInflated ? 1d - ZeroWeight : 1d; - if (componentWeightSum > 0d) + double componentWeightTarget = IsZeroInflated ? 1.0 - ZeroWeight : 1.0; + if (!IsFinite(componentWeightSum) || componentWeightSum <= 0.0) { - double scale = componentWeightTarget / componentWeightSum; - for (int k = 0; k < K; k++) - { - mleWeights[k] *= scale; - } + throw new InvalidOperationException("Mixture EM cannot update component weights because no finite positive responsibility mass is available."); } - else + + double scale = componentWeightTarget / componentWeightSum; + for (int componentIndex = 0; componentIndex < componentCount; componentIndex++) { - double weight = componentWeightTarget / K; - for (int k = 0; k < K; k++) mleWeights[k] = weight; + mleWeights[componentIndex] *= scale; } - // MLE - var solver = new NelderMead(logLH, Np, x, Lowers, Uppers); + + var solver = new NelderMead(Objective, distributionParameterCount, parameters, lowerParameters, upperParameters); solver.Maximize(); return solver.BestParameterSet.Values; } - // The log-likelihood to maximize in the M-Step - // Weights are held fixed, only the distribution parameters are solved. - double logLH(double[] x) + double Objective(double[] parameters) { - var dist = (Mixture)Clone(); - dist.SetParameters(mleWeights, x); - double lh = dist.LogLikelihood(sample); - if (double.IsNaN(lh) || double.IsInfinity(lh)) return double.NegativeInfinity; - return lh; + var distribution = (Mixture)Clone(); + distribution.SetParameters(mleWeights, parameters); + double logLikelihood = distribution.LogLikelihood(sample); + return IsFinite(logLikelihood) ? logLikelihood : double.NegativeInfinity; } - // Estimate using the EM Algorithm - for (Iterations = 1; Iterations <= MaxIterations; Iterations++) + InvalidOperationException CreateImpossibleRowException(int rowIndex, double value) { - // Perform the expectation step - newLogLH = EStep(mleParameters); - - // Check convergence before M-step to avoid pushing parameters - // into a degenerate state after the log-likelihood has already converged. - if (Math.Abs((oldLogLH - newLogLH) / oldLogLH) < Tolerance) - break; + return new InvalidOperationException( + "Mixture EM row " + rowIndex.ToString(CultureInfo.InvariantCulture) + + " with value " + value.ToString("R", CultureInfo.InvariantCulture) + + " has zero or nonfinite total probability."); + } - // Perform the maximization step + for (Iterations = 1; Iterations <= MaxIterations; Iterations++) + { + newLogLikelihood = EStep(mleParameters); + if (Math.Abs((oldLogLikelihood - newLogLikelihood) / oldLogLikelihood) < Tolerance) break; mleParameters = MStep(mleParameters); - - // Update log-likelihood state - oldLogLH = newLogLH; - + oldLogLikelihood = newLogLikelihood; } - // Return the full list of distribution parameters var result = new List(); result.AddRange(mleWeights); result.AddRange(mleParameters); return result.ToArray(); } - /// public override double PDF(double x) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(GetParameters, true); + if (!_parametersValid) ValidateParameters(GetParameters, true); - double f = 0.0; - if (IsZeroInflated && x <= 0.0) - { - f = ZeroWeight; - } - else + if (IsZeroInflated) { + if (x < 0.0) return 0.0; + if (x == 0.0) return ZeroWeight; + + double positiveDensity = 0.0; for (int i = 0; i < Distributions.Count(); i++) - f += Weights[i] * Distributions[i].PDF(x); + { + positiveDensity += Weights[i] * PositiveConditionalPDF(i, x); + } + return Math.Max(0.0, positiveDensity); } - return f < 0d ? 0d : f; + + double density = 0.0; + for (int i = 0; i < Distributions.Count(); i++) density += Weights[i] * Distributions[i].PDF(x); + return Math.Max(0.0, density); } /// public override double LogPDF(double x) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(GetParameters, true); + if (!_parametersValid) ValidateParameters(GetParameters, true); - var lnf = new List(); - if (IsZeroInflated && x <= 0.0) + if (IsZeroInflated) { - lnf.Add(Math.Log(ZeroWeight)); + if (x < 0.0) return double.NegativeInfinity; + if (x == 0.0) return Math.Log(ZeroWeight); } - else + + var logDensities = new List(); + for (int i = 0; i < Distributions.Count(); i++) { - for (int i = 0; i < Distributions.Count(); i++) - lnf.Add(Math.Log(Weights[i]) + Distributions[i].LogPDF(x)); + double componentLogDensity = IsZeroInflated + ? PositiveConditionalLogPDF(i, x) + : Distributions[i].LogPDF(x); + logDensities.Add(Math.Log(Weights[i]) + componentLogDensity); } - var f = Tools.LogSumExp(lnf); - return f; + return Tools.LogSumExp(logDensities); } /// public override double CDF(double x) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(GetParameters, true); + if (!_parametersValid) ValidateParameters(GetParameters, true); - double F = 0.0; if (IsZeroInflated) { - F = ZeroWeight; - if (x > 0.0) + if (x < 0.0) return 0.0; + if (x == 0.0) return ZeroWeight; + + double hurdleProbability = ZeroWeight; + for (int i = 0; i < Distributions.Count(); i++) { - for (int i = 0; i < Distributions.Count(); i++) - F += Weights[i] * Distributions[i].CDF(x); + hurdleProbability += Weights[i] * PositiveConditionalCDF(i, x); } + return Clamp(hurdleProbability, 0.0, 1.0); } - else - { - for (int i = 0; i < Distributions.Count(); i++) - F += Weights[i] * Distributions[i].CDF(x); - } - return F < 0d ? 0d : F > 1d ? 1d : F; + + double probability = 0.0; + for (int i = 0; i < Distributions.Count(); i++) probability += Weights[i] * Distributions[i].CDF(x); + return Clamp(probability, 0.0, 1.0); } /// public override double LogCDF(double x) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(GetParameters, true); + if (!_parametersValid) ValidateParameters(GetParameters, true); + if (IsZeroInflated) return Math.Log(CDF(x)); - var lnF = new List(); - if (IsZeroInflated) - { - lnF.Add(Math.Log(ZeroWeight)); - if (x > 0.0) - { - for (int i = 0; i < Distributions.Count(); i++) - lnF.Add(Math.Log(Weights[i]) + Distributions[i].LogCDF(x)); - } - } - else + var logProbabilities = new List(); + for (int i = 0; i < Distributions.Count(); i++) { - for (int i = 0; i < Distributions.Count(); i++) - lnF.Add(Math.Log(Weights[i]) + Distributions[i].LogCDF(x)); + logProbabilities.Add(Math.Log(Weights[i]) + Distributions[i].LogCDF(x)); } - var F = Tools.LogSumExp(lnF); - return F; + return Tools.LogSumExp(logProbabilities); } /// public override double LogCCDF(double x) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(GetParameters, true); + if (!_parametersValid) ValidateParameters(GetParameters, true); - var lnF = new List(); if (IsZeroInflated) { - if (x > 0.0) + if (x < 0.0) return 0.0; + + double probability = 0.0; + for (int i = 0; i < Distributions.Count(); i++) { - for (int i = 0; i < Distributions.Count(); i++) - lnF.Add(Math.Log(Weights[i]) + Distributions[i].LogCCDF(x)); + probability += Weights[i] * PositiveConditionalCCDF(i, x); } + return Math.Log(Clamp(probability, 0.0, 1.0)); } - else + + var logProbabilities = new List(); + for (int i = 0; i < Distributions.Count(); i++) { - for (int i = 0; i < Distributions.Count(); i++) - lnF.Add(Math.Log(Weights[i]) + Distributions[i].LogCCDF(x)); + logProbabilities.Add(Math.Log(Weights[i]) + Distributions[i].LogCCDF(x)); } - var F = Tools.LogSumExp(lnF); - return F; + return Tools.LogSumExp(logProbabilities); } - /// public override double InverseCDF(double probability) { - // Validate probability - if (probability < 0.0d || probability > 1.0d) - throw new ArgumentOutOfRangeException("probability", "Probability must be between 0 and 1."); - if (probability == 0.0d) return Minimum; - if (probability == 1.0d) return Maximum; - if (IsZeroInflated && probability <= ZeroWeight) return 0; - - // Validate parameters - if (_parametersValid == false) - ValidateParameters(GetParameters, true); - - // If there is only one distribution and not zero-inflated, return its inverse CDF + if (probability < 0.0 || probability > 1.0) + throw new ArgumentOutOfRangeException(nameof(probability), "Probability must be between 0 and 1."); + if (probability == 0.0) return Minimum; + if (probability == 1.0) return Maximum; + if (IsZeroInflated && probability <= ZeroWeight) return 0.0; + if (!_parametersValid) ValidateParameters(GetParameters, true); + if (Distributions.Count() == 1 && !IsZeroInflated) { return Distributions[0].InverseCDF(probability); } - double x = 0; - if (_empiricalCDFCreated == true) + if (_empiricalCDFCreated) { - x = _empiricalCDF.InverseCDF(probability); + double empiricalValue = _empiricalCDF.InverseCDF(probability); + return Clamp(empiricalValue, Minimum, Maximum); } - else + + double componentProbability = IsZeroInflated + ? (probability - ZeroWeight) / (1.0 - ZeroWeight) + : probability; + var componentQuantiles = new List(); + for (int i = 0; i < Distributions.Count(); i++) { - // Use a root finder to solve the inverse CDF - // For zero-inflated mixtures, use adjusted probability for lower bracket - // and unadjusted for upper bracket to ensure the root is bracketed - double adjProb = IsZeroInflated ? (probability - ZeroWeight) / (1d - ZeroWeight) : probability; - var minXVals = Distributions.Select(d => d.InverseCDF(adjProb)); - var maxXVals = Distributions.Select(d => d.InverseCDF(probability)); - double minX = minXVals.Min(); - double maxX = maxXVals.Max(); - try - { - if (IsZeroInflated) - { - Brent.Bracket((y) => { return probability - CDF(y); }, ref minX, ref maxX, out var f1, out var f2); - } - x = Brent.Solve((y) => { return probability - CDF(y); }, minX, maxX, 1E-6, 100, true); - } - catch (Exception) + double componentCdfProbability = componentProbability; + if (IsZeroInflated) { - // If the root finder fails, create an empirical CDF - if (_empiricalCDFCreated == false) - CreateEmpiricalCDF(); - x = _empiricalCDF.InverseCDF(probability); + componentCdfProbability = PositiveConditionalQuantileProbability(i, componentProbability); } + componentQuantiles.Add(Distributions[i].InverseCDF(componentCdfProbability)); } - double min = Minimum; - double max = Maximum; - return x < min ? min : x > max ? max : x; + + double lowerBound = componentQuantiles.Min(); + double upperBound = componentQuantiles.Max(); + double value; + try + { + if (lowerBound.AlmostEquals(upperBound)) return Clamp(lowerBound, Minimum, Maximum); + value = Brent.Solve(y => probability - CDF(y), lowerBound, upperBound, 1E-6, 100, true); + } + catch (Exception) + { + if (!_empiricalCDFCreated) CreateEmpiricalCDF(); + value = _empiricalCDF.InverseCDF(probability); + } + + return Clamp(value, Minimum, Maximum); } - + /// /// Create empirical distribution for the CDF. /// public void CreateEmpiricalCDF() { // Get min & max - double minP = 1E-16; double maxP = 1 - 1E-16; - double minX = Distributions.Min(d => d.InverseCDF(minP)); - double maxX = Distributions.Max(d => d.InverseCDF(maxP)); + double minX = Minimum; + double maxX = IsZeroInflated + ? Distributions.Select((distribution, index) => + { + TryGetPositiveMass(index, out double positiveMass); + double probability = distribution.CDF(0.0) + maxP * positiveMass; + return distribution.InverseCDF(Math.Min(probability, 1.0 - 1E-15)); + }).Max() + : Distributions.Max(d => d.InverseCDF(maxP)); // Get number of bins double shift = 0; if (minX <= 0) shift = Math.Abs(minX) + 1d; @@ -1093,35 +1230,37 @@ public void CreateEmpiricalCDF() /// public override double[] GenerateRandomValues(int sampleSize, int seed = -1) { - // Create PRNG for generating random numbers - var rnd = seed > 0 ? new MersenneTwister(seed) : new MersenneTwister(); - var weights = new List(); - var distributions = new List(); - if (IsZeroInflated) - { - weights.Add(ZeroWeight); - distributions.Add(new Deterministic(0.0)); - } - weights.AddRange(Weights); - distributions.AddRange(Distributions); + if (!_parametersValid) ValidateParameters(GetParameters, true); + var random = seed > 0 ? new MersenneTwister(seed) : new MersenneTwister(); var sample = new double[sampleSize]; - // Generate values - for (int i = 0; i < sampleSize; i++) + for (int sampleIndex = 0; sampleIndex < sampleSize; sampleIndex++) { - var u = rnd.NextDouble(); - var cdfW = new double[distributions.Count()]; - for (int j = 0; j < distributions.Count(); j++) + double mixtureProbability = random.NextDouble(); + double componentProbability = random.NextDouble(); + if (IsZeroInflated && mixtureProbability <= ZeroWeight) { - cdfW[j] = j == 0 ? weights[j] : cdfW[j - 1] + weights[j]; - if (u <= cdfW[j]) + sample[sampleIndex] = 0.0; + continue; + } + + double cumulativeWeight = IsZeroInflated ? ZeroWeight : 0.0; + for (int componentIndex = 0; componentIndex < Distributions.Count(); componentIndex++) + { + cumulativeWeight += Weights[componentIndex]; + if (mixtureProbability <= cumulativeWeight || componentIndex == Distributions.Count() - 1) { - sample[i] = distributions[j].InverseCDF(rnd.NextDouble()); + double probability = componentProbability; + if (IsZeroInflated) + { + probability = PositiveConditionalQuantileProbability(componentIndex, componentProbability); + } + sample[sampleIndex] = Distributions[componentIndex].InverseCDF(probability); break; } } } - // Return array of random values + return sample; } diff --git a/Test_Numerics/Distributions/Univariate/Test_Mixture_Phase4.cs b/Test_Numerics/Distributions/Univariate/Test_Mixture_Phase4.cs new file mode 100644 index 00000000..798d09e9 --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Test_Mixture_Phase4.cs @@ -0,0 +1,246 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Distributions; + +namespace Distributions.Univariate +{ + /// + /// Regression tests for the corrected physical-simplex and positive-hurdle mixture behavior. + /// + [TestClass] + public class Test_Mixture_Phase4 + { + /// + /// Verifies that every public setter family copies caller-owned arrays and lists. + /// + [TestMethod] + public void Test_Mixture_Setters_DoNotMutateOrAliasCallerArrays() + { + var constructorWeights = new[] { 0.4, 0.6 }; + var constructorDistributions = new UnivariateDistributionBase[] + { + new Normal(0.0, 1.0), + new Normal(3.0, 1.0) + }; + var mixture = new Mixture(constructorWeights, constructorDistributions); + constructorWeights[0] = 0.9; + constructorDistributions[0] = new Normal(10.0, 1.0); + Assert.AreEqual(0.4, mixture.Weights[0], 0.0); + Assert.AreEqual(0.0, mixture.Distributions[0].Mean, 0.0); + + var baseWeights = new[] { 0.25, 0.75 }; + var baseDistributions = new UnivariateDistributionBase[] + { + new Normal(1.0, 1.0), + new Normal(4.0, 1.0) + }; + mixture.SetParameters(baseWeights, baseDistributions); + baseWeights[0] = 0.8; + baseDistributions[0] = new Normal(20.0, 1.0); + Assert.AreEqual(0.25, mixture.Weights[0], 0.0); + Assert.AreEqual(1.0, mixture.Distributions[0].Mean, 0.0); + + var interfaceWeights = new[] { 0.3, 0.7 }; + var interfaceDistributions = new IUnivariateDistribution[] + { + new Normal(2.0, 1.0), + new Normal(5.0, 1.0) + }; + mixture.SetParameters(interfaceWeights, interfaceDistributions); + interfaceWeights[0] = 0.6; + interfaceDistributions[0] = new Normal(30.0, 1.0); + Assert.AreEqual(0.3, mixture.Weights[0], 0.0); + Assert.AreEqual(2.0, mixture.Distributions[0].Mean, 0.0); + + var parameterWeights = new[] { 0.2, 0.8 }; + var distributionParameters = new[] { 6.0, 1.0, 9.0, 2.0 }; + mixture.SetParameters(parameterWeights, distributionParameters); + parameterWeights[0] = 0.5; + distributionParameters[0] = 60.0; + Assert.AreEqual(0.2, mixture.Weights[0], 0.0); + Assert.AreEqual(6.0, mixture.Distributions[0].GetParameters[0], 0.0); + + var listParameters = new List { 0.35, 0.65, 7.0, 1.0, 10.0, 2.0 }; + double[] listSnapshot = listParameters.ToArray(); + mixture.SetParameters(listParameters); + CollectionAssert.AreEqual(listSnapshot, listParameters); + listParameters[0] = 0.9; + Assert.AreEqual(0.35, mixture.Weights[0], 0.0); + + var referencedParameters = new[] { 2.0, 6.0, 8.0, 1.0, 11.0, 2.0 }; + double[] referencedSnapshot = referencedParameters.ToArray(); + mixture.SetParameters(ref referencedParameters); + CollectionAssert.AreEqual(referencedSnapshot, referencedParameters); + Assert.AreEqual(0.25, mixture.Weights[0], 1E-15); + Assert.AreEqual(0.75, mixture.Weights[1], 1E-15); + } + + /// + /// Verifies ordinary and zero-inflated physical-simplex validation. + /// + [TestMethod] + public void Test_Mixture_ValidatesConfiguredPhysicalSimplex() + { + var ordinary = new Mixture( + new[] { 0.4, 0.4 }, + new UnivariateDistributionBase[] { new Normal(0.0, 1.0), new Normal(3.0, 1.0) }); + Assert.IsFalse(ordinary.ParametersValid); + Assert.Throws(() => ordinary.PDF(1.0)); + + var zeroInflated = new Mixture( + new[] { 0.5, 0.5 }, + new UnivariateDistributionBase[] { new Normal(0.0, 1.0), new Normal(3.0, 1.0) }) + { + IsZeroInflated = true, + ZeroWeight = 0.1 + }; + Assert.IsTrue(zeroInflated.ParametersValid); + zeroInflated.SetParameters( + new[] { 0.4, 0.4 }, + new UnivariateDistributionBase[] { new Normal(0.0, 1.0), new Normal(3.0, 1.0) }); + Assert.IsFalse(zeroInflated.ParametersValid); + Assert.Throws(() => zeroInflated.CDF(1.0)); + + zeroInflated.ZeroWeight = 1.0; + Assert.IsFalse(zeroInflated.ParametersValid); + } + + /// + /// Verifies the analytical positive-hurdle Normal density, distribution, log, and quantile identities. + /// + [TestMethod] + public void Test_Mixture_ZeroInflatedNormal_UsesPositiveHurdleIdentities() + { + var normal = new Normal(0.0, 1.0); + var mixture = new Mixture(new[] { 1.0 }, new UnivariateDistributionBase[] { normal }) + { + IsZeroInflated = true, + ZeroWeight = 0.2 + }; + + const double x = 1.3; + double positiveMass = normal.CCDF(0.0); + double expectedPdf = 0.8 * normal.PDF(x) / positiveMass; + double expectedCdf = 0.2 + 0.8 * (normal.CDF(x) - normal.CDF(0.0)) / positiveMass; + double expectedCcdf = 0.8 * normal.CCDF(x) / positiveMass; + + Assert.AreEqual(expectedPdf, mixture.PDF(x), 1E-14); + Assert.AreEqual(Math.Log(expectedPdf), mixture.LogPDF(x), 1E-14); + Assert.AreEqual(expectedCdf, mixture.CDF(x), 1E-14); + Assert.AreEqual(Math.Log(expectedCdf), mixture.LogCDF(x), 1E-14); + Assert.AreEqual(Math.Log(expectedCcdf), mixture.LogCCDF(x), 1E-14); + + const double probability = 0.6; + double conditionalProbability = (probability - 0.2) / 0.8; + double expectedQuantile = normal.InverseCDF(normal.CDF(0.0) + conditionalProbability * positiveMass); + Assert.AreEqual(expectedQuantile, mixture.InverseCDF(probability), 1E-6); + } + + /// + /// Verifies the atom at zero and absence of negative support under the hurdle model. + /// + [TestMethod] + public void Test_Mixture_ZeroInflatedModel_HasZeroJumpAndNoNegativeSupport() + { + var mixture = new Mixture( + new[] { 1.0 }, + new UnivariateDistributionBase[] { new Normal(0.0, 1.0) }) + { + IsZeroInflated = true, + ZeroWeight = 0.2 + }; + + Assert.AreEqual(0.0, mixture.PDF(-1.0), 0.0); + Assert.AreEqual(double.NegativeInfinity, mixture.LogPDF(-1.0)); + Assert.AreEqual(0.0, mixture.CDF(-1.0), 0.0); + Assert.AreEqual(0.2, mixture.PDF(0.0), 0.0); + Assert.AreEqual(Math.Log(0.2), mixture.LogPDF(0.0), 0.0); + Assert.AreEqual(0.2, mixture.CDF(0.0), 0.0); + Assert.AreEqual(Math.Log(0.8), mixture.LogCCDF(0.0), 1E-15); + Assert.AreEqual(0.0, mixture.InverseCDF(0.2), 0.0); + } + + /// + /// Verifies simulation uses the atom and strictly positive conditioned components. + /// + [TestMethod] + public void Test_Mixture_ZeroInflatedSimulation_UsesAtomAndPositiveConditioning() + { + var mixture = new Mixture( + new[] { 1.0 }, + new UnivariateDistributionBase[] { new Normal(0.0, 1.0) }) + { + IsZeroInflated = true, + ZeroWeight = 0.2 + }; + + double[] sample = mixture.GenerateRandomValues(20000, 12345); + double atomFrequency = sample.Count(value => value == 0.0) / (double)sample.Length; + + Assert.AreEqual(0.2, atomFrequency, 0.015); + Assert.IsFalse(sample.Any(value => value < 0.0)); + Assert.IsTrue(sample.Any(value => value > 0.0)); + } + + /// + /// Verifies a hurdle component must have finite, nonzero probability above zero. + /// + [TestMethod] + public void Test_Mixture_ZeroInflatedModel_RejectsComponentWithoutPositiveMass() + { + var mixture = new Mixture( + new[] { 1.0 }, + new UnivariateDistributionBase[] { new Deterministic(0.0) }); + mixture.ZeroWeight = 0.2; + mixture.IsZeroInflated = true; + + Assert.IsFalse(mixture.ParametersValid); + ArgumentOutOfRangeException exception = + Assert.Throws(() => mixture.PDF(1.0)); + StringAssert.Contains(exception.Message, "positive probability above zero"); + } + + /// + /// Verifies zero-inflated EM rejects negative exact observations with row context. + /// + [TestMethod] + public void Test_Mixture_ZeroInflatedEM_RejectsNegativeExactObservationWithRowContext() + { + var mixture = new Mixture( + new[] { 1.0 }, + new UnivariateDistributionBase[] { new Normal(1.0, 1.0) }) + { + IsZeroInflated = true, + ZeroWeight = 0.1 + }; + + InvalidOperationException exception = + Assert.Throws(() => mixture.MLE(new[] { 1.0, -2.5, 2.0 })); + StringAssert.Contains(exception.Message, "row 1"); + StringAssert.Contains(exception.Message, "-2.5"); + } + + /// + /// Verifies EM reports an impossible row instead of silently continuing. + /// + [TestMethod] + public void Test_Mixture_EM_ImpossibleRowThrowsWithRowContext() + { + var mixture = new Mixture( + new[] { 1.0 }, + new UnivariateDistributionBase[] { new Normal(1.0, 1.0) }) + { + IsZeroInflated = true, + ZeroWeight = 0.0 + }; + + InvalidOperationException exception = + Assert.Throws(() => mixture.MLE(new[] { 0.0, 1.0, 2.0 })); + StringAssert.Contains(exception.Message, "row 0"); + StringAssert.Contains(exception.Message, "value 0"); + StringAssert.Contains(exception.Message, "zero or nonfinite"); + } + } +} From eea1777f85a39b545d2f826c35b42a1b579e5634 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 3 Aug 2026 11:23:39 -0600 Subject: [PATCH 037/222] Restore the finite tail-quantile lower bound in Mixture.CreateEmpiricalCDF Seeding the stratification grid at the distribution minimum collapses the empirical table to a single ordinate for any unbounded-below component. Open the grid at the smallest 1E-16 tail quantile of the components instead, and pin the finite-quantile contract. --- Numerics/Distributions/Univariate/Mixture.cs | 3 ++- .../Distributions/Univariate/Test_Mixture.cs | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/Numerics/Distributions/Univariate/Mixture.cs b/Numerics/Distributions/Univariate/Mixture.cs index ee49871b..f3c240b8 100644 --- a/Numerics/Distributions/Univariate/Mixture.cs +++ b/Numerics/Distributions/Univariate/Mixture.cs @@ -1180,8 +1180,9 @@ public override double InverseCDF(double probability) public void CreateEmpiricalCDF() { // Get min & max + double minP = 1E-16; double maxP = 1 - 1E-16; - double minX = Minimum; + double minX = Distributions.Min(d => d.InverseCDF(minP)); double maxX = IsZeroInflated ? Distributions.Select((distribution, index) => { diff --git a/Test_Numerics/Distributions/Univariate/Test_Mixture.cs b/Test_Numerics/Distributions/Univariate/Test_Mixture.cs index 8f0208e7..0431f60b 100644 --- a/Test_Numerics/Distributions/Univariate/Test_Mixture.cs +++ b/Test_Numerics/Distributions/Univariate/Test_Mixture.cs @@ -310,5 +310,25 @@ public void Test_Mixture_LogPDF_InvalidSupport_ReturnsNegativeInfinity() Assert.AreEqual(double.NegativeInfinity, mix.LogPDF(-1.0)); } + /// + /// Test that the empirical CDF spans unbounded-below components with finite tail quantiles. + /// The stratification grid must open at a finite lower tail quantile of the components + /// rather than the distribution minimum of negative infinity, so quantile lookups on the + /// interpolated table return finite values. + /// + [TestMethod] + public void Test_Mixture_CreateEmpiricalCDF_FiniteQuantilesForUnboundedComponents() + { + var mix = new Mixture(new[] { 0.45, 0.55 }, new UnivariateDistributionBase[] { new Normal(100, 20), new Normal(60, 15) }); + mix.CreateEmpiricalCDF(); + + double median = mix.InverseCDF(0.5); + Assert.IsTrue(Tools.IsFinite(median)); + // The exact median lies strictly inside (60, 100): CDF(60) ≈ 0.285 and CDF(100) ≈ 0.773. + Assert.IsTrue(median > 60.0 && median < 100.0); + Assert.IsTrue(Tools.IsFinite(mix.InverseCDF(0.001))); + Assert.IsTrue(Tools.IsFinite(mix.InverseCDF(0.999))); + } + } } From cafe6cf3837988341912a5aa8bfda444ea55ff77 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 3 Aug 2026 11:29:26 -0600 Subject: [PATCH 038/222] Honor competing-risk dependencies in simulation --- .../Univariate/CompetingRisks.cs | 68 ++++++++++----- .../Univariate/Test_CompetingRisks.cs | 84 +++++++++++++++++++ 2 files changed, 133 insertions(+), 19 deletions(-) diff --git a/Numerics/Distributions/Univariate/CompetingRisks.cs b/Numerics/Distributions/Univariate/CompetingRisks.cs index c76de5c4..ad2f8702 100644 --- a/Numerics/Distributions/Univariate/CompetingRisks.cs +++ b/Numerics/Distributions/Univariate/CompetingRisks.cs @@ -1,6 +1,7 @@ using Numerics.Data; using Numerics.Data.Statistics; using Numerics.Mathematics; +using Numerics.Mathematics.LinearAlgebra; using Numerics.Mathematics.Optimization; using Numerics.Mathematics.RootFinding; using Numerics.Sampling; @@ -1064,6 +1065,52 @@ public List CumulativeIncidenceFunctions(List + /// Validates the user-supplied correlation matrix used by the Gaussian copula. + /// + /// + /// Thrown when the matrix is missing, has the wrong dimensions, contains invalid + /// entries, is not symmetric with unit diagonal, or is not positive definite. + /// + private void ValidateCorrelationMatrix() + { + int dimension = Distributions.Count; + if (CorrelationMatrix == null) + throw new ArgumentException("A correlation-matrix dependency requires a correlation matrix.", nameof(CorrelationMatrix)); + if (CorrelationMatrix.GetLength(0) != dimension || CorrelationMatrix.GetLength(1) != dimension) + throw new ArgumentException("The correlation matrix dimensions must match the number of distributions.", nameof(CorrelationMatrix)); + + for (int i = 0; i < dimension; i++) + { + if (Math.Abs(CorrelationMatrix[i, i] - 1d) > 1E-12) + throw new ArgumentException("The correlation matrix must have unit diagonal entries.", nameof(CorrelationMatrix)); + + for (int j = 0; j < dimension; j++) + { + double value = CorrelationMatrix[i, j]; + if (!Tools.IsFinite(value) || value < -1d || value > 1d) + throw new ArgumentException("The correlation matrix must contain finite values between -1 and 1.", nameof(CorrelationMatrix)); + if (j > i && Math.Abs(value - CorrelationMatrix[j, i]) > 1E-12) + throw new ArgumentException("The correlation matrix must be symmetric.", nameof(CorrelationMatrix)); + } + } + + try + { + var cholesky = new CholeskyDecomposition(new Matrix(CorrelationMatrix)); + if (!cholesky.IsPositiveDefinite) + throw new ArgumentException("The correlation matrix must be positive definite.", nameof(CorrelationMatrix)); + } + catch (ArgumentException) + { + throw; + } + catch (Exception exception) + { + throw new ArgumentException("The correlation matrix must be positive definite.", nameof(CorrelationMatrix), exception); + } + } + /// /// Create a Multivariate Normal distribution used for modeling dependency between the marginal distributions. /// @@ -1084,6 +1131,7 @@ private void CreateMultivariateNormal() } else { + ValidateCorrelationMatrix(); for (int i = 0; i < D; i++) { mu[i] = 0d; @@ -1145,29 +1193,11 @@ public void CreateEmpiricalCDF() /// public override double[] GenerateRandomValues(int sampleSize, int seed = -1) { - // Create PRNG for generating random numbers - var rnd = seed > 0 ? new MersenneTwister(seed) : new MersenneTwister(); - var sample = new double[sampleSize]; - // Generate values - for (int i = 0; i < sampleSize; i++) - { - double xMin = double.MaxValue; - double xMax = double.MinValue; - for (int j = 0; j < Distributions.Count; j++) - { - var x = Distributions[j].InverseCDF(rnd.NextDouble()); - if (x < xMin) xMin = x; - if (x > xMax) xMax = x; - } - sample[i] = MinimumOfRandomVariables == true ? xMin : xMax; - } - // Return array of random values - return sample; + return GenerateRandomValuesWithDependency(sampleSize, seed); } /// /// Generates random values accounting for dependency structure. - /// The original implementation only handles independent case correctly. /// /// Size of random sample to generate. /// Optional. The prng seed. If negative or zero, then the computer clock is used as a seed. diff --git a/Test_Numerics/Distributions/Univariate/Test_CompetingRisks.cs b/Test_Numerics/Distributions/Univariate/Test_CompetingRisks.cs index 37afe4e5..d860ae04 100644 --- a/Test_Numerics/Distributions/Univariate/Test_CompetingRisks.cs +++ b/Test_Numerics/Distributions/Univariate/Test_CompetingRisks.cs @@ -489,6 +489,90 @@ public void Test_DependencyChangeInvalidatesMvnWithoutMutatingCorrelation() Assert.IsGreaterThan(0.1d, perfectlyNegative - correlated); } + /// + /// Verifies that the independent simulation path preserves its established seeded sequence. + /// + [TestMethod] + public void Test_GenerateRandomValues_IndependentPreservesSeededSequence() + { + var risks = new CompetingRisks(new IUnivariateDistribution[] + { + new Normal(10d, 2d), + new Normal(20d, 3d) + }) + { + Dependency = Probability.DependencyType.Independent, + MinimumOfRandomVariables = false + }; + double[] expected = + { + 23.68205396527114d, + 16.630838190679725d, + 14.73954851356168d, + 22.820525389355137d, + 20.241482757909623d, + 25.128148621977083d, + 19.713254203467052d, + 24.292021772366837d, + 19.035542927284475d, + 16.953998432897098d + }; + + CollectionAssert.AreEqual(expected, risks.GenerateRandomValues(expected.Length, 12345)); + } + + /// + /// Verifies that the public override honors every dependency mode by matching the + /// explicit dependency-aware simulation entry point. + /// + /// The dependency mode to exercise. + [TestMethod] + [DataRow(Probability.DependencyType.Independent)] + [DataRow(Probability.DependencyType.PerfectlyPositive)] + [DataRow(Probability.DependencyType.PerfectlyNegative)] + [DataRow(Probability.DependencyType.CorrelationMatrix)] + public void Test_GenerateRandomValues_MatchesDependencyAwarePath(Probability.DependencyType dependency) + { + var risks = new CompetingRisks(new IUnivariateDistribution[] + { + new Normal(10d, 2d), + new Normal(20d, 3d) + }) + { + CorrelationMatrix = new[,] { { 1d, 0.6d }, { 0.6d, 1d } }, + Dependency = dependency, + MinimumOfRandomVariables = false + }; + + double[] expected = risks.GenerateRandomValuesWithDependency(128, 24680); + double[] actual = risks.GenerateRandomValues(128, 24680); + + CollectionAssert.AreEqual(expected, actual); + } + + /// + /// Verifies that correlation-matrix simulation rejects a matrix that is not + /// positive definite before attempting Gaussian-copula sampling. + /// + [TestMethod] + public void Test_GenerateRandomValues_CorrelationMatrixRejectsNonPositiveDefiniteMatrix() + { + var risks = new CompetingRisks(new IUnivariateDistribution[] + { + new Normal(), + new Normal() + }) + { + CorrelationMatrix = new[,] { { 1d, 1d }, { 1d, 1d } }, + Dependency = Probability.DependencyType.CorrelationMatrix + }; + + ArgumentException exception = Assert.ThrowsExactly( + () => risks.GenerateRandomValues(10, 12345)); + + StringAssert.Contains(exception.Message, "positive definite"); + } + // Tolerances - competing risks MLE is harder than single distribution MLE private const double SHAPE_TOLERANCE_PERCENT = 0.25; // 25% relative error private const double SCALE_TOLERANCE_PERCENT = 0.30; // 30% relative error From 0d617c21f1a831bfa609a0231d228a1cbe00c2ce Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 3 Aug 2026 11:35:17 -0600 Subject: [PATCH 039/222] Move misplaced regression tests into their subject test classes The root-level grab-bag file and the second mixture file held tests for many unrelated subjects. Each test now lives in its subject's test class under the folder-mirroring namespace, named and documented for the behavior it pins. No test logic changed. --- .../Test_ProbabilityLazyExclusive.cs | 23 + .../Test_ProbabilityPooledExclusive.cs | 21 + .../Data/Statistics/Test_Statistics.cs | 42 ++ .../Multivariate/Test_MultivariateNormal.cs | 11 +- .../Univariate/Test_BootstrapAnalysis.cs | 74 ++- .../Univariate/Test_CompetingRisks.cs | 38 ++ .../Test_DistributionXElementRoundTrips.cs | 34 +- .../Univariate/Test_EmpiricalConvolution.cs | 41 ++ .../Distributions/Univariate/Test_Mixture.cs | 233 ++++++++ .../Univariate/Test_Mixture_Phase4.cs | 246 -------- .../Functions/Test_CompositeFunction.cs | 173 +++++- .../Functions/Test_EnsembleFunction.cs | 35 +- .../Functions/Test_SegmentedPowerFunction.cs | 32 ++ .../Test_AdaptiveGaussKronrodRecorder.cs | 26 + .../Test_VegasTailFocusJacobian.cs | 17 + .../Test_SpecialFunctions.cs | 14 + Test_Numerics/Test_CorrectnessRepairs.cs | 532 ------------------ 17 files changed, 809 insertions(+), 783 deletions(-) delete mode 100644 Test_Numerics/Distributions/Univariate/Test_Mixture_Phase4.cs delete mode 100644 Test_Numerics/Test_CorrectnessRepairs.cs diff --git a/Test_Numerics/Data/Statistics/Test_ProbabilityLazyExclusive.cs b/Test_Numerics/Data/Statistics/Test_ProbabilityLazyExclusive.cs index 046ec90f..8a759d10 100644 --- a/Test_Numerics/Data/Statistics/Test_ProbabilityLazyExclusive.cs +++ b/Test_Numerics/Data/Statistics/Test_ProbabilityLazyExclusive.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Data.Statistics; using Numerics.Mathematics.SpecialFunctions; @@ -423,5 +424,27 @@ static void AssertProbability(double value, string label) new[] { double.NaN, 0.5d }, new[] { 1, 0 }); Assert.IsTrue(double.IsNaN(nan), "Tools.Clamp must preserve the established NaN signal."); } + + /// + /// Test that a capped enumeration without the no-event row still closes on the exact + /// union mass: the emitted rows plus the closing pseudo-row sum to one minus the + /// no-event probability. + /// + [TestMethod] + public void Test_LazyExclusive_CappedWithoutNoEventRow_PreservesUnionMass() + { + double[] probabilities = { 0.4d, 0.35d, 0.3d, 0.25d, 0.2d, 0.15d }; + var output = new List(); + var indicators = new List(); + + var status = Probability.IndependentExclusiveLazy(probabilities, output, indicators, + includeNoEventRow: false, maxEmittedCombinations: 10, + absoluteTolerance: 0d, relativeTolerance: 0d); + + Assert.AreEqual(Probability.ExclusiveEnumerationStatus.Capped, status); + double noEventMass = probabilities.Aggregate(1d, (mass, probability) => mass * (1d - probability)); + Assert.AreEqual(1d - noEventMass, output.Sum(), 1E-12); + Assert.HasCount(11, output); + } } } diff --git a/Test_Numerics/Data/Statistics/Test_ProbabilityPooledExclusive.cs b/Test_Numerics/Data/Statistics/Test_ProbabilityPooledExclusive.cs index 8268c502..bc8fb3d1 100644 --- a/Test_Numerics/Data/Statistics/Test_ProbabilityPooledExclusive.cs +++ b/Test_Numerics/Data/Statistics/Test_ProbabilityPooledExclusive.cs @@ -114,6 +114,27 @@ public void Test_Pooled_TruncationIsReported() } } + /// + /// Test that structurally inconsistent combination metadata and non-finite tolerances + /// are rejected: the per-size counts must sum to the indicator row count, match the + /// indicator layout, and the tolerance must be a finite number. + /// + [TestMethod] + public void Test_Pooled_RejectsMalformedMetadata() + { + double[] probabilities = { 0.2d, 0.3d }; + var output = new List(); + var indicators = new List(); + int[,] rows = { { 1, 0 }, { 0, 1 }, { 1, 1 } }; + + Assert.Throws(() => Probability.IndependentExclusive( + probabilities, new[] { 2 }, rows, output, indicators)); + Assert.Throws(() => Probability.IndependentExclusive( + probabilities, new[] { 1, 2 }, rows, output, indicators)); + Assert.Throws(() => Probability.IndependentExclusive( + probabilities, new[] { 2, 1 }, rows, output, indicators, double.NaN)); + } + /// /// Builds the full combination enumeration (sizes 1..n in combination order) for n /// events. diff --git a/Test_Numerics/Data/Statistics/Test_Statistics.cs b/Test_Numerics/Data/Statistics/Test_Statistics.cs index f26649e5..473fba75 100644 --- a/Test_Numerics/Data/Statistics/Test_Statistics.cs +++ b/Test_Numerics/Data/Statistics/Test_Statistics.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Threading; using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Distributions; @@ -492,5 +494,45 @@ public void Test_HarmonicMean_ZeroGuard() var data2 = new double[] { 1, 2, 0, 4 }; Assert.AreEqual(double.NaN, Numerics.Data.Statistics.Statistics.HarmonicMean(data2)); } + + /// + /// Test the jackknife edge contracts: a single-element sample returns a zero standard + /// error without evaluating an empty sample, and each resampling callback receives its + /// own isolated sample copy so the source data is never mutated. + /// + [TestMethod] + public void Test_JackKnife_SingleElementAndCallbackIsolation() + { + int calls = 0; + double standardError = Numerics.Data.Statistics.Statistics.JackKnifeStandardError(new[] { 5d }, sample => + { + Interlocked.Increment(ref calls); + return sample.Count; + }); + Assert.AreEqual(0d, standardError, 0d); + Assert.AreEqual(0, calls, "The single-element standard error does not evaluate an empty sample."); + + double[] single = Numerics.Data.Statistics.Statistics.JackKnifeSample(new[] { 5d }, sample => + { + Interlocked.Increment(ref calls); + return sample.Count; + }); + Assert.IsNotNull(single); + Assert.AreEqual(0d, single[0], 0d); + Assert.AreEqual(1, calls); + + double[] original = { 1d, 2d, 3d, 4d }; + var callbackSamples = new List>(); + object sync = new object(); + Numerics.Data.Statistics.Statistics.JackKnifeSample(original, sample => + { + lock (sync) callbackSamples.Add(sample); + if (sample.Count > 0) sample[0] = -100d; + return sample.Count; + }); + CollectionAssert.AreEqual(new[] { 1d, 2d, 3d, 4d }, original); + Assert.HasCount(original.Length, callbackSamples); + Assert.AreEqual(original.Length, callbackSamples.Distinct().Count()); + } } } diff --git a/Test_Numerics/Distributions/Multivariate/Test_MultivariateNormal.cs b/Test_Numerics/Distributions/Multivariate/Test_MultivariateNormal.cs index 050042e0..4057c045 100644 --- a/Test_Numerics/Distributions/Multivariate/Test_MultivariateNormal.cs +++ b/Test_Numerics/Distributions/Multivariate/Test_MultivariateNormal.cs @@ -614,5 +614,14 @@ private static double EvaluateStandardCdf(double[] upper, double[] correlations) return value; } -} + /// + /// Test that the lattice-rule uniform generator cannot be assigned null. + /// + [TestMethod] + public void Test_MVNUNI_RejectsNull() + { + var multivariate = new MultivariateNormal(2); + Assert.Throws(() => multivariate.MVNUNI = null!); + } } +} diff --git a/Test_Numerics/Distributions/Univariate/Test_BootstrapAnalysis.cs b/Test_Numerics/Distributions/Univariate/Test_BootstrapAnalysis.cs index 52828e38..b6a44fa6 100644 --- a/Test_Numerics/Distributions/Univariate/Test_BootstrapAnalysis.cs +++ b/Test_Numerics/Distributions/Univariate/Test_BootstrapAnalysis.cs @@ -2,13 +2,13 @@ using Numerics; using Numerics.Data.Statistics; using Numerics.Distributions; +using Numerics.Mathematics.Optimization; using Numerics.Sampling; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; -using static System.Reflection.Metadata.BlobBuilder; namespace Distributions.Univariate { @@ -329,5 +329,77 @@ public void Test_ProcessParameterSets_FillsFailuresWithNaN() Assert.IsTrue(double.IsNaN(results.ParameterSets[7].Values[0])); } + /// + /// Test that summary probabilities average only the successful fits, and that a + /// replication set with no successful fit is rejected loudly — as a single error for + /// the all-null expected-probability path and as an aggregate of the per-set failures + /// for the distribution builder. + /// + [TestMethod] + public void Test_UsesOnlySuccessfulFits_AndRejectsAllFailures() + { + var parent = new Normal(0d, 1d); + var analysis = new BootstrapAnalysis(parent, ParameterEstimationMethod.MethodOfMoments, 10, 1234); + IUnivariateDistribution[] mixed = { new Normal(0d, 1d), null!, new Normal(1d, 1d) }; + + double[] mean = analysis.ExpectedProbabilities(new[] { 0d }, mixed); + double expected = 0.5d * (new Normal(0d, 1d).CDF(0d) + new Normal(1d, 1d).CDF(0d)); + Assert.AreEqual(expected, mean[0], 1E-14); + Assert.Throws(() => + analysis.ExpectedProbabilities(new[] { 0d }, new IUnivariateDistribution[] { null!, null! })); + + var aggregate = Assert.Throws(() => analysis.Distributions(new[] + { + new ParameterSet(new[] { 0d, -1d }, 0d), + new ParameterSet(new[] { double.NaN, 1d }, 0d), + })); + Assert.HasCount(2, aggregate.InnerExceptions); + } + + /// + /// Test that the normal-approximation quantile interval preserves the sign of negative + /// quantiles: the transform applied around the point estimate must remain finite and + /// keep both interval endpoints on the data's side of zero. + /// + [TestMethod] + public void Test_NormalQuantileCI_PreservesNegativeQuantiles() + { + var parent = new Normal(-10d, 1d); + var analysis = new BootstrapAnalysis(parent, ParameterEstimationMethod.MethodOfMoments, 10, 1234); + IUnivariateDistribution[] fits = + { + new Normal(-9.5d, 1d), + new Normal(-10d, 1.1d), + null!, + new Normal(-10.5d, 0.9d), + }; + + double[,] interval = analysis.NormalQuantileCI(new[] { 0.5d }, 0.1d, fits); + Assert.IsTrue(Tools.IsFinite(interval[0, 0])); + Assert.IsTrue(Tools.IsFinite(interval[0, 1])); + Assert.IsLessThan(0d, interval[0, 0]); + Assert.IsLessThan(0d, interval[0, 1]); + } + + /// + /// Test that expected probabilities pair each quantile with its own probability after + /// the internal sort: unsorted input ordinates produce exactly the same value array as + /// the pre-sorted equivalent. + /// + [TestMethod] + public void Test_ExpectedProbabilities_InterpolationKeepsPairsSorted() + { + var parent = new Normal(0d, 1d); + var analysis = new BootstrapAnalysis(parent, ParameterEstimationMethod.MethodOfMoments, 10, 1234); + IUnivariateDistribution[] fits = { new Normal(0d, 1d), new Normal(1d, 2d) }; + double[] sorted = { -3d, -1d, 0d, 2d, 5d }; + double[] unsorted = { 2d, -3d, 5d, 0d, -1d }; + double[] probabilities = { 0.1d, 0.5d, 0.9d }; + + double[] expected = analysis.ExpectedProbabilities(sorted, probabilities, fits); + double[] actual = analysis.ExpectedProbabilities(unsorted, probabilities, fits); + CollectionAssert.AreEqual(expected, actual); + } + } } diff --git a/Test_Numerics/Distributions/Univariate/Test_CompetingRisks.cs b/Test_Numerics/Distributions/Univariate/Test_CompetingRisks.cs index d860ae04..188006ee 100644 --- a/Test_Numerics/Distributions/Univariate/Test_CompetingRisks.cs +++ b/Test_Numerics/Distributions/Univariate/Test_CompetingRisks.cs @@ -5,6 +5,8 @@ using Numerics.Mathematics.Integration; using Numerics.Mathematics.SpecialFunctions; using System; +using System.Reflection; +using System.Xml.Linq; namespace Distributions.Univariate { @@ -1117,6 +1119,42 @@ public void Test_MLE_MaxRule_3Dist_DifferentFamilies() #endregion + #region Seed and Serialization + + /// + /// Test that reseeding invalidates the lazily built multivariate-normal and + /// empirical-CDF caches, that the seed survives cloning and the XML round-trip, and + /// that undefined dependency ordinals are rejected on deserialization. + /// + [TestMethod] + public void Test_PRNGSeed_InvalidatesCachesAndRoundTrips() + { + var distribution = new CompetingRisks(new UnivariateDistributionBase[] + { + new Normal(0d, 1d), + new Exponential(2d), + }) { PRNGSeed = 2468 }; + + FieldInfo mvnCreated = typeof(CompetingRisks).GetField("_mvnCreated", BindingFlags.Instance | BindingFlags.NonPublic)!; + FieldInfo empiricalCreated = typeof(CompetingRisks).GetField("_empiricalCDFCreated", BindingFlags.Instance | BindingFlags.NonPublic)!; + mvnCreated.SetValue(distribution, true); + empiricalCreated.SetValue(distribution, true); + distribution.PRNGSeed = 1357; + Assert.IsFalse((bool)mvnCreated.GetValue(distribution)!); + Assert.IsFalse((bool)empiricalCreated.GetValue(distribution)!); + + var clone = (CompetingRisks)distribution.Clone(); + Assert.AreEqual(1357, clone.PRNGSeed); + var restored = (CompetingRisks)UnivariateDistributionFactory.CreateDistribution(distribution.ToXElement()); + Assert.AreEqual(1357, restored.PRNGSeed); + + XElement malformed = distribution.ToXElement(); + malformed.SetAttributeValue(nameof(CompetingRisks.Dependency), "999"); + Assert.Throws(() => CompetingRisks.FromXElement(malformed)); + } + + #endregion + #region Helper Methods /// diff --git a/Test_Numerics/Distributions/Univariate/Test_DistributionXElementRoundTrips.cs b/Test_Numerics/Distributions/Univariate/Test_DistributionXElementRoundTrips.cs index 6b63b1ea..1b2580f8 100644 --- a/Test_Numerics/Distributions/Univariate/Test_DistributionXElementRoundTrips.cs +++ b/Test_Numerics/Distributions/Univariate/Test_DistributionXElementRoundTrips.cs @@ -1,4 +1,5 @@ using System; +using System.Xml.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Data; using Numerics.Distributions; @@ -100,7 +101,38 @@ public void Test_KernelDensity_RoundTrip() Assert.AreEqual(weighted.PDF(3d), weightedRestored.PDF(3d), 1E-12, "Weighted kernel evaluation must survive the round-trip."); Assert.AreEqual(weighted.Mean, weightedRestored.Mean, 1E-12); - Assert.Throws(() => KernelDensity.FromXElement(new System.Xml.Linq.XElement("Distribution"))); + Assert.Throws(() => KernelDensity.FromXElement(new XElement("Distribution"))); + } + + /// + /// Test that malformed serialized payloads are rejected: undefined enum ordinals on the + /// empirical table, zero-sum kernel weights, non-finite bandwidths, and a missing + /// distribution type discriminator. + /// + [TestMethod] + public void Test_MalformedXElements_AreRejected() + { + var empirical = new EmpiricalDistribution(new[] { 1d, 2d }, new[] { 0d, 1d }); + XElement invalidOrder = empirical.ToXElement(); + invalidOrder.SetAttributeValue("ProbabilityOrder", "999"); + Assert.Throws(() => UnivariateDistributionFactory.CreateDistribution(invalidOrder)); + + XElement invalidTransform = empirical.ToXElement(); + invalidTransform.SetAttributeValue(nameof(EmpiricalDistribution.XTransform), "999"); + Assert.Throws(() => EmpiricalDistribution.FromXElement(invalidTransform)); + + var kernel = new KernelDensity(new[] { 1d, 2d, 3d }, new[] { 1d, 1d, 1d }, KernelDensity.KernelType.Gaussian, 0.5d); + XElement zeroWeights = kernel.ToXElement(); + zeroWeights.SetAttributeValue("Weights", "0|0|0"); + Assert.Throws(() => KernelDensity.FromXElement(zeroWeights)); + + XElement invalidBandwidth = kernel.ToXElement(); + invalidBandwidth.SetAttributeValue(nameof(KernelDensity.Bandwidth), "NaN"); + Assert.Throws(() => KernelDensity.FromXElement(invalidBandwidth)); + + XElement missingType = new Normal().ToXElement(); + missingType.Attribute(nameof(UnivariateDistributionBase.Type))!.Remove(); + Assert.Throws(() => UnivariateDistributionFactory.CreateDistribution(missingType)); } } } diff --git a/Test_Numerics/Distributions/Univariate/Test_EmpiricalConvolution.cs b/Test_Numerics/Distributions/Univariate/Test_EmpiricalConvolution.cs index e8b77395..7039ea41 100644 --- a/Test_Numerics/Distributions/Univariate/Test_EmpiricalConvolution.cs +++ b/Test_Numerics/Distributions/Univariate/Test_EmpiricalConvolution.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Distributions; @@ -103,6 +104,46 @@ public void Test_Convolve_LogSpacedOutput() Assert.Throws(() => EmpiricalDistribution.Convolve(negative, dist2, 1024, logSpacedOutput: true)); } + /// + /// Test that the lattice spans only the occupied support (atoms with zero mass do not + /// stretch it), the masses sum to one, the mean is exact, and zero-total-mass inputs + /// are rejected. + /// + [TestMethod] + public void Test_ConvolveDiscrete_UsesOccupiedSupportAndPositiveMass() + { + EmpiricalDistribution.ConvolveDiscrete( + new[] { -100d, 2d, 5d }, new[] { 0d, 0.25d, 0.75d }, + new[] { 3d, 7d, 100d }, new[] { 0.5d, 0.5d, 0d }, + 256, out double[] values, out double[] masses); + + Assert.AreEqual(5d, values[0], 1E-12); + double step = values[1] - values[0]; + Assert.IsGreaterThanOrEqualTo(12d, values[values.Length - 1]); + Assert.IsLessThanOrEqualTo(step + 1E-12, values[values.Length - 1] - 12d, + "The occupied lattice may extend at most one node beyond the exact support."); + Assert.AreEqual(1d, masses.Sum(), 1E-12); + double mean = values.Zip(masses, (value, mass) => value * mass).Sum(); + Assert.AreEqual(9.25d, mean, 1E-10); + + Assert.Throws(() => EmpiricalDistribution.ConvolveDiscrete( + new[] { 1d, 2d }, new[] { 0d, 0d }, new[] { 1d }, new[] { 1d }, + 256, out _, out _)); + } + + /// + /// Test that a degenerate logarithmic output support is rejected before the transform + /// reaches the FFT. + /// + [TestMethod] + public void Test_Convolve_LogSpaced_RejectsDegenerateSupport() + { + var point1 = new EmpiricalDistribution(new[] { 2d, 2d }, new[] { 0d, 1d }); + var point2 = new EmpiricalDistribution(new[] { 3d, 3d }, new[] { 0d, 1d }); + Assert.Throws(() => + EmpiricalDistribution.Convolve(point1, point2, 128, logSpacedOutput: true)); + } + /// /// Accumulates the lattice mass at and below a probe value. /// diff --git a/Test_Numerics/Distributions/Univariate/Test_Mixture.cs b/Test_Numerics/Distributions/Univariate/Test_Mixture.cs index 0431f60b..fe61d5bd 100644 --- a/Test_Numerics/Distributions/Univariate/Test_Mixture.cs +++ b/Test_Numerics/Distributions/Univariate/Test_Mixture.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics; using Numerics.Distributions; @@ -330,5 +332,236 @@ public void Test_Mixture_CreateEmpiricalCDF_FiniteQuantilesForUnboundedComponent Assert.IsTrue(Tools.IsFinite(mix.InverseCDF(0.999))); } + /// + /// Verifies that every public setter family copies caller-owned arrays and lists. + /// + [TestMethod] + public void Test_Mixture_Setters_DoNotMutateOrAliasCallerArrays() + { + var constructorWeights = new[] { 0.4, 0.6 }; + var constructorDistributions = new UnivariateDistributionBase[] + { + new Normal(0.0, 1.0), + new Normal(3.0, 1.0) + }; + var mixture = new Mixture(constructorWeights, constructorDistributions); + constructorWeights[0] = 0.9; + constructorDistributions[0] = new Normal(10.0, 1.0); + Assert.AreEqual(0.4, mixture.Weights[0], 0.0); + Assert.AreEqual(0.0, mixture.Distributions[0].Mean, 0.0); + + var baseWeights = new[] { 0.25, 0.75 }; + var baseDistributions = new UnivariateDistributionBase[] + { + new Normal(1.0, 1.0), + new Normal(4.0, 1.0) + }; + mixture.SetParameters(baseWeights, baseDistributions); + baseWeights[0] = 0.8; + baseDistributions[0] = new Normal(20.0, 1.0); + Assert.AreEqual(0.25, mixture.Weights[0], 0.0); + Assert.AreEqual(1.0, mixture.Distributions[0].Mean, 0.0); + + var interfaceWeights = new[] { 0.3, 0.7 }; + var interfaceDistributions = new IUnivariateDistribution[] + { + new Normal(2.0, 1.0), + new Normal(5.0, 1.0) + }; + mixture.SetParameters(interfaceWeights, interfaceDistributions); + interfaceWeights[0] = 0.6; + interfaceDistributions[0] = new Normal(30.0, 1.0); + Assert.AreEqual(0.3, mixture.Weights[0], 0.0); + Assert.AreEqual(2.0, mixture.Distributions[0].Mean, 0.0); + + var parameterWeights = new[] { 0.2, 0.8 }; + var distributionParameters = new[] { 6.0, 1.0, 9.0, 2.0 }; + mixture.SetParameters(parameterWeights, distributionParameters); + parameterWeights[0] = 0.5; + distributionParameters[0] = 60.0; + Assert.AreEqual(0.2, mixture.Weights[0], 0.0); + Assert.AreEqual(6.0, mixture.Distributions[0].GetParameters[0], 0.0); + + var listParameters = new List { 0.35, 0.65, 7.0, 1.0, 10.0, 2.0 }; + double[] listSnapshot = listParameters.ToArray(); + mixture.SetParameters(listParameters); + CollectionAssert.AreEqual(listSnapshot, listParameters); + listParameters[0] = 0.9; + Assert.AreEqual(0.35, mixture.Weights[0], 0.0); + + var referencedParameters = new[] { 2.0, 6.0, 8.0, 1.0, 11.0, 2.0 }; + double[] referencedSnapshot = referencedParameters.ToArray(); + mixture.SetParameters(ref referencedParameters); + CollectionAssert.AreEqual(referencedSnapshot, referencedParameters); + Assert.AreEqual(0.25, mixture.Weights[0], 1E-15); + Assert.AreEqual(0.75, mixture.Weights[1], 1E-15); + } + + /// + /// Verifies ordinary and zero-inflated physical-simplex validation. + /// + [TestMethod] + public void Test_Mixture_ValidatesConfiguredPhysicalSimplex() + { + var ordinary = new Mixture( + new[] { 0.4, 0.4 }, + new UnivariateDistributionBase[] { new Normal(0.0, 1.0), new Normal(3.0, 1.0) }); + Assert.IsFalse(ordinary.ParametersValid); + Assert.Throws(() => ordinary.PDF(1.0)); + + var zeroInflated = new Mixture( + new[] { 0.5, 0.5 }, + new UnivariateDistributionBase[] { new Normal(0.0, 1.0), new Normal(3.0, 1.0) }) + { + IsZeroInflated = true, + ZeroWeight = 0.1 + }; + Assert.IsTrue(zeroInflated.ParametersValid); + zeroInflated.SetParameters( + new[] { 0.4, 0.4 }, + new UnivariateDistributionBase[] { new Normal(0.0, 1.0), new Normal(3.0, 1.0) }); + Assert.IsFalse(zeroInflated.ParametersValid); + Assert.Throws(() => zeroInflated.CDF(1.0)); + + zeroInflated.ZeroWeight = 1.0; + Assert.IsFalse(zeroInflated.ParametersValid); + } + + /// + /// Verifies the analytical positive-hurdle Normal density, distribution, log, and quantile identities. + /// + [TestMethod] + public void Test_Mixture_ZeroInflatedNormal_UsesPositiveHurdleIdentities() + { + var normal = new Normal(0.0, 1.0); + var mixture = new Mixture(new[] { 1.0 }, new UnivariateDistributionBase[] { normal }) + { + IsZeroInflated = true, + ZeroWeight = 0.2 + }; + + const double x = 1.3; + double positiveMass = normal.CCDF(0.0); + double expectedPdf = 0.8 * normal.PDF(x) / positiveMass; + double expectedCdf = 0.2 + 0.8 * (normal.CDF(x) - normal.CDF(0.0)) / positiveMass; + double expectedCcdf = 0.8 * normal.CCDF(x) / positiveMass; + + Assert.AreEqual(expectedPdf, mixture.PDF(x), 1E-14); + Assert.AreEqual(Math.Log(expectedPdf), mixture.LogPDF(x), 1E-14); + Assert.AreEqual(expectedCdf, mixture.CDF(x), 1E-14); + Assert.AreEqual(Math.Log(expectedCdf), mixture.LogCDF(x), 1E-14); + Assert.AreEqual(Math.Log(expectedCcdf), mixture.LogCCDF(x), 1E-14); + + const double probability = 0.6; + double conditionalProbability = (probability - 0.2) / 0.8; + double expectedQuantile = normal.InverseCDF(normal.CDF(0.0) + conditionalProbability * positiveMass); + Assert.AreEqual(expectedQuantile, mixture.InverseCDF(probability), 1E-6); + } + + /// + /// Verifies the atom at zero and absence of negative support under the hurdle model. + /// + [TestMethod] + public void Test_Mixture_ZeroInflatedModel_HasZeroJumpAndNoNegativeSupport() + { + var mixture = new Mixture( + new[] { 1.0 }, + new UnivariateDistributionBase[] { new Normal(0.0, 1.0) }) + { + IsZeroInflated = true, + ZeroWeight = 0.2 + }; + + Assert.AreEqual(0.0, mixture.PDF(-1.0), 0.0); + Assert.AreEqual(double.NegativeInfinity, mixture.LogPDF(-1.0)); + Assert.AreEqual(0.0, mixture.CDF(-1.0), 0.0); + Assert.AreEqual(0.2, mixture.PDF(0.0), 0.0); + Assert.AreEqual(Math.Log(0.2), mixture.LogPDF(0.0), 0.0); + Assert.AreEqual(0.2, mixture.CDF(0.0), 0.0); + Assert.AreEqual(Math.Log(0.8), mixture.LogCCDF(0.0), 1E-15); + Assert.AreEqual(0.0, mixture.InverseCDF(0.2), 0.0); + } + + /// + /// Verifies simulation uses the atom and strictly positive conditioned components. + /// + [TestMethod] + public void Test_Mixture_ZeroInflatedSimulation_UsesAtomAndPositiveConditioning() + { + var mixture = new Mixture( + new[] { 1.0 }, + new UnivariateDistributionBase[] { new Normal(0.0, 1.0) }) + { + IsZeroInflated = true, + ZeroWeight = 0.2 + }; + + double[] sample = mixture.GenerateRandomValues(20000, 12345); + double atomFrequency = sample.Count(value => value == 0.0) / (double)sample.Length; + + Assert.AreEqual(0.2, atomFrequency, 0.015); + Assert.IsFalse(sample.Any(value => value < 0.0)); + Assert.IsTrue(sample.Any(value => value > 0.0)); + } + + /// + /// Verifies a hurdle component must have finite, nonzero probability above zero. + /// + [TestMethod] + public void Test_Mixture_ZeroInflatedModel_RejectsComponentWithoutPositiveMass() + { + var mixture = new Mixture( + new[] { 1.0 }, + new UnivariateDistributionBase[] { new Deterministic(0.0) }); + mixture.ZeroWeight = 0.2; + mixture.IsZeroInflated = true; + + Assert.IsFalse(mixture.ParametersValid); + ArgumentOutOfRangeException exception = + Assert.Throws(() => mixture.PDF(1.0)); + StringAssert.Contains(exception.Message, "positive probability above zero"); + } + + /// + /// Verifies zero-inflated EM rejects negative exact observations with row context. + /// + [TestMethod] + public void Test_Mixture_ZeroInflatedEM_RejectsNegativeExactObservationWithRowContext() + { + var mixture = new Mixture( + new[] { 1.0 }, + new UnivariateDistributionBase[] { new Normal(1.0, 1.0) }) + { + IsZeroInflated = true, + ZeroWeight = 0.1 + }; + + InvalidOperationException exception = + Assert.Throws(() => mixture.MLE(new[] { 1.0, -2.5, 2.0 })); + StringAssert.Contains(exception.Message, "row 1"); + StringAssert.Contains(exception.Message, "-2.5"); + } + + /// + /// Verifies EM reports an impossible row instead of silently continuing. + /// + [TestMethod] + public void Test_Mixture_EM_ImpossibleRowThrowsWithRowContext() + { + var mixture = new Mixture( + new[] { 1.0 }, + new UnivariateDistributionBase[] { new Normal(1.0, 1.0) }) + { + IsZeroInflated = true, + ZeroWeight = 0.0 + }; + + InvalidOperationException exception = + Assert.Throws(() => mixture.MLE(new[] { 0.0, 1.0, 2.0 })); + StringAssert.Contains(exception.Message, "row 0"); + StringAssert.Contains(exception.Message, "value 0"); + StringAssert.Contains(exception.Message, "zero or nonfinite"); + } + } } diff --git a/Test_Numerics/Distributions/Univariate/Test_Mixture_Phase4.cs b/Test_Numerics/Distributions/Univariate/Test_Mixture_Phase4.cs deleted file mode 100644 index 798d09e9..00000000 --- a/Test_Numerics/Distributions/Univariate/Test_Mixture_Phase4.cs +++ /dev/null @@ -1,246 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Numerics.Distributions; - -namespace Distributions.Univariate -{ - /// - /// Regression tests for the corrected physical-simplex and positive-hurdle mixture behavior. - /// - [TestClass] - public class Test_Mixture_Phase4 - { - /// - /// Verifies that every public setter family copies caller-owned arrays and lists. - /// - [TestMethod] - public void Test_Mixture_Setters_DoNotMutateOrAliasCallerArrays() - { - var constructorWeights = new[] { 0.4, 0.6 }; - var constructorDistributions = new UnivariateDistributionBase[] - { - new Normal(0.0, 1.0), - new Normal(3.0, 1.0) - }; - var mixture = new Mixture(constructorWeights, constructorDistributions); - constructorWeights[0] = 0.9; - constructorDistributions[0] = new Normal(10.0, 1.0); - Assert.AreEqual(0.4, mixture.Weights[0], 0.0); - Assert.AreEqual(0.0, mixture.Distributions[0].Mean, 0.0); - - var baseWeights = new[] { 0.25, 0.75 }; - var baseDistributions = new UnivariateDistributionBase[] - { - new Normal(1.0, 1.0), - new Normal(4.0, 1.0) - }; - mixture.SetParameters(baseWeights, baseDistributions); - baseWeights[0] = 0.8; - baseDistributions[0] = new Normal(20.0, 1.0); - Assert.AreEqual(0.25, mixture.Weights[0], 0.0); - Assert.AreEqual(1.0, mixture.Distributions[0].Mean, 0.0); - - var interfaceWeights = new[] { 0.3, 0.7 }; - var interfaceDistributions = new IUnivariateDistribution[] - { - new Normal(2.0, 1.0), - new Normal(5.0, 1.0) - }; - mixture.SetParameters(interfaceWeights, interfaceDistributions); - interfaceWeights[0] = 0.6; - interfaceDistributions[0] = new Normal(30.0, 1.0); - Assert.AreEqual(0.3, mixture.Weights[0], 0.0); - Assert.AreEqual(2.0, mixture.Distributions[0].Mean, 0.0); - - var parameterWeights = new[] { 0.2, 0.8 }; - var distributionParameters = new[] { 6.0, 1.0, 9.0, 2.0 }; - mixture.SetParameters(parameterWeights, distributionParameters); - parameterWeights[0] = 0.5; - distributionParameters[0] = 60.0; - Assert.AreEqual(0.2, mixture.Weights[0], 0.0); - Assert.AreEqual(6.0, mixture.Distributions[0].GetParameters[0], 0.0); - - var listParameters = new List { 0.35, 0.65, 7.0, 1.0, 10.0, 2.0 }; - double[] listSnapshot = listParameters.ToArray(); - mixture.SetParameters(listParameters); - CollectionAssert.AreEqual(listSnapshot, listParameters); - listParameters[0] = 0.9; - Assert.AreEqual(0.35, mixture.Weights[0], 0.0); - - var referencedParameters = new[] { 2.0, 6.0, 8.0, 1.0, 11.0, 2.0 }; - double[] referencedSnapshot = referencedParameters.ToArray(); - mixture.SetParameters(ref referencedParameters); - CollectionAssert.AreEqual(referencedSnapshot, referencedParameters); - Assert.AreEqual(0.25, mixture.Weights[0], 1E-15); - Assert.AreEqual(0.75, mixture.Weights[1], 1E-15); - } - - /// - /// Verifies ordinary and zero-inflated physical-simplex validation. - /// - [TestMethod] - public void Test_Mixture_ValidatesConfiguredPhysicalSimplex() - { - var ordinary = new Mixture( - new[] { 0.4, 0.4 }, - new UnivariateDistributionBase[] { new Normal(0.0, 1.0), new Normal(3.0, 1.0) }); - Assert.IsFalse(ordinary.ParametersValid); - Assert.Throws(() => ordinary.PDF(1.0)); - - var zeroInflated = new Mixture( - new[] { 0.5, 0.5 }, - new UnivariateDistributionBase[] { new Normal(0.0, 1.0), new Normal(3.0, 1.0) }) - { - IsZeroInflated = true, - ZeroWeight = 0.1 - }; - Assert.IsTrue(zeroInflated.ParametersValid); - zeroInflated.SetParameters( - new[] { 0.4, 0.4 }, - new UnivariateDistributionBase[] { new Normal(0.0, 1.0), new Normal(3.0, 1.0) }); - Assert.IsFalse(zeroInflated.ParametersValid); - Assert.Throws(() => zeroInflated.CDF(1.0)); - - zeroInflated.ZeroWeight = 1.0; - Assert.IsFalse(zeroInflated.ParametersValid); - } - - /// - /// Verifies the analytical positive-hurdle Normal density, distribution, log, and quantile identities. - /// - [TestMethod] - public void Test_Mixture_ZeroInflatedNormal_UsesPositiveHurdleIdentities() - { - var normal = new Normal(0.0, 1.0); - var mixture = new Mixture(new[] { 1.0 }, new UnivariateDistributionBase[] { normal }) - { - IsZeroInflated = true, - ZeroWeight = 0.2 - }; - - const double x = 1.3; - double positiveMass = normal.CCDF(0.0); - double expectedPdf = 0.8 * normal.PDF(x) / positiveMass; - double expectedCdf = 0.2 + 0.8 * (normal.CDF(x) - normal.CDF(0.0)) / positiveMass; - double expectedCcdf = 0.8 * normal.CCDF(x) / positiveMass; - - Assert.AreEqual(expectedPdf, mixture.PDF(x), 1E-14); - Assert.AreEqual(Math.Log(expectedPdf), mixture.LogPDF(x), 1E-14); - Assert.AreEqual(expectedCdf, mixture.CDF(x), 1E-14); - Assert.AreEqual(Math.Log(expectedCdf), mixture.LogCDF(x), 1E-14); - Assert.AreEqual(Math.Log(expectedCcdf), mixture.LogCCDF(x), 1E-14); - - const double probability = 0.6; - double conditionalProbability = (probability - 0.2) / 0.8; - double expectedQuantile = normal.InverseCDF(normal.CDF(0.0) + conditionalProbability * positiveMass); - Assert.AreEqual(expectedQuantile, mixture.InverseCDF(probability), 1E-6); - } - - /// - /// Verifies the atom at zero and absence of negative support under the hurdle model. - /// - [TestMethod] - public void Test_Mixture_ZeroInflatedModel_HasZeroJumpAndNoNegativeSupport() - { - var mixture = new Mixture( - new[] { 1.0 }, - new UnivariateDistributionBase[] { new Normal(0.0, 1.0) }) - { - IsZeroInflated = true, - ZeroWeight = 0.2 - }; - - Assert.AreEqual(0.0, mixture.PDF(-1.0), 0.0); - Assert.AreEqual(double.NegativeInfinity, mixture.LogPDF(-1.0)); - Assert.AreEqual(0.0, mixture.CDF(-1.0), 0.0); - Assert.AreEqual(0.2, mixture.PDF(0.0), 0.0); - Assert.AreEqual(Math.Log(0.2), mixture.LogPDF(0.0), 0.0); - Assert.AreEqual(0.2, mixture.CDF(0.0), 0.0); - Assert.AreEqual(Math.Log(0.8), mixture.LogCCDF(0.0), 1E-15); - Assert.AreEqual(0.0, mixture.InverseCDF(0.2), 0.0); - } - - /// - /// Verifies simulation uses the atom and strictly positive conditioned components. - /// - [TestMethod] - public void Test_Mixture_ZeroInflatedSimulation_UsesAtomAndPositiveConditioning() - { - var mixture = new Mixture( - new[] { 1.0 }, - new UnivariateDistributionBase[] { new Normal(0.0, 1.0) }) - { - IsZeroInflated = true, - ZeroWeight = 0.2 - }; - - double[] sample = mixture.GenerateRandomValues(20000, 12345); - double atomFrequency = sample.Count(value => value == 0.0) / (double)sample.Length; - - Assert.AreEqual(0.2, atomFrequency, 0.015); - Assert.IsFalse(sample.Any(value => value < 0.0)); - Assert.IsTrue(sample.Any(value => value > 0.0)); - } - - /// - /// Verifies a hurdle component must have finite, nonzero probability above zero. - /// - [TestMethod] - public void Test_Mixture_ZeroInflatedModel_RejectsComponentWithoutPositiveMass() - { - var mixture = new Mixture( - new[] { 1.0 }, - new UnivariateDistributionBase[] { new Deterministic(0.0) }); - mixture.ZeroWeight = 0.2; - mixture.IsZeroInflated = true; - - Assert.IsFalse(mixture.ParametersValid); - ArgumentOutOfRangeException exception = - Assert.Throws(() => mixture.PDF(1.0)); - StringAssert.Contains(exception.Message, "positive probability above zero"); - } - - /// - /// Verifies zero-inflated EM rejects negative exact observations with row context. - /// - [TestMethod] - public void Test_Mixture_ZeroInflatedEM_RejectsNegativeExactObservationWithRowContext() - { - var mixture = new Mixture( - new[] { 1.0 }, - new UnivariateDistributionBase[] { new Normal(1.0, 1.0) }) - { - IsZeroInflated = true, - ZeroWeight = 0.1 - }; - - InvalidOperationException exception = - Assert.Throws(() => mixture.MLE(new[] { 1.0, -2.5, 2.0 })); - StringAssert.Contains(exception.Message, "row 1"); - StringAssert.Contains(exception.Message, "-2.5"); - } - - /// - /// Verifies EM reports an impossible row instead of silently continuing. - /// - [TestMethod] - public void Test_Mixture_EM_ImpossibleRowThrowsWithRowContext() - { - var mixture = new Mixture( - new[] { 1.0 }, - new UnivariateDistributionBase[] { new Normal(1.0, 1.0) }) - { - IsZeroInflated = true, - ZeroWeight = 0.0 - }; - - InvalidOperationException exception = - Assert.Throws(() => mixture.MLE(new[] { 0.0, 1.0, 2.0 })); - StringAssert.Contains(exception.Message, "row 0"); - StringAssert.Contains(exception.Message, "value 0"); - StringAssert.Contains(exception.Message, "zero or nonfinite"); - } - } -} diff --git a/Test_Numerics/Functions/Test_CompositeFunction.cs b/Test_Numerics/Functions/Test_CompositeFunction.cs index fcbb5b50..cbb0ab35 100644 --- a/Test_Numerics/Functions/Test_CompositeFunction.cs +++ b/Test_Numerics/Functions/Test_CompositeFunction.cs @@ -1,4 +1,8 @@ using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using System.Xml.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Functions; @@ -7,7 +11,8 @@ namespace Functions /// /// Unit tests for : the weighted-average and mixture modes, /// the single-uniform mixture composition, the deterministic semantics, weight validation, - /// the numeric inverse, and the serialization round-trip (including nesting). + /// the numeric inverse, exception-safe child state restoration, stateful-child + /// synchronization, and the serialization round-trip (including nesting). /// /// /// Authors: @@ -173,5 +178,171 @@ public void Test_Serialization_RoundTrip() Assert.AreEqual(UnivariateFunctionType.Composite, UnivariateFunctionFactory.GetFunctionType(original)); Assert.Throws(() => UnivariateFunctionFactory.CreateFunction(UnivariateFunctionType.Composite)); } + + /// + /// Test that evaluation restores a child's configured confidence level when the child + /// throws, on both the forward and inverse paths. + /// + [TestMethod] + public void Test_ChildStateRestored_AfterExceptions() + { + var child = new ConfidenceProbeFunction { ConfidenceLevel = 0.35d, ThrowOnEvaluation = true }; + var composite = new CompositeFunction(new IUnivariateFunction[] { child }) { ConfidenceLevel = 0.8d }; + + Assert.Throws(() => composite.Function(1d)); + Assert.AreEqual(0.35d, child.ConfidenceLevel, 0d); + Assert.Throws(() => composite.InverseFunction(1d)); + Assert.AreEqual(0.35d, child.ConfidenceLevel, 0d); + } + + /// + /// Test that concurrent evaluation over a shared stateful child never interleaves + /// confidence assignments, and that a deterministic child evaluates through the + /// lock-free path without any confidence mutation. + /// + [TestMethod] + public void Test_SynchronizesOnlyStatefulChildren() + { + var child = new ConfidenceProbeFunction { ConfidenceLevel = 0.5d }; + var lower = new CompositeFunction(new IUnivariateFunction[] { child }) { ConfidenceLevel = 0.2d }; + var upper = new CompositeFunction(new IUnivariateFunction[] { child }) { ConfidenceLevel = 0.8d }; + var configured = new CompositeFunction(new IUnivariateFunction[] { child }); + int failures = 0; + + Parallel.For(0, 600, i => + { + int branch = i % 3; + double expected = branch == 0 ? 0.2d : branch == 1 ? 0.8d : 0.5d; + double actual = branch == 0 ? lower.Function(0d) + : branch == 1 ? upper.Function(0d) + : configured.Function(0d); + if (actual != expected) Interlocked.Increment(ref failures); + }); + + Assert.AreEqual(0, failures); + Assert.AreEqual(0.5d, child.ConfidenceLevel, 0d); + + child.IsDeterministic = true; + child.ThrowOnConfidenceAssignment = true; + Assert.AreEqual(0.5d, lower.Function(0d), 0d, + "Deterministic child evaluation must not mutate confidence state."); + } + + /// + /// Test that a rejected weight update leaves the prior weights intact, and that + /// undefined mode values are rejected at the setter and on deserialization. + /// + [TestMethod] + public void Test_RejectsInvalidStateAtomically() + { + var composite = new CompositeFunction( + new IUnivariateFunction[] { new LinearFunction(), new LinearFunction(1d, 2d) }, + new[] { 0.25d, 0.75d }); + composite.SetParameters(new[] { double.NaN, 0.75d }); + Assert.IsTrue(composite.ParametersValid); + Assert.AreEqual(0.25d, composite.Weights[0], 0d); + Assert.Throws(() => composite.Mode = (CompositeFunctionMode)999); + + XElement malformed = composite.ToXElement(); + malformed.SetAttributeValue(nameof(CompositeFunction.Mode), "999"); + Assert.Throws(() => CompositeFunction.FromXElement(malformed)); + } + + /// + /// Test function that exposes confidence-state changes and controlled evaluation failures. + /// + private sealed class ConfidenceProbeFunction : IUnivariateFunction + { + /// The configured confidence level. + private double _confidenceLevel = -1d; + + /// Gets or sets whether evaluations throw a controlled exception. + public bool ThrowOnEvaluation { get; set; } + + /// Gets or sets whether confidence-level assignments throw a controlled exception. + public bool ThrowOnConfidenceAssignment { get; set; } + + /// + public int NumberOfParameters => 0; + + /// + public bool ParametersValid => true; + + /// + public double Minimum { get; set; } = double.MinValue; + + /// + public double Maximum { get; set; } = double.MaxValue; + + /// + public double[] MinimumOfParameters => Array.Empty(); + + /// + public double[] MaximumOfParameters => Array.Empty(); + + /// + public bool IsDeterministic { get; set; } + + /// + public double ConfidenceLevel + { + get { return _confidenceLevel; } + set + { + if (ThrowOnConfidenceAssignment) throw new InvalidOperationException("Confidence assignment was not expected."); + _confidenceLevel = value; + } + } + + /// + /// Validates that no parameters are supplied to this parameterless test function. + /// + /// The parameter collection, which must be empty. + /// Thrown when is null. + /// Thrown when is not empty. + public void SetParameters(IList parameters) + { + if (parameters == null) throw new ArgumentNullException(nameof(parameters)); + if (parameters.Count != 0) throw new ArgumentException("This test function has no parameters.", nameof(parameters)); + } + + /// + /// Reports that the parameterless test function has no range-validation error. + /// + /// The parameter collection. + /// Ignored because this test function has no parameters. + /// . + public ArgumentOutOfRangeException ValidateParameters(IList parameters, bool throwException) + { + return null; + } + + /// + /// Returns the configured confidence level after checking for concurrent state changes. + /// + /// The unused evaluation point. + /// The configured confidence level. + /// Thrown when controlled failure is enabled or confidence state changes during evaluation. + public double Function(double x) + { + if (ThrowOnEvaluation) throw new InvalidOperationException("Test evaluation failure."); + double first = ConfidenceLevel; + Thread.SpinWait(10000); + if (first != ConfidenceLevel) throw new InvalidOperationException("Confidence state changed during evaluation."); + return ConfidenceLevel; + } + + /// + /// Returns the configured confidence level as the inverse result. + /// + /// The unused value to invert. + /// The configured confidence level. + /// Thrown when controlled failure is enabled. + public double InverseFunction(double y) + { + if (ThrowOnEvaluation) throw new InvalidOperationException("Test inverse failure."); + return ConfidenceLevel; + } + } } } diff --git a/Test_Numerics/Functions/Test_EnsembleFunction.cs b/Test_Numerics/Functions/Test_EnsembleFunction.cs index f90c5396..65d96844 100644 --- a/Test_Numerics/Functions/Test_EnsembleFunction.cs +++ b/Test_Numerics/Functions/Test_EnsembleFunction.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using System.Xml.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Functions; using Numerics.Mathematics.Optimization; @@ -128,7 +129,39 @@ public void Test_Serialization_RoundTrip() Assert.AreEqual(a.Function(5d), b.Function(5d), 1E-12, $"Draw {i} must evaluate identically after the round-trip."); } - Assert.Throws(() => EnsembleFunction.FromXElement(new System.Xml.Linq.XElement(nameof(EnsembleFunction)))); + Assert.Throws(() => EnsembleFunction.FromXElement(new XElement(nameof(EnsembleFunction)))); + } + + /// + /// Test that construction deep-copies the template and every parameter set, so + /// caller-owned arrays and exposed sets cannot mutate later draws, and that serialized + /// sets with malformed values or fitness are rejected. + /// + [TestMethod] + public void Test_OwnsDeepCopies_AndValidatesXmlSets() + { + var values = new[] { 1d, 0.5d, 2d, 0.1d }; + var ensemble = new EnsembleFunction( + new SegmentedPowerFunction(values), + new[] { new ParameterSet(values, 1d, 0.5d) }); + + values[0] = 99d; + Assert.AreEqual(1d, ((SegmentedPowerFunction)ensemble.Sample(0)).GetBreakpoint(1), 0d); + + ParameterSet exposed = ensemble.ParameterSets[0]; + exposed.Values[0] = 88d; + Assert.AreEqual(1d, ((SegmentedPowerFunction)ensemble.Sample(0)).GetBreakpoint(1), 0d); + Assert.Throws(() => ensemble.Sample(double.NaN)); + + XElement invalidValues = ensemble.ToXElement(); + invalidValues.Element("ParameterSets")!.Element(nameof(ParameterSet))! + .SetAttributeValue(nameof(ParameterSet.Values), "1|0.5|0|0.1"); + Assert.Throws(() => EnsembleFunction.FromXElement(invalidValues)); + + XElement invalidFitness = ensemble.ToXElement(); + invalidFitness.Element("ParameterSets")!.Element(nameof(ParameterSet))! + .SetAttributeValue(nameof(ParameterSet.Fitness), "NaN"); + Assert.Throws(() => EnsembleFunction.FromXElement(invalidFitness)); } } } diff --git a/Test_Numerics/Functions/Test_SegmentedPowerFunction.cs b/Test_Numerics/Functions/Test_SegmentedPowerFunction.cs index 94556045..88759f51 100644 --- a/Test_Numerics/Functions/Test_SegmentedPowerFunction.cs +++ b/Test_Numerics/Functions/Test_SegmentedPowerFunction.cs @@ -1,4 +1,5 @@ using System; +using System.Xml.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Functions; @@ -145,5 +146,36 @@ public void Test_Serialization_RoundTrip() Assert.AreEqual(UnivariateFunctionType.SegmentedPower, UnivariateFunctionFactory.GetFunctionType(original)); Assert.IsInstanceOfType(UnivariateFunctionFactory.CreateFunction(UnivariateFunctionType.SegmentedPower), typeof(SegmentedPowerFunction)); } + + /// + /// Test that a deterministic function restores through the factory with its Maximum, + /// that a rejected parameter update leaves the prior state valid and unchanged, and + /// that the Maximum setter and malformed serialized payloads are rejected. + /// + [TestMethod] + public void Test_DeterministicRestore_AndAtomicValidation() + { + var function = new SegmentedPowerFunction(1) { IsDeterministic = true, Maximum = 20d }; + function.SetParameters(new[] { 1d, 0.5d, 2d, 0d }); + Assert.IsTrue(function.ParametersValid); + + var restored = (SegmentedPowerFunction)UnivariateFunctionFactory.CreateFromXElement(function.ToXElement()); + Assert.IsTrue(restored.IsDeterministic); + Assert.AreEqual(20d, restored.Maximum, 0d); + Assert.AreEqual(function.Function(4d), restored.Function(4d), 0d); + + double originalBeta = function.GetBeta(1); + function.SetParameters(new[] { 1d, 0.5d, 0d, 0d }); + Assert.IsTrue(function.ParametersValid, "A rejected update must leave the prior state valid."); + Assert.AreEqual(originalBeta, function.GetBeta(1), 0d); + + Assert.Throws(() => function.Maximum = function.Minimum); + Assert.AreEqual(20d, function.Maximum, 0d); + Assert.Throws(() => new SegmentedPowerFunction(new[] { 1d, 0.5d, 0d, 0.1d })); + + XElement malformed = function.ToXElement(); + malformed.SetAttributeValue(nameof(SegmentedPowerFunction.Maximum), "0"); + Assert.Throws(() => SegmentedPowerFunction.FromXElement(malformed)); + } } } diff --git a/Test_Numerics/Mathematics/Integration/Test_AdaptiveGaussKronrodRecorder.cs b/Test_Numerics/Mathematics/Integration/Test_AdaptiveGaussKronrodRecorder.cs index cd138fef..55ce6947 100644 --- a/Test_Numerics/Mathematics/Integration/Test_AdaptiveGaussKronrodRecorder.cs +++ b/Test_Numerics/Mathematics/Integration/Test_AdaptiveGaussKronrodRecorder.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Mathematics; using Numerics.Mathematics.Integration; @@ -121,5 +122,30 @@ public void Test_Recorder_OffIsByteIdentical() Assert.AreEqual(plain.FunctionEvaluations, recorded.FunctionEvaluations, "Recording must not change the evaluation count."); } + + /// + /// Test that clearing the recorder from inside a callback does not affect the active + /// integration: the snapshot taken at the start of the run remains in effect until the + /// integration completes. + /// + [TestMethod] + public void Test_Recorder_IsSnapshotForTheIntegration() + { + int calls = 0; + AdaptiveGaussKronrod integration = null; + integration = new AdaptiveGaussKronrod(x => x * x, 0d, 1d) + { + MinDepth = 2, + Recorder = (x, weight, value) => + { + Interlocked.Increment(ref calls); + integration!.Recorder = null; + }, + }; + + integration.Integrate(); + Assert.AreEqual(IntegrationStatus.Success, integration.Status); + Assert.IsGreaterThan(1, calls, "The recorder snapshot remains active until the integration completes."); + } } } diff --git a/Test_Numerics/Mathematics/Integration/Test_VegasTailFocusJacobian.cs b/Test_Numerics/Mathematics/Integration/Test_VegasTailFocusJacobian.cs index 4488a91e..0116765f 100644 --- a/Test_Numerics/Mathematics/Integration/Test_VegasTailFocusJacobian.cs +++ b/Test_Numerics/Mathematics/Integration/Test_VegasTailFocusJacobian.cs @@ -77,5 +77,22 @@ public void Test_WeightSum_EqualsDomainVolumeAtEveryGamma() $"γ = {gamma}: the weights must sum to the domain volume per evaluation batch (Jacobian folded into the weight)."); } } + + /// + /// Test that the tail-focus parameter and the rare-event configuration reject + /// non-finite or non-positive values, leaving the configured state unchanged. + /// + [TestMethod] + public void Test_TailFocus_RejectsInvalidParametersAtomically() + { + var vegas = new Vegas((point, weight) => point[0] * weight, 1, new[] { 0d }, new[] { 1d }); + Assert.Throws(() => vegas.TailFocusParameter = 0d); + Assert.Throws(() => vegas.TailFocusParameter = double.NaN); + Assert.Throws(() => vegas.TailFocusParameter = double.PositiveInfinity); + Assert.AreEqual(1d, vegas.TailFocusParameter, 0d); + Assert.Throws(() => vegas.ConfigureForRareEvents(0d)); + Assert.Throws(() => vegas.ConfigureForRareEvents(1d)); + Assert.Throws(() => vegas.ConfigureForRareEvents(double.NaN)); + } } } diff --git a/Test_Numerics/Mathematics/Special Functions/Test_SpecialFunctions.cs b/Test_Numerics/Mathematics/Special Functions/Test_SpecialFunctions.cs index 9ca6b01d..1f03f59e 100644 --- a/Test_Numerics/Mathematics/Special Functions/Test_SpecialFunctions.cs +++ b/Test_Numerics/Mathematics/Special Functions/Test_SpecialFunctions.cs @@ -306,5 +306,19 @@ public void Test_PolynomialRev_1() double actual = Evaluate.PolynomialRev_1(coeffs, x); Assert.AreEqual(valid, actual); } + + /// + /// Test that NextCombination rejects null, empty, duplicated, out-of-range, and + /// negative-count tuples. + /// + [TestMethod] + public void Test_NextCombination_RejectsInvalidTuples() + { + Assert.Throws(() => Factorial.NextCombination(null!, 3)); + Assert.Throws(() => Factorial.NextCombination(Array.Empty(), 3)); + Assert.Throws(() => Factorial.NextCombination(new[] { 0, 0 }, 3)); + Assert.Throws(() => Factorial.NextCombination(new[] { 0, 3 }, 3)); + Assert.Throws(() => Factorial.NextCombination(new[] { 0 }, -1)); + } } } diff --git a/Test_Numerics/Test_CorrectnessRepairs.cs b/Test_Numerics/Test_CorrectnessRepairs.cs deleted file mode 100644 index 5152d817..00000000 --- a/Test_Numerics/Test_CorrectnessRepairs.cs +++ /dev/null @@ -1,532 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Threading; -using System.Threading.Tasks; -using System.Xml.Linq; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Numerics.Data; -using Numerics.Data.Statistics; -using Numerics.Distributions; -using Numerics.Functions; -using Numerics.Mathematics; -using Numerics.Mathematics.Integration; -using Numerics.Mathematics.Optimization; -using Numerics.Mathematics.SpecialFunctions; - -namespace Correctness -{ - /// - /// Regression tests for function state, validation, and serialization behavior. - /// - [TestClass] - public class Test_FunctionCorrectnessRepairs - { - /// Verifies deterministic restoration and atomic validation for segmented power functions. - [TestMethod] - public void SegmentedPower_DeterministicXmlAndAtomicValidation() - { - var function = new SegmentedPowerFunction(1) { IsDeterministic = true, Maximum = 20d }; - function.SetParameters(new[] { 1d, 0.5d, 2d, 0d }); - Assert.IsTrue(function.ParametersValid); - - var restored = (SegmentedPowerFunction)UnivariateFunctionFactory.CreateFromXElement(function.ToXElement()); - Assert.IsTrue(restored.IsDeterministic); - Assert.AreEqual(20d, restored.Maximum, 0d); - Assert.AreEqual(function.Function(4d), restored.Function(4d), 0d); - - double originalBeta = function.GetBeta(1); - function.SetParameters(new[] { 1d, 0.5d, 0d, 0d }); - Assert.IsTrue(function.ParametersValid, "A rejected update must leave the prior state valid."); - Assert.AreEqual(originalBeta, function.GetBeta(1), 0d); - - Assert.Throws(() => function.Maximum = function.Minimum); - Assert.AreEqual(20d, function.Maximum, 0d); - Assert.Throws(() => new SegmentedPowerFunction(new[] { 1d, 0.5d, 0d, 0.1d })); - - XElement malformed = function.ToXElement(); - malformed.SetAttributeValue(nameof(SegmentedPowerFunction.Maximum), "0"); - Assert.Throws(() => SegmentedPowerFunction.FromXElement(malformed)); - } - - /// Verifies that composite evaluation restores child confidence state after an exception. - [TestMethod] - public void Composite_RestoresChildStateAfterExceptions() - { - var child = new ConfidenceProbeFunction { ConfidenceLevel = 0.35d, ThrowOnEvaluation = true }; - var composite = new CompositeFunction(new IUnivariateFunction[] { child }) { ConfidenceLevel = 0.8d }; - - Assert.Throws(() => composite.Function(1d)); - Assert.AreEqual(0.35d, child.ConfidenceLevel, 0d); - Assert.Throws(() => composite.InverseFunction(1d)); - Assert.AreEqual(0.35d, child.ConfidenceLevel, 0d); - } - - /// Verifies concurrent stateful evaluation and the lock-free deterministic path. - [TestMethod] - public void Composite_SynchronizesOnlyStatefulChildren() - { - var child = new ConfidenceProbeFunction { ConfidenceLevel = 0.5d }; - var lower = new CompositeFunction(new IUnivariateFunction[] { child }) { ConfidenceLevel = 0.2d }; - var upper = new CompositeFunction(new IUnivariateFunction[] { child }) { ConfidenceLevel = 0.8d }; - var configured = new CompositeFunction(new IUnivariateFunction[] { child }); - int failures = 0; - - Parallel.For(0, 600, i => - { - int branch = i % 3; - double expected = branch == 0 ? 0.2d : branch == 1 ? 0.8d : 0.5d; - double actual = branch == 0 ? lower.Function(0d) - : branch == 1 ? upper.Function(0d) - : configured.Function(0d); - if (actual != expected) Interlocked.Increment(ref failures); - }); - - Assert.AreEqual(0, failures); - Assert.AreEqual(0.5d, child.ConfidenceLevel, 0d); - - child.IsDeterministic = true; - child.ThrowOnConfidenceAssignment = true; - Assert.AreEqual(0.5d, lower.Function(0d), 0d, - "Deterministic child evaluation must not mutate confidence state."); - } - - /// Verifies atomic weight rejection and composite mode validation. - [TestMethod] - public void Composite_RejectsInvalidStateAtomically() - { - var composite = new CompositeFunction( - new IUnivariateFunction[] { new LinearFunction(), new LinearFunction(1d, 2d) }, - new[] { 0.25d, 0.75d }); - composite.SetParameters(new[] { double.NaN, 0.75d }); - Assert.IsTrue(composite.ParametersValid); - Assert.AreEqual(0.25d, composite.Weights[0], 0d); - Assert.Throws(() => composite.Mode = (CompositeFunctionMode)999); - - XElement malformed = composite.ToXElement(); - malformed.SetAttributeValue(nameof(CompositeFunction.Mode), "999"); - Assert.Throws(() => CompositeFunction.FromXElement(malformed)); - } - - /// Verifies ensemble parameter ownership and serialized-set validation. - [TestMethod] - public void Ensemble_OwnsDeepCopiesAndValidatesXmlSets() - { - var values = new[] { 1d, 0.5d, 2d, 0.1d }; - var ensemble = new EnsembleFunction( - new SegmentedPowerFunction(values), - new[] { new ParameterSet(values, 1d, 0.5d) }); - - values[0] = 99d; - Assert.AreEqual(1d, ((SegmentedPowerFunction)ensemble.Sample(0)).GetBreakpoint(1), 0d); - - ParameterSet exposed = ensemble.ParameterSets[0]; - exposed.Values[0] = 88d; - Assert.AreEqual(1d, ((SegmentedPowerFunction)ensemble.Sample(0)).GetBreakpoint(1), 0d); - Assert.Throws(() => ensemble.Sample(double.NaN)); - - XElement invalidValues = ensemble.ToXElement(); - invalidValues.Element("ParameterSets")!.Element(nameof(ParameterSet))! - .SetAttributeValue(nameof(ParameterSet.Values), "1|0.5|0|0.1"); - Assert.Throws(() => EnsembleFunction.FromXElement(invalidValues)); - - XElement invalidFitness = ensemble.ToXElement(); - invalidFitness.Element("ParameterSets")!.Element(nameof(ParameterSet))! - .SetAttributeValue(nameof(ParameterSet.Fitness), "NaN"); - Assert.Throws(() => EnsembleFunction.FromXElement(invalidFitness)); - } - - /// - /// Test function that exposes confidence-state changes and controlled evaluation failures. - /// - private sealed class ConfidenceProbeFunction : IUnivariateFunction - { - /// The configured confidence level. - private double _confidenceLevel = -1d; - - /// Gets or sets whether evaluations throw a controlled exception. - public bool ThrowOnEvaluation { get; set; } - - /// Gets or sets whether confidence-level assignments throw a controlled exception. - public bool ThrowOnConfidenceAssignment { get; set; } - - /// - public int NumberOfParameters => 0; - - /// - public bool ParametersValid => true; - - /// - public double Minimum { get; set; } = double.MinValue; - - /// - public double Maximum { get; set; } = double.MaxValue; - - /// - public double[] MinimumOfParameters => Array.Empty(); - - /// - public double[] MaximumOfParameters => Array.Empty(); - - /// - public bool IsDeterministic { get; set; } - - /// - public double ConfidenceLevel - { - get { return _confidenceLevel; } - set - { - if (ThrowOnConfidenceAssignment) throw new InvalidOperationException("Confidence assignment was not expected."); - _confidenceLevel = value; - } - } - - /// - /// Validates that no parameters are supplied to this parameterless test function. - /// - /// The parameter collection, which must be empty. - /// Thrown when is null. - /// Thrown when is not empty. - public void SetParameters(IList parameters) - { - if (parameters == null) throw new ArgumentNullException(nameof(parameters)); - if (parameters.Count != 0) throw new ArgumentException("This test function has no parameters.", nameof(parameters)); - } - - /// - /// Reports that the parameterless test function has no range-validation error. - /// - /// The parameter collection. - /// Ignored because this test function has no parameters. - /// . - public ArgumentOutOfRangeException ValidateParameters(IList parameters, bool throwException) - { - return null; - } - - /// - /// Returns the configured confidence level after checking for concurrent state changes. - /// - /// The unused evaluation point. - /// The configured confidence level. - /// Thrown when controlled failure is enabled or confidence state changes during evaluation. - public double Function(double x) - { - if (ThrowOnEvaluation) throw new InvalidOperationException("Test evaluation failure."); - double first = ConfidenceLevel; - Thread.SpinWait(10000); - if (first != ConfidenceLevel) throw new InvalidOperationException("Confidence state changed during evaluation."); - return ConfidenceLevel; - } - - /// - /// Returns the configured confidence level as the inverse result. - /// - /// The unused value to invert. - /// The configured confidence level. - /// Thrown when controlled failure is enabled. - public double InverseFunction(double y) - { - if (ThrowOnEvaluation) throw new InvalidOperationException("Test inverse failure."); - return ConfidenceLevel; - } - } - } - - /// - /// Regression tests for probability and jackknife edge cases. - /// - [TestClass] - public class Test_StatisticsCorrectnessRepairs - { - /// Verifies residual mass when capped enumeration omits the no-event row. - [TestMethod] - public void LazyExclusive_CappedWithoutNoEventRowPreservesUnionMass() - { - double[] probabilities = { 0.4d, 0.35d, 0.3d, 0.25d, 0.2d, 0.15d }; - var output = new List(); - var indicators = new List(); - - var status = Probability.IndependentExclusiveLazy(probabilities, output, indicators, - includeNoEventRow: false, maxEmittedCombinations: 10, - absoluteTolerance: 0d, relativeTolerance: 0d); - - Assert.AreEqual(Probability.ExclusiveEnumerationStatus.Capped, status); - double noEventMass = probabilities.Aggregate(1d, (mass, probability) => mass * (1d - probability)); - Assert.AreEqual(1d - noEventMass, output.Sum(), 1E-12); - Assert.HasCount(11, output); - } - - /// Verifies structural and tolerance validation for pooled enumeration. - [TestMethod] - public void PooledExclusive_RejectsMalformedMetadata() - { - double[] probabilities = { 0.2d, 0.3d }; - var output = new List(); - var indicators = new List(); - int[,] rows = { { 1, 0 }, { 0, 1 }, { 1, 1 } }; - - Assert.Throws(() => Probability.IndependentExclusive( - probabilities, new[] { 2 }, rows, output, indicators)); - Assert.Throws(() => Probability.IndependentExclusive( - probabilities, new[] { 1, 2 }, rows, output, indicators)); - Assert.Throws(() => Probability.IndependentExclusive( - probabilities, new[] { 2, 1 }, rows, output, indicators, double.NaN)); - } - - /// Verifies public combination tuple validation. - [TestMethod] - public void NextCombination_RejectsInvalidTuples() - { - Assert.Throws(() => Factorial.NextCombination(null!, 3)); - Assert.Throws(() => Factorial.NextCombination(Array.Empty(), 3)); - Assert.Throws(() => Factorial.NextCombination(new[] { 0, 0 }, 3)); - Assert.Throws(() => Factorial.NextCombination(new[] { 0, 3 }, 3)); - Assert.Throws(() => Factorial.NextCombination(new[] { 0 }, -1)); - } - - /// Verifies single-element behavior and callback sample isolation. - [TestMethod] - public void Jackknife_PreservesSingleElementAndIsolatesCallbacks() - { - int calls = 0; - double standardError = Statistics.JackKnifeStandardError(new[] { 5d }, sample => - { - Interlocked.Increment(ref calls); - return sample.Count; - }); - Assert.AreEqual(0d, standardError, 0d); - Assert.AreEqual(0, calls, "The single-element standard error does not evaluate an empty sample."); - - double[] single = Statistics.JackKnifeSample(new[] { 5d }, sample => - { - Interlocked.Increment(ref calls); - return sample.Count; - }); - Assert.IsNotNull(single); - Assert.AreEqual(0d, single[0], 0d); - Assert.AreEqual(1, calls); - - double[] original = { 1d, 2d, 3d, 4d }; - var callbackSamples = new List>(); - object sync = new object(); - Statistics.JackKnifeSample(original, sample => - { - lock (sync) callbackSamples.Add(sample); - if (sample.Count > 0) sample[0] = -100d; - return sample.Count; - }); - CollectionAssert.AreEqual(new[] { 1d, 2d, 3d, 4d }, original); - Assert.HasCount(original.Length, callbackSamples); - Assert.AreEqual(original.Length, callbackSamples.Distinct().Count()); - } - } - - /// - /// Regression tests for bootstrap fitting and interpolation behavior. - /// - [TestClass] - public class Test_BootstrapCorrectnessRepairs - { - /// Verifies successful-fit denominators and explicit all-failure errors. - [TestMethod] - public void Bootstrap_UsesOnlySuccessfulFitsAndRejectsAllFailures() - { - var parent = new Normal(0d, 1d); - var analysis = new BootstrapAnalysis(parent, ParameterEstimationMethod.MethodOfMoments, 10, 1234); - IUnivariateDistribution[] mixed = { new Normal(0d, 1d), null!, new Normal(1d, 1d) }; - - double[] mean = analysis.ExpectedProbabilities(new[] { 0d }, mixed); - double expected = 0.5d * (new Normal(0d, 1d).CDF(0d) + new Normal(1d, 1d).CDF(0d)); - Assert.AreEqual(expected, mean[0], 1E-14); - Assert.Throws(() => - analysis.ExpectedProbabilities(new[] { 0d }, new IUnivariateDistribution[] { null!, null! })); - - var aggregate = Assert.Throws(() => analysis.Distributions(new[] - { - new ParameterSet(new[] { 0d, -1d }, 0d), - new ParameterSet(new[] { double.NaN, 1d }, 0d), - })); - Assert.HasCount(2, aggregate.InnerExceptions); - } - - /// Verifies sign-preserving transforms for negative quantiles. - [TestMethod] - public void Bootstrap_NormalIntervalsPreserveNegativeQuantiles() - { - var parent = new Normal(-10d, 1d); - var analysis = new BootstrapAnalysis(parent, ParameterEstimationMethod.MethodOfMoments, 10, 1234); - IUnivariateDistribution[] fits = - { - new Normal(-9.5d, 1d), - new Normal(-10d, 1.1d), - null!, - new Normal(-10.5d, 0.9d), - }; - - double[,] interval = analysis.NormalQuantileCI(new[] { 0.5d }, 0.1d, fits); - Assert.IsFalse(double.IsNaN(interval[0, 0]) || double.IsInfinity(interval[0, 0])); - Assert.IsFalse(double.IsNaN(interval[0, 1]) || double.IsInfinity(interval[0, 1])); - Assert.IsLessThan(0d, interval[0, 0]); - Assert.IsLessThan(0d, interval[0, 1]); - } - - /// Verifies interpolation equivalence for sorted and unsorted ordinates. - [TestMethod] - public void Bootstrap_InterpolationKeepsSortedPairsTogether() - { - var parent = new Normal(0d, 1d); - var analysis = new BootstrapAnalysis(parent, ParameterEstimationMethod.MethodOfMoments, 10, 1234); - IUnivariateDistribution[] fits = { new Normal(0d, 1d), new Normal(1d, 2d) }; - double[] sorted = { -3d, -1d, 0d, 2d, 5d }; - double[] unsorted = { 2d, -3d, 5d, 0d, -1d }; - double[] probabilities = { 0.1d, 0.5d, 0.9d }; - - double[] expected = analysis.ExpectedProbabilities(sorted, probabilities, fits); - double[] actual = analysis.ExpectedProbabilities(unsorted, probabilities, fits); - CollectionAssert.AreEqual(expected, actual); - } - } - - /// - /// Regression tests for distribution serialization and numerical edge cases. - /// - [TestClass] - public class Test_DistributionCorrectnessRepairs - { - /// Verifies rejection of malformed serialized distribution data. - [TestMethod] - public void DistributionXml_RejectsMalformedTablesEnumsAndWeights() - { - var empirical = new EmpiricalDistribution(new[] { 1d, 2d }, new[] { 0d, 1d }); - XElement invalidOrder = empirical.ToXElement(); - invalidOrder.SetAttributeValue("ProbabilityOrder", "999"); - Assert.Throws(() => UnivariateDistributionFactory.CreateDistribution(invalidOrder)); - - XElement invalidTransform = empirical.ToXElement(); - invalidTransform.SetAttributeValue(nameof(EmpiricalDistribution.XTransform), "999"); - Assert.Throws(() => EmpiricalDistribution.FromXElement(invalidTransform)); - - var kernel = new KernelDensity(new[] { 1d, 2d, 3d }, new[] { 1d, 1d, 1d }, KernelDensity.KernelType.Gaussian, 0.5d); - XElement zeroWeights = kernel.ToXElement(); - zeroWeights.SetAttributeValue("Weights", "0|0|0"); - Assert.Throws(() => KernelDensity.FromXElement(zeroWeights)); - - XElement invalidBandwidth = kernel.ToXElement(); - invalidBandwidth.SetAttributeValue(nameof(KernelDensity.Bandwidth), "NaN"); - Assert.Throws(() => KernelDensity.FromXElement(invalidBandwidth)); - - XElement missingType = new Normal().ToXElement(); - missingType.Attribute(nameof(UnivariateDistributionBase.Type))!.Remove(); - Assert.Throws(() => UnivariateDistributionFactory.CreateDistribution(missingType)); - } - - /// Verifies occupied support, total mass, and mean for lattice convolution. - [TestMethod] - public void DiscreteConvolution_UsesOccupiedSupportAndPositiveMass() - { - EmpiricalDistribution.ConvolveDiscrete( - new[] { -100d, 2d, 5d }, new[] { 0d, 0.25d, 0.75d }, - new[] { 3d, 7d, 100d }, new[] { 0.5d, 0.5d, 0d }, - 256, out double[] values, out double[] masses); - - Assert.AreEqual(5d, values[0], 1E-12); - double step = values[1] - values[0]; - Assert.IsGreaterThanOrEqualTo(12d, values[values.Length - 1]); - Assert.IsLessThanOrEqualTo(step + 1E-12, values[values.Length - 1] - 12d, - "The occupied lattice may extend at most one node beyond the exact support."); - Assert.AreEqual(1d, masses.Sum(), 1E-12); - double mean = values.Zip(masses, (value, mass) => value * mass).Sum(); - Assert.AreEqual(9.25d, mean, 1E-10); - - Assert.Throws(() => EmpiricalDistribution.ConvolveDiscrete( - new[] { 1d, 2d }, new[] { 0d, 0d }, new[] { 1d }, new[] { 1d }, - 256, out _, out _)); - } - - /// Verifies early rejection of degenerate logarithmic output support. - [TestMethod] - public void LogConvolution_RejectsDegenerateSupportBeforeFft() - { - var point1 = new EmpiricalDistribution(new[] { 2d, 2d }, new[] { 0d, 1d }); - var point2 = new EmpiricalDistribution(new[] { 3d, 3d }, new[] { 0d, 1d }); - Assert.Throws(() => - EmpiricalDistribution.Convolve(point1, point2, 128, logSpacedOutput: true)); - } - - /// Verifies competing-risk seed invalidation, cloning, and serialization. - [TestMethod] - public void CompetingRisks_SeedInvalidatesCachesAndRoundTrips() - { - var distribution = new CompetingRisks(new UnivariateDistributionBase[] - { - new Normal(0d, 1d), - new Exponential(2d), - }) { PRNGSeed = 2468 }; - - FieldInfo mvnCreated = typeof(CompetingRisks).GetField("_mvnCreated", BindingFlags.Instance | BindingFlags.NonPublic)!; - FieldInfo empiricalCreated = typeof(CompetingRisks).GetField("_empiricalCDFCreated", BindingFlags.Instance | BindingFlags.NonPublic)!; - mvnCreated.SetValue(distribution, true); - empiricalCreated.SetValue(distribution, true); - distribution.PRNGSeed = 1357; - Assert.IsFalse((bool)mvnCreated.GetValue(distribution)!); - Assert.IsFalse((bool)empiricalCreated.GetValue(distribution)!); - - var clone = (CompetingRisks)distribution.Clone(); - Assert.AreEqual(1357, clone.PRNGSeed); - var restored = (CompetingRisks)UnivariateDistributionFactory.CreateDistribution(distribution.ToXElement()); - Assert.AreEqual(1357, restored.PRNGSeed); - - XElement malformed = distribution.ToXElement(); - malformed.SetAttributeValue(nameof(CompetingRisks.Dependency), "999"); - Assert.Throws(() => CompetingRisks.FromXElement(malformed)); - - var multivariate = new MultivariateNormal(2); - Assert.Throws(() => multivariate.MVNUNI = null!); - } - } - - /// - /// Regression tests for integration state and tail-focus validation. - /// - [TestClass] - public class Test_IntegrationCorrectnessRepairs - { - /// Verifies that recorder mutation does not alter an active integration. - [TestMethod] - public void AdaptiveRecorder_IsSnapshottedForAnIntegration() - { - int calls = 0; - AdaptiveGaussKronrod integration = null; - integration = new AdaptiveGaussKronrod(x => x * x, 0d, 1d) - { - MinDepth = 2, - Recorder = (x, weight, value) => - { - Interlocked.Increment(ref calls); - integration!.Recorder = null; - }, - }; - - integration.Integrate(); - Assert.AreEqual(IntegrationStatus.Success, integration.Status); - Assert.IsGreaterThan(1, calls, "The recorder snapshot remains active until the integration completes."); - } - - /// Verifies finite positive tail-focus validation. - [TestMethod] - public void Vegas_RejectsInvalidTailFocusParametersAtomically() - { - var vegas = new Vegas((point, weight) => point[0] * weight, 1, new[] { 0d }, new[] { 1d }); - Assert.Throws(() => vegas.TailFocusParameter = 0d); - Assert.Throws(() => vegas.TailFocusParameter = double.NaN); - Assert.Throws(() => vegas.TailFocusParameter = double.PositiveInfinity); - Assert.AreEqual(1d, vegas.TailFocusParameter, 0d); - Assert.Throws(() => vegas.ConfigureForRareEvents(0d)); - Assert.Throws(() => vegas.ConfigureForRareEvents(1d)); - Assert.Throws(() => vegas.ConfigureForRareEvents(double.NaN)); - } - } -} \ No newline at end of file From 58116813004b082c36099419ea54530b37217ce8 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 3 Aug 2026 11:40:09 -0600 Subject: [PATCH 040/222] Describe test contracts without change narration Test summaries and inline comments now state the behavior under test; the expected-value derivations already in the docstrings remain the justification for every pinned constant. --- Test_Numerics/Data/Interpolation/Test_Linear.cs | 1 - .../Data/Statistics/Test_GoodnessOfFit.cs | 4 ---- Test_Numerics/Data/Time Series/Test_TimeSeries.cs | 15 +++++++-------- .../Univariate/Test_CompetingRisks.cs | 1 - .../Sampling/MCMC/Test_MCMCResults_Recompute.cs | 1 - 5 files changed, 7 insertions(+), 15 deletions(-) diff --git a/Test_Numerics/Data/Interpolation/Test_Linear.cs b/Test_Numerics/Data/Interpolation/Test_Linear.cs index 5aa923fc..3e50204f 100644 --- a/Test_Numerics/Data/Interpolation/Test_Linear.cs +++ b/Test_Numerics/Data/Interpolation/Test_Linear.cs @@ -299,7 +299,6 @@ public void Test_Rev_Z() Assert.AreEqual(0.36093855992815d, Y3, 1E-6); } - // ??? /// /// Tests linear interpolation from list inputs. /// diff --git a/Test_Numerics/Data/Statistics/Test_GoodnessOfFit.cs b/Test_Numerics/Data/Statistics/Test_GoodnessOfFit.cs index 997892ee..8bcb4bd0 100644 --- a/Test_Numerics/Data/Statistics/Test_GoodnessOfFit.cs +++ b/Test_Numerics/Data/Statistics/Test_GoodnessOfFit.cs @@ -402,8 +402,6 @@ public void Test_KlingGuptaEfficiency() var modeled = new double[] { 3.0, -0.5, 2.0, 1.5, 3.0, 2.9, 2.1, 0.8 }; double KGE = GoodnessOfFit.KlingGuptaEfficiency(observed, modeled); - // Corrected expected value based on mathematical calculation - // Previous test value of 0.9125211 was incorrect double trueKGE = 0.88573; Assert.AreEqual(trueKGE, KGE, 1E-4); @@ -435,8 +433,6 @@ public void Test_KlingGuptaEfficiencyMod() var modeled = new double[] { 3.0, -0.5, 2.0, 1.5, 3.0, 2.9, 2.1, 0.8 }; double KGEmod = GoodnessOfFit.KlingGuptaEfficiencyMod(observed, modeled); - // Corrected expected value based on mathematical calculation - // Previous test value of 0.9117433 was close but slightly off double trueKGEmod = 0.91295; Assert.AreEqual(trueKGEmod, KGEmod, 1E-4); diff --git a/Test_Numerics/Data/Time Series/Test_TimeSeries.cs b/Test_Numerics/Data/Time Series/Test_TimeSeries.cs index 9a0e7b73..b7a2c519 100644 --- a/Test_Numerics/Data/Time Series/Test_TimeSeries.cs +++ b/Test_Numerics/Data/Time Series/Test_TimeSeries.cs @@ -1249,12 +1249,11 @@ public void Test_ResampleWithKNN_OutputLength() } /// - /// Regression test for the off-by-one bug. /// On a strongly trended series x[t] = t, the conditional KNN bootstrap (Lall-Sharma) - /// should advance through the trend, accumulating mean drift of ≈ +1 per step. The - /// pre-fix implementation took the neighbor's own value rather than x[j+1], which - /// kept the trajectory hovering near the starting value (zero net drift). - /// We average drift over multiple seeds to crush the random-walk noise. + /// advances through the trend, accumulating mean drift of ≈ +1 per step: each step + /// must take the neighbor's successor value x[j+1], never the neighbor's own value, + /// or the trajectory hovers near the starting value (zero net drift). Drift is + /// averaged over multiple seeds to suppress the random-walk noise. /// [TestMethod] public void Test_ResampleWithKNN_AdvancesThroughTime() @@ -1273,11 +1272,11 @@ public void Test_ResampleWithKNN_AdvancesThroughTime() } avgDrift /= trials; - // Pre-fix: avgDrift ~ N(0, ~5) — fails this assertion clearly. - // Post-fix: avgDrift ~ +50 (drift = +1 per step over 50 steps). + // Successor sampling gives avgDrift ≈ +50 (+1 per step over 50 steps); + // neighbor-value sampling gives avgDrift ~ N(0, ~5) and fails clearly. Assert.IsGreaterThan(25.0, avgDrift, $"Expected KNN trajectory to advance through the trend (avgDrift > 25 over {steps} steps); " + - $"observed avgDrift = {avgDrift:F2}. The pre-fix off-by-one keeps the trajectory near the starting value."); + $"observed avgDrift = {avgDrift:F2}. Sampling the neighbor's own value keeps the trajectory near the starting value."); } /// diff --git a/Test_Numerics/Distributions/Univariate/Test_CompetingRisks.cs b/Test_Numerics/Distributions/Univariate/Test_CompetingRisks.cs index 188006ee..d7329ca4 100644 --- a/Test_Numerics/Distributions/Univariate/Test_CompetingRisks.cs +++ b/Test_Numerics/Distributions/Univariate/Test_CompetingRisks.cs @@ -97,7 +97,6 @@ public void Test_CR_CDF() [TestMethod] public void Test_PDF_MaxRule_SmallX_NoInfinity() { - // This test verifies the fix for division by zero when CDF ≈ 0 var dist1 = new Normal(100, 10); var dist2 = new Normal(110, 15); var cr = new CompetingRisks(new[] { dist1, dist2 }); diff --git a/Test_Numerics/Sampling/MCMC/Test_MCMCResults_Recompute.cs b/Test_Numerics/Sampling/MCMC/Test_MCMCResults_Recompute.cs index b0c0fc54..914870f3 100644 --- a/Test_Numerics/Sampling/MCMC/Test_MCMCResults_Recompute.cs +++ b/Test_Numerics/Sampling/MCMC/Test_MCMCResults_Recompute.cs @@ -14,7 +14,6 @@ namespace Sampling.MCMC /// (Rhat, ESS, Autocorrelation) AND the underlying chain output (Output, MAP). /// /// - /// This is the cornerstone of the RMC.BestFit Phase 2 reprocess-don't-clear refactor. /// A regression that drops one of the three snapshot/restore lines silently corrupts /// every convergence-diagnostic display the moment a user changes CredibleIntervalWidth /// on an estimated analysis. From 3c3aaa3c7273d131c380e29d1c3f0c750d506438 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 3 Aug 2026 11:45:20 -0600 Subject: [PATCH 041/222] Remove dead code and console output from the numerical library Dijkstra drops its commented duplicate declarations and the per-node console report of unreachable nodes (an unreachable node remains detectable by its infinite cost). MultivariateNormal drops the C++ porting residue. BivariateEmpirical documents the joint-density contract: PDF is not implemented for the interpolated empirical CDF surface and returns NaN. --- .../Multivariate/BivariateEmpirical.cs | 15 ++++++++------- .../Multivariate/MultivariateNormal.cs | 11 ----------- .../Mathematics/Optimization/Dynamic/Dijkstra.cs | 10 ---------- .../{BinaryHeapTesting.cs => Test_BinaryHeap.cs} | 0 .../{DijkstraTesting.cs => Test_ShortestPath.cs} | 0 5 files changed, 8 insertions(+), 28 deletions(-) rename Test_Numerics/Mathematics/Optimization/Dynamic/{BinaryHeapTesting.cs => Test_BinaryHeap.cs} (100%) rename Test_Numerics/Mathematics/Optimization/Dynamic/{DijkstraTesting.cs => Test_ShortestPath.cs} (100%) diff --git a/Numerics/Distributions/Multivariate/BivariateEmpirical.cs b/Numerics/Distributions/Multivariate/BivariateEmpirical.cs index 018628d4..99f6ea5d 100644 --- a/Numerics/Distributions/Multivariate/BivariateEmpirical.cs +++ b/Numerics/Distributions/Multivariate/BivariateEmpirical.cs @@ -239,10 +239,11 @@ public void SetParameters(IList x1Values, IList x2Values, double /// The Probability Density Function (PDF) of the distribution evaluated at a point X. /// /// A point in the distribution space. + /// + /// NaN. A joint density is not implemented for the interpolated empirical CDF surface. + /// public override double PDF(double[] x) { - // Validate parameters - //if (_parametersValid == false) ValidateParameters(X1Values, X2Values, ProbabilityValues, true); return PDF(x[0], x[1]); } @@ -251,13 +252,13 @@ public override double PDF(double[] x) /// /// The x1-value. /// The x2-value. + /// + /// NaN. A joint density is not implemented for the interpolated empirical CDF surface. + /// public double PDF(double x1, double x2) { - // The PDF is estimated using numerical differentiation of the CDF. - // This approach is not ideal, and is a temporary place holder, - // until I learn how to do this better. - // double h = 0.0001d; - // return (CDF(x1 + h, x2 + h) - CDF(x1 - h, x2 - h)) / (2d * h); + // A joint density is not implemented for the interpolated empirical CDF surface. + // Returning NaN keeps the non-result explicit instead of fabricating a value. return double.NaN; } diff --git a/Numerics/Distributions/Multivariate/MultivariateNormal.cs b/Numerics/Distributions/Multivariate/MultivariateNormal.cs index 63d51872..69d5c4e9 100644 --- a/Numerics/Distributions/Multivariate/MultivariateNormal.cs +++ b/Numerics/Distributions/Multivariate/MultivariateNormal.cs @@ -913,14 +913,6 @@ public static double bivnor(double ah, double ak, double r) rr = (1.0 + r) * (1.0 - r); - if (rr < 0.0) - { - //cerr << "\n"; - //cerr << "BIVNOR - Fatal error!\n"; - //cerr << " 1 < |R|.\n"; - //exit(0); - } - if (rr == 0.0) { if (r < 0.0) @@ -1459,7 +1451,6 @@ private double MVNDFN(int N, double[] W) double SUM, AI = 0, BI = 0, DI = 0, EI = 0; var Y = new double[NL]; - //double[] Y = new double[500]; double result = 1; int INFA = 0; @@ -1908,8 +1899,6 @@ private void DKBVRC(int NDIM, int MINVLS, int MAXVLS, Func Date: Mon, 3 Aug 2026 11:45:20 -0600 Subject: [PATCH 042/222] Align the dynamic-optimization test files with the naming convention --- .../Mathematics/Optimization/Dynamic/Test_BinaryHeap.cs | 2 +- .../Mathematics/Optimization/Dynamic/Test_ShortestPath.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Test_Numerics/Mathematics/Optimization/Dynamic/Test_BinaryHeap.cs b/Test_Numerics/Mathematics/Optimization/Dynamic/Test_BinaryHeap.cs index b7028b33..066a6d40 100644 --- a/Test_Numerics/Mathematics/Optimization/Dynamic/Test_BinaryHeap.cs +++ b/Test_Numerics/Mathematics/Optimization/Dynamic/Test_BinaryHeap.cs @@ -11,7 +11,7 @@ namespace Mathematics.Optimization /// Tests binary heap behavior used by dynamic optimization routines. /// [TestClass] - public class BinaryHeapTesting + public class Test_BinaryHeap { /// /// Checking weights on heap. diff --git a/Test_Numerics/Mathematics/Optimization/Dynamic/Test_ShortestPath.cs b/Test_Numerics/Mathematics/Optimization/Dynamic/Test_ShortestPath.cs index 030c673b..74bf701f 100644 --- a/Test_Numerics/Mathematics/Optimization/Dynamic/Test_ShortestPath.cs +++ b/Test_Numerics/Mathematics/Optimization/Dynamic/Test_ShortestPath.cs @@ -7,10 +7,10 @@ namespace Mathematics.Optimization /// Tests shortest-path routing behavior for Dijkstra networks. /// [TestClass] - public class ShortestPathTesting + public class Test_ShortestPath { /// - /// Testing a something that cost doesn't really matter. + /// Tests predecessor routing and cumulative costs on a simple edge graph. /// [TestMethod] public void SimpleEdgeGraphCost() From e07f80a352eb63baef8b85f363ecbd994426f7bf Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 3 Aug 2026 15:04:09 -0600 Subject: [PATCH 043/222] Fix BinaryHeap.Replace ordering and complete the heap documentation Replace now locates the node through the position map instead of a linear scan and restores heap order in both directions - a replacement with a heavier weight previously stayed at its slot and corrupted the order. The class documents its fixed-capacity, weight-agnostic, and single-node-per-index contracts, and the fuzz test drives all four operations against a linear-scan reference model. --- .../Optimization/Dynamic/BinaryHeap.cs | 104 +++++++++------ .../Optimization/Dynamic/Test_BinaryHeap.cs | 122 ++++++++++++++++++ 2 files changed, 190 insertions(+), 36 deletions(-) diff --git a/Numerics/Mathematics/Optimization/Dynamic/BinaryHeap.cs b/Numerics/Mathematics/Optimization/Dynamic/BinaryHeap.cs index 02f56a4b..4c89cc31 100644 --- a/Numerics/Mathematics/Optimization/Dynamic/BinaryHeap.cs +++ b/Numerics/Mathematics/Optimization/Dynamic/BinaryHeap.cs @@ -1,17 +1,35 @@ -using System; +using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Numerics.Mathematics.Optimization { /// - /// This is an implementation of the binary heap data structure. The binary heap is especially convenient for shortest path algorithms - /// such as Djikstra's shortest path. - /// source of inspiration: http://opendatastructures.org/versions/edition-0.1e/ods-java/10_1_BinaryHeap_Implicit_Bi.html + /// An array-backed binary min-heap keyed by node weight, with an index-to-position map that + /// supports keyed decrease-key and replace operations. The heap is especially convenient for + /// shortest path algorithms such as Dijkstra's method. /// + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + /// Description: + /// The heap has a fixed capacity set at construction; adding past the capacity throws. Node + /// weights may be any finite ordering value, including negatives — the heap orders by weight + /// and imposes no algorithmic precondition of its own. The keyed operations + /// ( and ) address nodes by their + /// and assume at most one live node per index; behavior with + /// duplicate indices is unsupported for those operations. + /// + /// References: + /// + /// + /// + /// + /// + /// /// Generic variable to store with each node. Typically used to store important data associated with the network that isn't required for the binary heap. public class BinaryHeap { @@ -47,11 +65,20 @@ public Node(float nodeWeight, int nodeIndex, T nodeValue) } } + /// + /// The heap slots in implicit binary-tree order. + /// private readonly Node[] _heap; + + /// + /// Maps a node index to its current slot in . + /// private readonly Dictionary _positionMap = new(); - private int _n = 0; // Number of nodes. - //private int _p = 0; // Parent Index + /// + /// The number of nodes currently in the heap. + /// + private int _n = 0; /// /// The number of nodes in the heap. @@ -68,10 +95,9 @@ public BinaryHeap(int heapSize) } /// - /// Putting the new inem in the first vacant cell in the array. - /// Then move it up in the heap based on its value compared to its parent. + /// Moves the node at the given slot up the heap until its parent is no heavier. /// - /// Index of the node. + /// The slot of the node to sift up. private void BubbleUp(int i) { while (i > 0) @@ -81,7 +107,6 @@ private void BubbleUp(int i) //Swap (_heap[i], _heap[parent]) = (_heap[parent], _heap[i]); - _positionMap[_heap[i].Index] = i; _positionMap[_heap[parent].Index] = parent; @@ -90,22 +115,22 @@ private void BubbleUp(int i) } /// - /// Used in heap deletion. Compares the parent nodes with child nodes in subtree. + /// Moves the node at the given slot down the heap until no child is lighter. /// - /// + /// The slot of the node to sift down. private void BubbleDown(int i) { while (true) { int left = 2 * i + 1; int right = 2 * i + 2; - int smallest = i; + int smallest = i; - if (left <_n && _heap[left].Weight < _heap[smallest].Weight) + if (left < _n && _heap[left].Weight < _heap[smallest].Weight) smallest = left; if (right < _n && _heap[right].Weight < _heap[smallest].Weight) smallest = right; - if (smallest == i) break; + if (smallest == i) break; (_heap[i], _heap[smallest]) = (_heap[smallest], _heap[i]); _positionMap[_heap[i].Index] = i; @@ -116,12 +141,13 @@ private void BubbleDown(int i) } /// - /// Updates the distance (priority) of a node if a shorter path is found. + /// Updates the weight (priority) of the node with the same index if the new weight is + /// smaller; adds the node when its index is not in the heap. Larger weights are ignored. /// - /// + /// The node carrying the index to address and the candidate weight. public void DecreaseKey(Node newNode) { - if(!_positionMap.TryGetValue(newNode.Index, out int position)) + if (!_positionMap.TryGetValue(newNode.Index, out int position)) { Add(newNode); return; @@ -135,13 +161,15 @@ public void DecreaseKey(Node newNode) /// /// Add a node to the heap. /// + /// The node to add. + /// Thrown when the heap is at capacity. public void Add(Node node) { - if (_n >= _heap.Length) + if (_n >= _heap.Length) throw new InvalidOperationException("Heap is full."); _heap[_n] = node; - _positionMap[node.Index] = _n; // Map the index to the position in the heap array (for Replace method) + _positionMap[node.Index] = _n; // Map the index to the position in the heap array for the keyed operations. BubbleUp(_n); _n++; } @@ -149,10 +177,11 @@ public void Add(Node node) /// /// Remove the minimum (top) node from the heap. /// - /// + /// The node with the smallest weight. + /// Thrown when the heap is empty. public Node RemoveMin() { - if (_n == 0) + if (_n == 0) throw new InvalidOperationException("Heap is empty."); Node min = _heap[0]; @@ -170,22 +199,25 @@ public Node RemoveMin() } /// - /// Replace a node that has the same index value as the new node. + /// Replaces the node that has the same index value as the new node, restoring heap order + /// in either direction. Does nothing when the index is not in the heap. /// - /// + /// The node carrying the index to address and the replacement weight and value. public void Replace(Node newNode) { - for (int i = 0; i < _n; i++) + if (!_positionMap.TryGetValue(newNode.Index, out int position)) return; + + float previousWeight = _heap[position].Weight; + _heap[position] = newNode; + if (newNode.Weight < previousWeight) { - if (_heap[i].Index == newNode.Index) - { - _heap[i] = newNode; - BubbleUp(i); - break; - } + BubbleUp(position); + } + else + { + BubbleDown(position); } } - } -} \ No newline at end of file +} diff --git a/Test_Numerics/Mathematics/Optimization/Dynamic/Test_BinaryHeap.cs b/Test_Numerics/Mathematics/Optimization/Dynamic/Test_BinaryHeap.cs index 066a6d40..7082a825 100644 --- a/Test_Numerics/Mathematics/Optimization/Dynamic/Test_BinaryHeap.cs +++ b/Test_Numerics/Mathematics/Optimization/Dynamic/Test_BinaryHeap.cs @@ -197,6 +197,128 @@ public void OrderingWithNegativeWeightsTest() Assert.AreEqual("C", heap.RemoveMin().Value); } + /// + /// Replacing the minimum node with a heavier weight must sift it down, not leave it at + /// the root: the drain returns every node in weight order. + /// + [TestMethod] + public void ReplaceWithHigherWeightReordersHeap() + { + var heap = new BinaryHeap(10); + heap.Add(new BinaryHeap.Node(1f, 1, "A")); + heap.Add(new BinaryHeap.Node(2f, 2, "B")); + heap.Add(new BinaryHeap.Node(3f, 3, "C")); + heap.Add(new BinaryHeap.Node(4f, 4, "D")); + + heap.Replace(new BinaryHeap.Node(10f, 1, "A")); // the minimum becomes the maximum + + Assert.AreEqual("B", heap.RemoveMin().Value); + Assert.AreEqual("C", heap.RemoveMin().Value); + Assert.AreEqual("D", heap.RemoveMin().Value); + var last = heap.RemoveMin(); + Assert.AreEqual("A", last.Value); + Assert.AreEqual(10f, last.Weight); + } + + /// + /// Replacing an index that is not in the heap is a silent no-op. + /// + [TestMethod] + public void ReplaceUnknownIndexIsNoOp() + { + var heap = new BinaryHeap(5); + heap.Add(new BinaryHeap.Node(5f, 1, 1)); + + heap.Replace(new BinaryHeap.Node(1f, 99, 99)); + + Assert.AreEqual(1, heap.Count); + var node = heap.RemoveMin(); + Assert.AreEqual(1, node.Index); + Assert.AreEqual(5f, node.Weight); + } + + /// + /// Decreasing the key of an index that is not in the heap adds the node. + /// + [TestMethod] + public void DecreaseKeyUnknownIndexAdds() + { + var heap = new BinaryHeap(5); + heap.Add(new BinaryHeap.Node(5f, 1, 1)); + + heap.DecreaseKey(new BinaryHeap.Node(2f, 7, 7)); + + Assert.AreEqual(2, heap.Count); + Assert.AreEqual(7, heap.RemoveMin().Index); + } + + /// + /// Fixed-seed fuzz of Add, RemoveMin, DecreaseKey, and Replace (including weight + /// increases and unknown indices) against a linear-scan reference model, with a full + /// ordered drain at the end. + /// + [TestMethod] + public void HeapFuzzMatchesReferenceModel() + { + var randy = new MersenneTwister(12345); + var heap = new BinaryHeap(64); + var model = new Dictionary(); + + for (int op = 0; op < 2000; op++) + { + int index = randy.Next(0, 64); + double action = randy.NextDouble(); + float weight = (float)randy.NextDouble(); + + if (action < 0.4) + { + if (!model.ContainsKey(index)) + { + heap.Add(new BinaryHeap.Node(weight, index, index)); + model[index] = weight; + } + } + else if (action < 0.6) + { + if (model.Count == 0) continue; + var popped = heap.RemoveMin(); + float modelMin = float.MaxValue; + foreach (var entry in model) + { + if (entry.Value < modelMin) modelMin = entry.Value; + } + Assert.AreEqual(modelMin, popped.Weight, 0f); + Assert.AreEqual(modelMin, model[popped.Index], 0f); + model.Remove(popped.Index); + } + else if (action < 0.8) + { + heap.DecreaseKey(new BinaryHeap.Node(weight, index, index)); + if (!model.ContainsKey(index) || weight < model[index]) model[index] = weight; + } + else + { + heap.Replace(new BinaryHeap.Node(weight, index, index)); + if (model.ContainsKey(index)) model[index] = weight; + } + + Assert.AreEqual(model.Count, heap.Count); + } + + while (model.Count > 0) + { + var popped = heap.RemoveMin(); + float modelMin = float.MaxValue; + foreach (var entry in model) + { + if (entry.Value < modelMin) modelMin = entry.Value; + } + Assert.AreEqual(modelMin, popped.Weight, 0f); + Assert.AreEqual(modelMin, model[popped.Index], 0f); + model.Remove(popped.Index); + } + Assert.AreEqual(0, heap.Count); + } } } From 4a880ebc59c3e11d1bfb2ed3674ae937a09f8477 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 3 Aug 2026 15:11:57 -0600 Subject: [PATCH 044/222] Harden Dijkstra.Solve with validation, exact heap sizing, and compact adjacency The solvers now run over an internal compressed sparse row view built with a stable counting sort, so relaxation order - and every tie outcome - matches the previous per-node list traversal exactly, and an internal indexed min-heap with a flat position array replaces the dictionary-mapped generic heap in the hot loop. The heap is sized at the node count, which the solver state machine makes an exact bound: the previous fixed 10,000-slot heap threw for larger networks and carried 240 KB for small ones. A 316x316 unit grid (99,856 nodes, which previously overflowed) solves in 0.042 s single-destination and 0.084 s for four destinations on the development machine. Malformed inputs now fail with clear argument exceptions instead of raw index or LINQ crashes; negative, NaN, and infinite weights still pass through untouched, with the non-negative precondition and the infinite-weight impassable convention documented. The multi-destination overload reuses one scratch set across destinations; running the per-destination passes in parallel and merging in destination order would preserve results exactly and remains a recorded option. --- .../Optimization/Dynamic/CompactAdjacency.cs | 180 ++++++++ .../Optimization/Dynamic/Dijkstra.cs | 342 ++++++++++----- .../Optimization/Dynamic/IndexedMinHeap.cs | 176 ++++++++ .../Dynamic/Test_IndexedMinHeap.cs | 127 ++++++ .../Optimization/Dynamic/Test_ShortestPath.cs | 406 ++++++++++++++++++ 5 files changed, 1134 insertions(+), 97 deletions(-) create mode 100644 Numerics/Mathematics/Optimization/Dynamic/CompactAdjacency.cs create mode 100644 Numerics/Mathematics/Optimization/Dynamic/IndexedMinHeap.cs create mode 100644 Test_Numerics/Mathematics/Optimization/Dynamic/Test_IndexedMinHeap.cs diff --git a/Numerics/Mathematics/Optimization/Dynamic/CompactAdjacency.cs b/Numerics/Mathematics/Optimization/Dynamic/CompactAdjacency.cs new file mode 100644 index 00000000..5319a58e --- /dev/null +++ b/Numerics/Mathematics/Optimization/Dynamic/CompactAdjacency.cs @@ -0,0 +1,180 @@ +using System; +using System.Collections.Generic; + +namespace Numerics.Mathematics.Optimization +{ + /// + /// A compressed sparse row (CSR) view of a network's edges, grouped by one endpoint, used by + /// the shortest-path solvers in place of per-node edge lists: one contiguous slot range per + /// node, with the slot's endpoints, weight, edge index, and original edge position held in + /// parallel arrays. + /// + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + /// Description: + /// The build is a stable two-pass counting sort, so each node's slots appear in the edges' + /// original sequence order — exactly the order per-node List<Edge> grouping + /// produces — which keeps relaxation order, and therefore every tie outcome, identical to + /// the list-based traversal. maps a slot back to its edge's + /// position in the original sequence so a caller can overlay per-solve weights positionally + /// without rebuilding; it is null when the view was built from caller-supplied per-node + /// lists, which carry no positional information. + /// + /// + internal readonly struct CompactAdjacency + { + /// The number of nodes in the network. + internal readonly int NodeCount; + + /// The slot range per node: node n owns slots [RowStart[n], RowStart[n + 1]). + internal readonly int[] RowStart; + + /// The slot's edge start node (). + internal readonly int[] FromNode; + + /// The slot's edge end node (). + internal readonly int[] ToNode; + + /// The slot's edge weight. + internal readonly float[] Weight; + + /// The slot's edge index (). + internal readonly int[] EdgeIndex; + + /// + /// The slot's edge position in the original edge sequence, for positional weight + /// overlays; null when the view was built from caller-supplied per-node lists. + /// + internal readonly int[]? SourcePosition; + + /// + /// Creates the view over the given arrays. + /// + /// The number of nodes. + /// The per-node slot ranges. + /// The per-slot edge start nodes. + /// The per-slot edge end nodes. + /// The per-slot edge weights. + /// The per-slot edge indices. + /// The per-slot original edge positions, or null when unavailable. + private CompactAdjacency(int nodeCount, int[] rowStart, int[] fromNode, int[] toNode, + float[] weight, int[] edgeIndex, int[]? sourcePosition) + { + NodeCount = nodeCount; + RowStart = rowStart; + FromNode = fromNode; + ToNode = toNode; + Weight = weight; + EdgeIndex = edgeIndex; + SourcePosition = sourcePosition; + } + + /// + /// Builds the view from an edge sequence, grouping by the end node (incoming edges, the + /// orientation the destination-rooted solver walks) or by the start node (outgoing + /// edges, the orientation forward routing walks). + /// + /// The edges that make up the network. + /// The number of nodes; every referenced node index must lie in [0, nodeCount). + /// True to group slots by ; false to group by . + /// The caller's parameter name for exception reporting. + /// The compact view. + /// Thrown when an edge references a node index outside [0, nodeCount). + internal static CompactAdjacency FromEdges(IList edges, int nodeCount, bool groupByEndNode, string parameterName) + { + int edgeCount = edges.Count; + var rowStart = new int[nodeCount + 1]; + + for (int i = 0; i < edgeCount; i++) + { + Edge edge = edges[i]; + if (edge.FromIndex < 0 || edge.FromIndex >= nodeCount || edge.ToIndex < 0 || edge.ToIndex >= nodeCount) + { + throw new ArgumentException($"The edge at position {i} references node index " + + $"{(edge.FromIndex < 0 || edge.FromIndex >= nodeCount ? edge.FromIndex : edge.ToIndex)}, " + + $"outside the node range [0, {nodeCount}).", parameterName); + } + rowStart[(groupByEndNode ? edge.ToIndex : edge.FromIndex) + 1]++; + } + for (int n = 0; n < nodeCount; n++) rowStart[n + 1] += rowStart[n]; + + var cursor = new int[nodeCount]; + var fromNode = new int[edgeCount]; + var toNode = new int[edgeCount]; + var weight = new float[edgeCount]; + var edgeIndex = new int[edgeCount]; + var sourcePosition = new int[edgeCount]; + + for (int i = 0; i < edgeCount; i++) + { + Edge edge = edges[i]; + int bucket = groupByEndNode ? edge.ToIndex : edge.FromIndex; + int slot = rowStart[bucket] + cursor[bucket]; + cursor[bucket]++; + fromNode[slot] = edge.FromIndex; + toNode[slot] = edge.ToIndex; + weight[slot] = edge.Weight; + edgeIndex[slot] = edge.Index; + sourcePosition[slot] = i; + } + + return new CompactAdjacency(nodeCount, rowStart, fromNode, toNode, weight, edgeIndex, sourcePosition); + } + + /// + /// Builds the view from caller-supplied per-node incoming-edge lists, preserving each + /// list's order. The lists are authoritative when supplied (the documented solver + /// precedence), so their contents are read as-is; node indices carried by the listed + /// edges are still range-checked so a malformed list fails loudly. + /// + /// The incoming edges for each node; a null entry means the node has none. + /// The number of nodes; the array length must equal it. + /// The caller's parameter name for exception reporting. + /// The compact view, without source positions. + /// Thrown when a listed edge references a node index outside [0, nodeCount). + internal static CompactAdjacency FromIncomingLists(List[] edgesToNodes, int nodeCount, string parameterName) + { + var rowStart = new int[nodeCount + 1]; + int edgeCount = 0; + for (int n = 0; n < nodeCount; n++) + { + int count = edgesToNodes[n]?.Count ?? 0; + rowStart[n + 1] = rowStart[n] + count; + edgeCount += count; + } + + var fromNode = new int[edgeCount]; + var toNode = new int[edgeCount]; + var weight = new float[edgeCount]; + var edgeIndex = new int[edgeCount]; + + for (int n = 0; n < nodeCount; n++) + { + List? list = edgesToNodes[n]; + if (list == null) continue; + int slot = rowStart[n]; + for (int j = 0; j < list.Count; j++) + { + Edge edge = list[j]; + if (edge.FromIndex < 0 || edge.FromIndex >= nodeCount || edge.ToIndex < 0 || edge.ToIndex >= nodeCount) + { + throw new ArgumentException($"The incoming-edge list for node {n} contains an edge referencing node index " + + $"{(edge.FromIndex < 0 || edge.FromIndex >= nodeCount ? edge.FromIndex : edge.ToIndex)}, " + + $"outside the node range [0, {nodeCount}).", parameterName); + } + fromNode[slot] = edge.FromIndex; + toNode[slot] = edge.ToIndex; + weight[slot] = edge.Weight; + edgeIndex[slot] = edge.Index; + slot++; + } + } + + return new CompactAdjacency(nodeCount, rowStart, fromNode, toNode, weight, edgeIndex, null); + } + } +} diff --git a/Numerics/Mathematics/Optimization/Dynamic/Dijkstra.cs b/Numerics/Mathematics/Optimization/Dynamic/Dijkstra.cs index fb146a71..a4271dc6 100644 --- a/Numerics/Mathematics/Optimization/Dynamic/Dijkstra.cs +++ b/Numerics/Mathematics/Optimization/Dynamic/Dijkstra.cs @@ -1,8 +1,5 @@ -using System; +using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Numerics.Mathematics.Optimization { @@ -27,7 +24,7 @@ public struct Edge(int fromNodeIndex, int toNodeIndex, float edgeWeight, int edg /// public int ToIndex = toNodeIndex; /// - /// Weight (or Cost) of transversing the edge. + /// Weight (or Cost) of transversing the edge. /// public float Weight = edgeWeight; /// @@ -39,73 +36,125 @@ public struct Edge(int fromNodeIndex, int toNodeIndex, float edgeWeight, int edg /// /// Dijkstra dynamic programming implementation for shortest path optimization. /// + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + /// Description: + /// The solvers run Dijkstra's algorithm rooted at the destination, following edges backward, + /// and return a routing table for every node: column 0 is the next node toward the + /// destination, column 1 is the index of the edge to take, and column 2 is the cumulative + /// cost to the destination. The destination row is (itself, -1, 0); an unreachable node's + /// row is (-1, -1, positive infinity). Dijkstra's algorithm assumes non-negative edge + /// weights; negative weights are not rejected, but routes computed from them are undefined. + /// A weight of positive infinity makes an edge impassable, and a NaN weight never relaxes, + /// severing its edge. + /// + /// References: + /// + /// + /// Dijkstra, E. W. (1959). A note on two problems in connexion with graphs. Numerische Mathematik, 1, 269-271. + /// + /// + /// + /// + /// + /// public static class Dijkstra { + /// The result-table column holding the next node toward the destination. private const int NEXT_NODE = 0; + + /// The result-table column holding the index of the edge to take. private const int EDGE_INDEX = 1; + + /// The result-table column holding the cumulative cost to the destination. private const int COST = 2; /// - /// May be a useful call in LifeSim -> GetPath(). - /// Follows the logic that is implemented in the Solve method. + /// Reports whether the result table records a finite-cost route from the specified node + /// to the solved destination. /// - /// - /// - /// + /// A result table produced by one of the solvers. + /// The node to query. + /// True when the node can reach the destination; false when its cost is positive infinity. + /// Thrown when the result table is null. + /// Thrown when the result table does not have three columns. + /// Thrown when the node index is outside the table. public static bool PathExists(float[,] resultTable, int nodeIndex) { + if (resultTable == null) throw new ArgumentNullException(nameof(resultTable)); + if (resultTable.GetLength(1) != 3) throw new ArgumentException("The result table must have three columns.", nameof(resultTable)); + if (nodeIndex < 0 || nodeIndex >= resultTable.GetLength(0)) throw new ArgumentOutOfRangeException(nameof(nodeIndex), $"The node index must be within [0, {resultTable.GetLength(0)})."); return !float.IsPositiveInfinity(resultTable[nodeIndex, COST]); } + /// - /// Solves the shortest path from every node in the network of edges to a given destination. + /// Solves the shortest path from every node in the network of edges to the nearest of + /// the given destinations. /// /// Edges, or segments, that make up the network. /// Indices of the destination nodes. /// Optional number of nodes in the network. If not provided it will be calculated internally. - /// Optional list of incoming edges from each node in the network. If not provided or mismatched with edges it will be calculated internally. + /// Optional list of incoming edges for each node in the network. If not provided or mismatched with the node count it will be calculated internally. /// Lookup table of shortest paths from any given node. + /// + /// Each destination is solved independently and the tables merge per node by strictly + /// smaller cost in destination order, so on an exact cost tie the earlier destination in + /// the array wins. An empty destination array returns an all-unreachable table. + /// + /// Thrown when the edges or destination indices are null. + /// Thrown when the node count cannot be derived, or an edge references a node outside the network. + /// Thrown when the node count is not positive, or a destination index is outside the network. public static float[,] Solve(IList edges, int[] destinationIndices, int nodeCount = -1, List[]? edgesFromNodes = null) { - // Set optional parameters if required. - int nNodes = (nodeCount == -1) ? (edges.Max(o => Math.Max(o.FromIndex,o.ToIndex)) + 1) : nodeCount; + if (edges == null) throw new ArgumentNullException(nameof(edges)); + if (destinationIndices == null) throw new ArgumentNullException(nameof(destinationIndices)); - if (edgesFromNodes == null || edgesFromNodes.Length != nNodes) + int nNodes = ResolveNodeCount(edges, nodeCount); + for (int i = 0; i < destinationIndices.Length; i++) { - edgesFromNodes = new List[nNodes]; - // - foreach(var edge in edges) - { - edgesFromNodes[edge.ToIndex] ??= new List(); - edgesFromNodes[edge.ToIndex].Add(edge); - } + if (destinationIndices[i] < 0 || destinationIndices[i] >= nNodes) + throw new ArgumentOutOfRangeException(nameof(destinationIndices), $"The destination index {destinationIndices[i]} must be within [0, {nNodes})."); } + CompactAdjacency adjacency = BuildIncomingAdjacency(edges, nNodes, edgesFromNodes); - float[,] resultTable = new float[nNodes, 3]; - for(int i = 0; i < nNodes; i++) + // Accumulators start all-unreachable; each destination's solve merges in by + // strictly smaller cost, preserving the earlier-destination-wins tie rule. + var bestNext = new int[nNodes]; + var bestEdge = new int[nNodes]; + var bestDist = new float[nNodes]; + for (int i = 0; i < nNodes; i++) { - resultTable[i, NEXT_NODE] = -1; - resultTable[i, EDGE_INDEX] = -1; - resultTable[i, COST] = float.PositiveInfinity; + bestNext[i] = -1; + bestEdge[i] = -1; + bestDist[i] = float.PositiveInfinity; } - for (int i = 0; i < destinationIndices.Length; i++) + // One scratch set reused across destinations. + var next = new int[nNodes]; + var edgeIndexes = new int[nNodes]; + var dist = new float[nNodes]; + var state = new int[nNodes]; + var heap = new IndexedMinHeap(nNodes); + + for (int d = 0; d < destinationIndices.Length; d++) { - int destinationIndex = destinationIndices[i]; - var partialResult = Solve(edges, destinationIndex, nNodes, edgesFromNodes); - for(int j = 0; j < nNodes; j++) + SolveCore(adjacency, null, destinationIndices[d], next, edgeIndexes, dist, state, heap); + for (int j = 0; j < nNodes; j++) { - // Keep better path - if (partialResult[j, COST] < resultTable[j, COST]) + if (dist[j] < bestDist[j]) { - resultTable[j, NEXT_NODE] = partialResult[j, NEXT_NODE]; - resultTable[j, EDGE_INDEX] = partialResult[j, EDGE_INDEX]; - resultTable[j,COST] = partialResult[j,COST]; + bestNext[j] = next[j]; + bestEdge[j] = edgeIndexes[j]; + bestDist[j] = dist[j]; } } } - return resultTable; - + return WriteTable(bestNext, bestEdge, bestDist); } /// @@ -114,93 +163,192 @@ public static bool PathExists(float[,] resultTable, int nodeIndex) /// Edges, or segments, that make up the network. /// Index of the destination node. /// Optional number of nodes in the network. If not provided it will be calculated internally. - /// Optional list of incoming edges from each node in the network. If not provided or mismatched with edges it will be calculated internally. + /// Optional list of incoming edges for each node in the network. If not provided or mismatched with the node count it will be calculated internally. /// Lookup table of shortest paths from any given node. + /// Thrown when the edges are null. + /// Thrown when the node count cannot be derived, or an edge references a node outside the network. + /// Thrown when the node count is not positive, or the destination index is outside the network. public static float[,] Solve(IList edges, int destinationIndex, int nodeCount = -1, List[]? edgesToNodes = null) { - // Set optional parameters if required. - int nNodes = (nodeCount == -1) ? (edges.Max(o => Math.Max(o.FromIndex, o.ToIndex)) + 1) : nodeCount; + if (edges == null) throw new ArgumentNullException(nameof(edges)); - if (edgesToNodes == null || edgesToNodes.Length != nNodes) - { - edgesToNodes = new List[nNodes]; - // - foreach (var edge in edges) - { - edgesToNodes[edge.ToIndex] ??= new List(); - edgesToNodes[edge.ToIndex].Add(edge); - } - } + int nNodes = ResolveNodeCount(edges, nodeCount); + if (destinationIndex < 0 || destinationIndex >= nNodes) + throw new ArgumentOutOfRangeException(nameof(destinationIndex), $"The destination index must be within [0, {nNodes})."); + + CompactAdjacency adjacency = BuildIncomingAdjacency(edges, nNodes, edgesToNodes); - // Prepare results table with destination defined. - float[,] resultTable = new float[nNodes, 3]; - int[] nodeState = new int[nNodes]; //0 - Node hasn't been scanned yet, 1 - Node has been solved for, 2 - Node has been scanned into heap but not solved for. - float[] nodeWeightToDestination = new float[nNodes]; + var next = new int[nNodes]; + var edgeIndexes = new int[nNodes]; + var dist = new float[nNodes]; + var state = new int[nNodes]; + var heap = new IndexedMinHeap(nNodes); - //Initialize all nodes are unreachable + SolveCore(adjacency, null, destinationIndex, next, edgeIndexes, dist, state, heap); + return WriteTable(next, edgeIndexes, dist); + } + + /// + /// Runs the destination-rooted solve over the compact adjacency, filling the caller's + /// flat buffers: for each node, the next node toward the destination, the edge index to + /// take, and the cumulative cost. Buffers are fully re-initialized, so they can be + /// reused across calls. + /// + /// The incoming-edge compact adjacency. + /// Optional positional weight overlay (indexed by original edge position); null uses the adjacency weights. Requires an adjacency built from an edge sequence. + /// The destination node. + /// Receives the next node toward the destination per node; -1 when unreachable. + /// Receives the edge index to take per node; -1 when unreachable. + /// Receives the cumulative cost per node; positive infinity when unreachable. + /// Scratch node states (0 unscanned, 1 solved, 2 in the heap). + /// The scratch heap, sized at the node count. + /// + /// The heap holds exactly the nodes with state 2 and never a duplicate: nodes enter only + /// through Add when their state is not 2, in-heap improvements route through + /// DecreaseKey, and RemoveMin retires the node to state 1. The heap count therefore + /// never exceeds the node count, independent of weight signs, which is what makes the + /// node-count capacity exact. + /// + internal static void SolveCore(in CompactAdjacency adjacency, float[]? weightOverride, int destinationIndex, + int[] next, int[] edgeIndexes, float[] dist, int[] state, IndexedMinHeap heap) + { + int nNodes = adjacency.NodeCount; for (int i = 0; i < nNodes; i++) { - resultTable[i, NEXT_NODE] = -1; - resultTable[i, EDGE_INDEX] = -1; - resultTable[i, COST] = float.PositiveInfinity; - nodeWeightToDestination[i] = float.PositiveInfinity; + next[i] = -1; + edgeIndexes[i] = -1; + dist[i] = float.PositiveInfinity; + state[i] = 0; } + heap.Clear(); + + next[destinationIndex] = destinationIndex; + edgeIndexes[destinationIndex] = -1; + dist[destinationIndex] = 0f; + heap.Add(destinationIndex, 0f); + state[destinationIndex] = 2; - BinaryHeap heap = new BinaryHeap(10000); + RunToExhaustion(adjacency, weightOverride, next, edgeIndexes, dist, state, heap); + } - resultTable[destinationIndex, NEXT_NODE] = destinationIndex; //Tail - resultTable[destinationIndex, EDGE_INDEX] = -1; //edge index - resultTable[destinationIndex, COST] = 0; //Cumulative Weight - nodeWeightToDestination[destinationIndex] = 0; - heap.Add(new BinaryHeap.Node(0, destinationIndex, new Edge(destinationIndex, destinationIndex, 0, -1))); - nodeState[destinationIndex] = 2; + /// + /// Drains the heap, relaxing each settled node's incoming edges — the shared main loop + /// of the destination-rooted solvers. Seeding is the caller's responsibility. + /// + /// The incoming-edge compact adjacency. + /// Optional positional weight overlay; null uses the adjacency weights. + /// The per-node next-node buffer. + /// The per-node edge-index buffer. + /// The per-node cumulative-cost buffer. + /// The per-node state buffer. + /// The seeded heap. + internal static void RunToExhaustion(in CompactAdjacency adjacency, float[]? weightOverride, + int[] next, int[] edgeIndexes, float[] dist, int[] state, IndexedMinHeap heap) + { + int[] rowStart = adjacency.RowStart; + int[] fromNodes = adjacency.FromNode; + int[] toNodes = adjacency.ToNode; + float[] weights = adjacency.Weight; + int[] slotEdgeIndexes = adjacency.EdgeIndex; + int[]? sourcePositions = adjacency.SourcePosition; - while (heap.Count > 0) { - var node = heap.RemoveMin(); - int current = node.Index; - float cost = node.Weight; - - if (nodeState[current] == 1) - continue; + heap.RemoveMin(out int current, out float cost); - nodeState[current] = 1; + // Defensive only: the heap never holds a settled node (see SolveCore remarks). + if (state[current] == 1) continue; + state[current] = 1; - if (edgesToNodes[current] == null) - continue; - - foreach (var edge in edgesToNodes[current]) + int rowEnd = rowStart[current + 1]; + for (int k = rowStart[current]; k < rowEnd; k++) { - int from = edge.FromIndex; - int to = edge.ToIndex; - float newCost = cost + edge.Weight; + int from = fromNodes[k]; + float weight = weightOverride == null ? weights[k] : weightOverride[sourcePositions![k]]; + float newCost = cost + weight; - if (newCost < nodeWeightToDestination[from]) + if (newCost < dist[from]) { - nodeWeightToDestination[from] = newCost; - var newNode = new BinaryHeap.Node(newCost, from, edge); - - if (nodeState[from] != 2) + dist[from] = newCost; + if (state[from] != 2) { - heap.Add(newNode); - nodeState[from] = 2; + heap.Add(from, newCost); + state[from] = 2; } else { - heap.DecreaseKey(newNode); + heap.DecreaseKey(from, newCost); } - - resultTable[from, NEXT_NODE] = to; - resultTable[from, EDGE_INDEX] = edge.Index; - resultTable[from, COST] = newCost; - + next[from] = toNodes[k]; + edgeIndexes[from] = slotEdgeIndexes[k]; } } } - return resultTable; + } + + /// + /// Resolves the working node count: -1 derives it from the largest node index the edges + /// reference; any other value must be positive and is used as given. + /// + /// The network edges. + /// The caller's node count, or -1 to derive. + /// The node count. + /// Thrown when the count must be derived from an empty edge list. + /// Thrown when an explicit count is not positive. + private static int ResolveNodeCount(IList edges, int nodeCount) + { + if (nodeCount == -1) + { + if (edges.Count == 0) + throw new ArgumentException("The node count cannot be derived from an empty edge list; provide nodeCount explicitly.", nameof(edges)); + int max = 0; + for (int i = 0; i < edges.Count; i++) + { + Edge edge = edges[i]; + if (edge.FromIndex > max) max = edge.FromIndex; + if (edge.ToIndex > max) max = edge.ToIndex; + } + return max + 1; } + if (nodeCount < 1) + throw new ArgumentOutOfRangeException(nameof(nodeCount), "The node count must be positive, or -1 to derive it from the edges."); + return nodeCount; + } - + /// + /// Builds the incoming-edge compact adjacency, honoring the documented precedence: a + /// caller-supplied per-node list array whose length matches the node count is + /// authoritative; otherwise the adjacency is calculated from the edges. + /// + /// The network edges. + /// The working node count. + /// The caller's per-node incoming-edge lists, or null. + /// The compact adjacency. + private static CompactAdjacency BuildIncomingAdjacency(IList edges, int nodeCount, List[]? providedLists) + { + if (providedLists != null && providedLists.Length == nodeCount) + return CompactAdjacency.FromIncomingLists(providedLists, nodeCount, nameof(edges)); + return CompactAdjacency.FromEdges(edges, nodeCount, groupByEndNode: true, nameof(edges)); + } + + /// + /// Writes the flat solve buffers into the public three-column result table. + /// + /// The per-node next-node buffer. + /// The per-node edge-index buffer. + /// The per-node cumulative-cost buffer. + /// The result table. + internal static float[,] WriteTable(int[] next, int[] edgeIndexes, float[] dist) + { + int nNodes = next.Length; + var resultTable = new float[nNodes, 3]; + for (int i = 0; i < nNodes; i++) + { + resultTable[i, NEXT_NODE] = next[i]; + resultTable[i, EDGE_INDEX] = edgeIndexes[i]; + resultTable[i, COST] = dist[i]; + } + return resultTable; + } } } diff --git a/Numerics/Mathematics/Optimization/Dynamic/IndexedMinHeap.cs b/Numerics/Mathematics/Optimization/Dynamic/IndexedMinHeap.cs new file mode 100644 index 00000000..4242879d --- /dev/null +++ b/Numerics/Mathematics/Optimization/Dynamic/IndexedMinHeap.cs @@ -0,0 +1,176 @@ +using System; + +namespace Numerics.Mathematics.Optimization +{ + /// + /// An array-backed indexed binary min-heap specialized for the shortest-path solvers: node + /// identifiers are dense integers in [0, capacity), so the index-to-slot map is a flat + /// array rather than a dictionary, and the heap slots store the weight and node identifier + /// in parallel arrays. + /// + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + /// Description: + /// The sift operations use hole shifting (one write per level instead of a swap) and stop on + /// equal weights, so the slot layout — and therefore the pop order among equal weights — + /// evolves exactly as it does for the swap-based public given + /// the same operation sequence. Capacity is fixed at construction; the shortest-path solvers + /// size it at the node count, which the solver state machine makes an exact bound (at most + /// one live entry per node). resets only the occupied slots, so a heap + /// can be reused across solves without reallocation. + /// + /// + internal sealed class IndexedMinHeap + { + /// The heap-ordered weights. + private readonly float[] _weights; + + /// The heap-ordered node identifiers. + private readonly int[] _nodes; + + /// Maps a node identifier to its heap slot; -1 when the node is not in the heap. + private readonly int[] _position; + + /// The number of nodes currently in the heap. + private int _count; + + /// + /// The number of nodes in the heap. + /// + internal int Count => _count; + + /// + /// Creates a new indexed min-heap for node identifiers in [0, capacity). + /// + /// The maximum number of nodes the heap can hold, and the exclusive upper bound on node identifiers. + internal IndexedMinHeap(int capacity) + { + _weights = new float[capacity]; + _nodes = new int[capacity]; + _position = new int[capacity]; + for (int i = 0; i < capacity; i++) _position[i] = -1; + } + + /// + /// Empties the heap, resetting only the occupied position entries. + /// + internal void Clear() + { + for (int i = 0; i < _count; i++) _position[_nodes[i]] = -1; + _count = 0; + } + + /// + /// Adds a node that is not currently in the heap. + /// + /// The node identifier. + /// The node's weight (priority). + /// Thrown when the heap is at capacity. + internal void Add(int node, float weight) + { + if (_count >= _nodes.Length) + throw new InvalidOperationException("Heap is full."); + + int slot = _count; + _count++; + SiftUp(slot, node, weight); + } + + /// + /// Removes the minimum-weight node from the heap. + /// + /// The removed node identifier. + /// The removed node's weight. + /// Thrown when the heap is empty. + internal void RemoveMin(out int node, out float weight) + { + if (_count == 0) + throw new InvalidOperationException("Heap is empty."); + + node = _nodes[0]; + weight = _weights[0]; + _position[node] = -1; + + _count--; + if (_count > 0) + { + SiftDown(0, _nodes[_count], _weights[_count]); + } + } + + /// + /// Lowers the weight of a node already in the heap; adds the node when it is absent. + /// Weights that are not smaller than the current weight are ignored. + /// + /// The node identifier. + /// The candidate weight. + internal void DecreaseKey(int node, float weight) + { + int slot = _position[node]; + if (slot < 0) + { + Add(node, weight); + return; + } + if (weight >= _weights[slot]) return; + + SiftUp(slot, node, weight); + } + + /// + /// Moves the given entry up from the given slot until its parent is no heavier, using + /// hole shifting, and writes the entry at its final slot. + /// + /// The starting slot (treated as a hole). + /// The entry's node identifier. + /// The entry's weight. + private void SiftUp(int slot, int node, float weight) + { + while (slot > 0) + { + int parent = (slot - 1) >> 1; + if (weight >= _weights[parent]) break; + + _weights[slot] = _weights[parent]; + _nodes[slot] = _nodes[parent]; + _position[_nodes[slot]] = slot; + slot = parent; + } + _weights[slot] = weight; + _nodes[slot] = node; + _position[node] = slot; + } + + /// + /// Moves the given entry down from the given slot until no child is lighter, using hole + /// shifting, and writes the entry at its final slot. Child ties prefer the left child, + /// matching the swap-based sift. + /// + /// The starting slot (treated as a hole). + /// The entry's node identifier. + /// The entry's weight. + private void SiftDown(int slot, int node, float weight) + { + while (true) + { + int left = 2 * slot + 1; + if (left >= _count) break; + int right = left + 1; + int smallest = (right < _count && _weights[right] < _weights[left]) ? right : left; + if (_weights[smallest] >= weight) break; + + _weights[slot] = _weights[smallest]; + _nodes[slot] = _nodes[smallest]; + _position[_nodes[slot]] = slot; + slot = smallest; + } + _weights[slot] = weight; + _nodes[slot] = node; + _position[node] = slot; + } + } +} diff --git a/Test_Numerics/Mathematics/Optimization/Dynamic/Test_IndexedMinHeap.cs b/Test_Numerics/Mathematics/Optimization/Dynamic/Test_IndexedMinHeap.cs new file mode 100644 index 00000000..c4e0673c --- /dev/null +++ b/Test_Numerics/Mathematics/Optimization/Dynamic/Test_IndexedMinHeap.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Mathematics.Optimization; +using Numerics.Sampling; + +namespace Mathematics.Optimization +{ + /// + /// Tests the internal indexed min-heap that backs the shortest-path solvers. + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + [TestClass] + public class Test_IndexedMinHeap + { + /// + /// Fixed-seed fuzz of Add, RemoveMin, and DecreaseKey against a linear-scan reference + /// model, across three Clear-and-reuse rounds with a full ordered drain per round. + /// + [TestMethod] + public void IndexedHeapFuzzMatchesReferenceModel() + { + var randy = new MersenneTwister(24680); + var heap = new IndexedMinHeap(48); + + for (int round = 0; round < 3; round++) + { + heap.Clear(); + var model = new Dictionary(); + + for (int op = 0; op < 1200; op++) + { + int node = randy.Next(0, 48); + double action = randy.NextDouble(); + float weight = (float)randy.NextDouble(); + + if (action < 0.45) + { + if (!model.ContainsKey(node)) + { + heap.Add(node, weight); + model[node] = weight; + } + } + else if (action < 0.65) + { + if (model.Count == 0) continue; + heap.RemoveMin(out int popped, out float poppedWeight); + float modelMin = float.MaxValue; + foreach (var entry in model) + { + if (entry.Value < modelMin) modelMin = entry.Value; + } + Assert.AreEqual(modelMin, poppedWeight, 0f); + Assert.AreEqual(modelMin, model[popped], 0f); + model.Remove(popped); + } + else + { + heap.DecreaseKey(node, weight); + if (!model.ContainsKey(node) || weight < model[node]) model[node] = weight; + } + + Assert.AreEqual(model.Count, heap.Count); + } + + while (model.Count > 0) + { + heap.RemoveMin(out int popped, out float poppedWeight); + float modelMin = float.MaxValue; + foreach (var entry in model) + { + if (entry.Value < modelMin) modelMin = entry.Value; + } + Assert.AreEqual(modelMin, poppedWeight, 0f); + Assert.AreEqual(modelMin, model[popped], 0f); + model.Remove(popped); + } + Assert.AreEqual(0, heap.Count); + } + } + + /// + /// The heap fills to exactly its capacity and drains fully sorted; the count never + /// exceeds the capacity under solver-shaped operation sequences. + /// + [TestMethod] + public void IndexedHeapCapacityInvariant() + { + const int capacity = 64; + var randy = new MersenneTwister(13579); + var heap = new IndexedMinHeap(capacity); + + // Exact fill: every node enters once. + var weights = new float[capacity]; + for (int node = 0; node < capacity; node++) + { + weights[node] = (float)randy.NextDouble(); + heap.Add(node, weights[node]); + Assert.IsTrue(heap.Count <= capacity); + } + Assert.AreEqual(capacity, heap.Count); + Assert.Throws(() => heap.Add(0, 0f)); + + // Decrease keys while full never grows the heap. + for (int node = 0; node < capacity; node += 3) + { + heap.DecreaseKey(node, weights[node] / 2f); + Assert.AreEqual(capacity, heap.Count); + } + + // Full drain returns ascending weights. + float previous = float.NegativeInfinity; + for (int i = 0; i < capacity; i++) + { + heap.RemoveMin(out _, out float weight); + Assert.IsTrue(weight >= previous, "The drain must be non-decreasing."); + previous = weight; + } + Assert.AreEqual(0, heap.Count); + Assert.Throws(() => heap.RemoveMin(out _, out _)); + } + } +} diff --git a/Test_Numerics/Mathematics/Optimization/Dynamic/Test_ShortestPath.cs b/Test_Numerics/Mathematics/Optimization/Dynamic/Test_ShortestPath.cs index 74bf701f..544e7783 100644 --- a/Test_Numerics/Mathematics/Optimization/Dynamic/Test_ShortestPath.cs +++ b/Test_Numerics/Mathematics/Optimization/Dynamic/Test_ShortestPath.cs @@ -1,5 +1,7 @@ +using System.Diagnostics; using Numerics.Mathematics.Optimization; +using Numerics.Sampling; namespace Mathematics.Optimization { @@ -283,5 +285,409 @@ public void TrianglePath() Assert.AreEqual(1, result[1, 2]); Assert.AreEqual(2, result[2, 0]); } + + /// + /// Pins the edge-index column: every routed node records the index of the edge it takes, + /// the destination and unreachable nodes record -1. + /// + [TestMethod] + public void ResultTableEdgeIndexColumn() + { + var edges = new List + { + new Edge(0, 1, 2, 0), + new Edge(0, 2, 4, 2), + new Edge(1, 2, 1, 2), + new Edge(1, 3, 7, 3), + new Edge(2, 3, 3, 4), + new Edge(4, 0, 1, 5), + }; + + var result = Dijkstra.Solve(edges, 3, 6); + + Assert.AreEqual(0f, result[0, 1]); // 0 departs on edge 0 toward node 1 + Assert.AreEqual(2f, result[1, 1]); // 1 departs on edge 2 toward node 2 + Assert.AreEqual(4f, result[2, 1]); // 2 departs on edge 4 toward node 3 + Assert.AreEqual(-1f, result[3, 1]); // the destination takes no edge + Assert.AreEqual(5f, result[4, 1]); // 4 departs on edge 5 toward node 0 + Assert.AreEqual(-1f, result[5, 1]); // unreachable + Assert.AreEqual(1f, result[0, 0]); + Assert.AreEqual(2f, result[1, 0]); + Assert.AreEqual(3f, result[2, 0]); + Assert.AreEqual(0f, result[4, 0]); + } + + /// + /// The destination row is exactly (itself, -1, 0) for single- and multi-destination solves. + /// + [TestMethod] + public void DestinationRowContract() + { + var edges = new List { new Edge(0, 1, 1, 0), new Edge(1, 2, 1, 1) }; + + var single = Dijkstra.Solve(edges, 2, 3); + Assert.AreEqual(2f, single[2, 0]); + Assert.AreEqual(-1f, single[2, 1]); + Assert.AreEqual(0f, single[2, 2]); + + var multi = Dijkstra.Solve(edges, [0, 2], 3); + Assert.AreEqual(0f, multi[0, 0]); + Assert.AreEqual(-1f, multi[0, 1]); + Assert.AreEqual(0f, multi[0, 2]); + Assert.AreEqual(2f, multi[2, 0]); + Assert.AreEqual(-1f, multi[2, 1]); + Assert.AreEqual(0f, multi[2, 2]); + } + + /// + /// An unreachable node's row is exactly (-1, -1, positive infinity) in all three columns. + /// + [TestMethod] + public void UnreachableRowContract() + { + var edges = new List { new Edge(0, 1, 1, 0) }; + + var result = Dijkstra.Solve(edges, 1, 3); + + Assert.AreEqual(-1f, result[2, 0]); + Assert.AreEqual(-1f, result[2, 1]); + Assert.IsTrue(float.IsPositiveInfinity(result[2, 2])); + } + + /// + /// Solving the same inputs twice produces element-for-element identical tables. + /// + [TestMethod] + public void RepeatedSolveIsBitIdentical() + { + var edges = BuildRandomGraph(new MersenneTwister(45678), 300, 1200); + + var first = Dijkstra.Solve(edges, 7, 300); + var second = Dijkstra.Solve(edges, 7, 300); + + for (int i = 0; i < 300; i++) + { + for (int c = 0; c < 3; c++) + { + Assert.AreEqual(first[i, c], second[i, c], 0f, $"Cell [{i},{c}] differs between runs."); + } + } + } + + /// + /// On an exact cost tie between destinations, the earlier destination in the array wins; + /// reversing the array flips the winner. + /// + [TestMethod] + public void MultiDestinationTieBreaksByArrayOrder() + { + var edges = new List + { + new Edge(0, 1, 5, 0), + new Edge(0, 2, 5, 1), + }; + + var forward = Dijkstra.Solve(edges, [1, 2], 3); + Assert.AreEqual(1f, forward[0, 0]); + Assert.AreEqual(0f, forward[0, 1]); + Assert.AreEqual(5f, forward[0, 2]); + + var reversed = Dijkstra.Solve(edges, [2, 1], 3); + Assert.AreEqual(2f, reversed[0, 0]); + Assert.AreEqual(1f, reversed[0, 1]); + Assert.AreEqual(5f, reversed[0, 2]); + } + + /// + /// The cost column equals an independent Bellman-Ford re-derivation on fixed-seed random + /// graphs of several sizes and densities (integer weights make the comparison exact). + /// + [TestMethod] + public void SingleDestinationMatchesBellmanFordOnRandomGraphs() + { + (int nodes, int edges, int seed)[] cases = { (200, 800, 101), (500, 2500, 102), (1000, 6000, 103) }; + foreach (var (nodeCount, edgeCount, seed) in cases) + { + var edges = BuildRandomGraph(new MersenneTwister(seed), nodeCount, edgeCount); + int destination = nodeCount / 2; + + var result = Dijkstra.Solve(edges, destination, nodeCount); + float[] oracle = BellmanFordToDestination(edges, nodeCount, destination); + + for (int i = 0; i < nodeCount; i++) + { + Assert.AreEqual(oracle[i], result[i, 2], 0f, $"Cost mismatch at node {i} (n={nodeCount})."); + } + } + } + + /// + /// Multi-destination merged costs equal the per-destination oracle minima. + /// + [TestMethod] + public void MultiDestinationMatchesPerDestinationOracleMin() + { + var edges = BuildRandomGraph(new MersenneTwister(104), 400, 1600); + int[] destinations = { 3, 200, 397 }; + + var result = Dijkstra.Solve(edges, destinations, 400); + + var oracles = new float[destinations.Length][]; + for (int d = 0; d < destinations.Length; d++) + { + oracles[d] = BellmanFordToDestination(edges, 400, destinations[d]); + } + for (int i = 0; i < 400; i++) + { + float expected = float.PositiveInfinity; + for (int d = 0; d < destinations.Length; d++) + { + if (oracles[d][i] < expected) expected = oracles[d][i]; + } + Assert.AreEqual(expected, result[i, 2], 0f, $"Merged cost mismatch at node {i}."); + } + } + + /// + /// Structural self-consistency on random graphs: every reachable non-destination row + /// names a real edge from the node to its recorded next node, and the costs telescope + /// exactly along that edge. + /// + [TestMethod] + public void ResultTableIsSelfConsistentOnRandomGraphs() + { + var edges = BuildRandomGraph(new MersenneTwister(105), 300, 1500); + int destination = 17; + + var result = Dijkstra.Solve(edges, destination, 300); + + for (int i = 0; i < 300; i++) + { + if (i == destination || float.IsPositiveInfinity(result[i, 2])) continue; + int next = (int)result[i, 0]; + int edgeIndex = (int)result[i, 1]; + + bool found = false; + for (int j = 0; j < edges.Count; j++) + { + var edge = edges[j]; + if (edge.FromIndex == i && edge.ToIndex == next && edge.Index == edgeIndex + && result[i, 2] == result[next, 2] + edge.Weight) + { + found = true; + break; + } + } + Assert.IsTrue(found, $"Node {i} routes over edge index {edgeIndex} to {next}, but no such edge telescopes the costs."); + } + } + + /// + /// A complete directed graph keeps every node in flight at once; the solver's + /// exact-node-count heap capacity must never overflow. + /// + [TestMethod] + public void DenseGraphNeverOverflowsExactHeapCapacity() + { + const int n = 200; + var edges = new List(n * (n - 1)); + int index = 0; + for (int a = 0; a < n; a++) + { + for (int b = 0; b < n; b++) + { + if (a == b) continue; + edges.Add(new Edge(a, b, 1f + ((a + b) % 7), index++)); + } + } + + var result = Dijkstra.Solve(edges, n - 1, n); + + for (int i = 0; i < n; i++) + { + Assert.IsTrue(Dijkstra.PathExists(result, i), $"Node {i} must reach the destination in a complete graph."); + } + } + + /// + /// Malformed inputs fail with clear argument exceptions instead of raw index or LINQ + /// crashes. + /// + [TestMethod] + public void SolveValidationThrows() + { + var edges = new List { new Edge(0, 1, 1, 0) }; + + Assert.Throws(() => Dijkstra.Solve(null!, 0, 2)); + Assert.Throws(() => Dijkstra.Solve(edges, null!, 2)); + Assert.Throws(() => Dijkstra.Solve(new List(), 0)); + Assert.Throws(() => Dijkstra.Solve(edges, 0, 0)); + Assert.Throws(() => Dijkstra.Solve(edges, -1, 2)); + Assert.Throws(() => Dijkstra.Solve(edges, 2, 2)); + Assert.Throws(() => Dijkstra.Solve(edges, [0, 5], 2)); + Assert.Throws(() => Dijkstra.Solve(new List { new Edge(0, 9, 1, 0) }, 0, 3)); + + Assert.Throws(() => Dijkstra.PathExists(null!, 0)); + Assert.Throws(() => Dijkstra.PathExists(new float[2, 3], 2)); + Assert.Throws(() => Dijkstra.PathExists(new float[2, 2], 0)); + } + + /// + /// Negative, NaN, and positive-infinity weights pass through without rejection: a + /// negative weight computes, a NaN weight never relaxes its edge, and an infinite + /// weight is impassable. + /// + [TestMethod] + public void NegativeNaNAndInfinityWeightsAreAccepted() + { + var negative = Dijkstra.Solve(new List { new Edge(0, 1, -5, 0) }, 1, 2); + Assert.AreEqual(-5f, negative[0, 2]); + + var nan = Dijkstra.Solve(new List { new Edge(0, 1, float.NaN, 0) }, 1, 2); + Assert.IsTrue(float.IsPositiveInfinity(nan[0, 2]), "A NaN weight never relaxes, severing its edge."); + + var infinite = Dijkstra.Solve(new List { new Edge(0, 1, float.PositiveInfinity, 0) }, 1, 2); + Assert.IsTrue(float.IsPositiveInfinity(infinite[0, 2]), "An infinite weight is impassable."); + } + + /// + /// An empty edge list with an explicit node count yields the destination row and + /// all-unreachable rows, and an empty destination array yields an all-unreachable table. + /// + [TestMethod] + public void EmptyInputsPreserveDocumentedSemantics() + { + var noEdges = Dijkstra.Solve(new List(), 0, 3); + Assert.AreEqual(0f, noEdges[0, 0]); + Assert.AreEqual(0f, noEdges[0, 2]); + Assert.IsTrue(float.IsPositiveInfinity(noEdges[1, 2])); + Assert.IsTrue(float.IsPositiveInfinity(noEdges[2, 2])); + + var noDestinations = Dijkstra.Solve(new List { new Edge(0, 1, 1, 0) }, new int[0], 2); + Assert.IsTrue(float.IsPositiveInfinity(noDestinations[0, 2])); + Assert.IsTrue(float.IsPositiveInfinity(noDestinations[1, 2])); + } + + /// + /// Performance smoke: a 316 by 316 four-neighbor grid (99,856 nodes) solves to a corner + /// destination without heap overflow, and the far corner's cost equals the Manhattan + /// distance. + /// + [TestMethod] + [TestCategory("Performance")] + public void P1_LargeGridSingleDestination() + { + const int side = 316; + var edges = BuildGrid(side); + + var stopwatch = Stopwatch.StartNew(); + var result = Dijkstra.Solve(edges, 0, side * side); + stopwatch.Stop(); + Console.WriteLine($"P1 grid {side}x{side}: {stopwatch.Elapsed.TotalSeconds:F3} s"); + + Assert.AreEqual(2f * (side - 1), result[side * side - 1, 2], 0f); + } + + /// + /// Performance smoke: the same grid solved to its four corners through the + /// multi-destination overload; the center's merged cost equals the nearest corner's + /// Manhattan distance. + /// + [TestMethod] + [TestCategory("Performance")] + public void P2_LargeGridMultiDestination() + { + const int side = 316; + var edges = BuildGrid(side); + int[] corners = { 0, side - 1, side * (side - 1), side * side - 1 }; + + var stopwatch = Stopwatch.StartNew(); + var result = Dijkstra.Solve(edges, corners, side * side); + stopwatch.Stop(); + Console.WriteLine($"P2 grid {side}x{side} x4 destinations: {stopwatch.Elapsed.TotalSeconds:F3} s"); + + int center = (side / 2) * side + (side / 2); + // The nearest corner from (158, 158) is (315, 315): 157 + 157 steps. + float expected = 2f * (side - 1 - side / 2); + Assert.AreEqual(expected, result[center, 2], 0f); + } + + /// + /// Builds a random directed graph with integer-valued float weights (exact float sums). + /// + /// The seeded generator. + /// The node count. + /// The edge count. + /// The edge list. + private static List BuildRandomGraph(Random randy, int nodeCount, int edgeCount) + { + var edges = new List(edgeCount); + for (int j = 0; j < edgeCount; j++) + { + edges.Add(new Edge(randy.Next(0, nodeCount), randy.Next(0, nodeCount), randy.Next(1, 20), j)); + } + return edges; + } + + /// + /// Builds a bidirectional four-neighbor grid with unit weights. + /// + /// The grid side length. + /// The edge list. + private static List BuildGrid(int side) + { + var edges = new List(4 * side * (side - 1)); + int index = 0; + for (int r = 0; r < side; r++) + { + for (int c = 0; c < side; c++) + { + int node = r * side + c; + if (c + 1 < side) + { + edges.Add(new Edge(node, node + 1, 1, index++)); + edges.Add(new Edge(node + 1, node, 1, index++)); + } + if (r + 1 < side) + { + edges.Add(new Edge(node, node + side, 1, index++)); + edges.Add(new Edge(node + side, node, 1, index++)); + } + } + } + return edges; + } + + /// + /// Independent Bellman-Ford re-derivation of every node's cost to the destination, + /// following edges backward exactly like the solver under test. + /// + /// The network edges. + /// The node count. + /// The destination node. + /// The per-node costs to the destination. + private static float[] BellmanFordToDestination(List edges, int nodeCount, int destination) + { + var dist = new float[nodeCount]; + for (int i = 0; i < nodeCount; i++) dist[i] = float.PositiveInfinity; + dist[destination] = 0f; + + for (int pass = 0; pass < nodeCount; pass++) + { + bool changed = false; + for (int j = 0; j < edges.Count; j++) + { + var edge = edges[j]; + float candidate = dist[edge.ToIndex] + edge.Weight; + if (candidate < dist[edge.FromIndex]) + { + dist[edge.FromIndex] = candidate; + changed = true; + } + } + if (!changed) break; + } + return dist; + } } } From 108793ff0440e3e10e293759597908f60e01d893 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 3 Aug 2026 15:14:07 -0600 Subject: [PATCH 045/222] Add path reconstruction and single-pass nearest-destination solves to Dijkstra GetPath and TryGetPath walk a result table into the ordered edge-index list with the total cost, guarding against inconsistent tables instead of hanging - the walker consumers previously re-implemented by hand. SolveNearest seeds every destination at cost zero and runs one pass: costs match the merged multi-destination overload exactly, tie routing resolves by deterministic heap order, and the four-destination 316x316 grid drops from 0.080 s merged to 0.034 s on the development machine. --- .../Optimization/Dynamic/Dijkstra.cs | 122 ++++++++++++++ .../Optimization/Dynamic/Test_ShortestPath.cs | 152 ++++++++++++++++++ 2 files changed, 274 insertions(+) diff --git a/Numerics/Mathematics/Optimization/Dynamic/Dijkstra.cs b/Numerics/Mathematics/Optimization/Dynamic/Dijkstra.cs index a4271dc6..2564a5f1 100644 --- a/Numerics/Mathematics/Optimization/Dynamic/Dijkstra.cs +++ b/Numerics/Mathematics/Optimization/Dynamic/Dijkstra.cs @@ -91,6 +91,128 @@ public static bool PathExists(float[,] resultTable, int nodeIndex) return !float.IsPositiveInfinity(resultTable[nodeIndex, COST]); } + /// + /// Walks a result table from the specified start node and returns the ordered list of + /// edge indices leading to the destination. + /// + /// A result table produced by one of the solvers. + /// The node to start from. + /// + /// The ordered edge indices from the start node to the destination; an empty list when + /// the start node is itself the destination; null when the destination is unreachable. + /// + /// Thrown when the result table is null. + /// Thrown when the result table does not have three columns, or does not converge to a destination. + /// Thrown when the start node is outside the table. + public static List? GetPath(float[,] resultTable, int startNodeIndex) + { + return TryGetPath(resultTable, startNodeIndex, out List pathEdgeIndices, out _) ? pathEdgeIndices : null; + } + + /// + /// Attempts to walk a result table from the specified start node, returning the ordered + /// edge indices and the total path cost. + /// + /// A result table produced by one of the solvers. + /// The node to start from. + /// Receives the ordered edge indices; empty when the start node is the destination or unreachable. + /// Receives the total path cost; positive infinity when unreachable. + /// True when the destination is reachable from the start node; otherwise false. + /// Thrown when the result table is null. + /// Thrown when the result table does not have three columns, or does not converge to a destination. + /// Thrown when the start node is outside the table. + public static bool TryGetPath(float[,] resultTable, int startNodeIndex, out List pathEdgeIndices, out float totalCost) + { + if (resultTable == null) throw new ArgumentNullException(nameof(resultTable)); + if (resultTable.GetLength(1) != 3) throw new ArgumentException("The result table must have three columns.", nameof(resultTable)); + int rowCount = resultTable.GetLength(0); + if (startNodeIndex < 0 || startNodeIndex >= rowCount) throw new ArgumentOutOfRangeException(nameof(startNodeIndex), $"The start node index must be within [0, {rowCount})."); + + pathEdgeIndices = new List(); + totalCost = resultTable[startNodeIndex, COST]; + if (float.IsPositiveInfinity(totalCost)) return false; + + int node = startNodeIndex; + int steps = 0; + while (resultTable[node, EDGE_INDEX] >= 0f) + { + if (++steps > rowCount) + throw new ArgumentException("The result table does not converge to a destination; it may be inconsistent.", nameof(resultTable)); + pathEdgeIndices.Add((int)resultTable[node, EDGE_INDEX]); + int next = (int)resultTable[node, NEXT_NODE]; + if (next < 0 || next >= rowCount) + throw new ArgumentException("The result table routes to a node outside the table; it may be inconsistent.", nameof(resultTable)); + node = next; + } + if (resultTable[node, COST] != 0f) + throw new ArgumentException("The result table walk ended away from a destination; it may be inconsistent.", nameof(resultTable)); + return true; + } + + /// + /// Solves the shortest path from every node to its nearest destination in a single + /// multi-source pass. + /// + /// Edges, or segments, that make up the network. + /// Indices of the destination nodes; at least one is required. + /// Optional number of nodes in the network. If not provided it will be calculated internally. + /// Lookup table of shortest paths from any given node to its nearest destination. + /// + /// Costs match the multi-destination + /// overload exactly; the routed next node and edge can differ from it only where two + /// destinations are exactly equidistant, where this method resolves the tie by + /// deterministic heap order rather than destination array order. One pass over the + /// network replaces one pass per destination. Duplicate destination indices are + /// tolerated. + /// + /// Thrown when the edges or destination indices are null. + /// Thrown when the destination array is empty, the node count cannot be derived, or an edge references a node outside the network. + /// Thrown when the node count is not positive, or a destination index is outside the network. + public static float[,] SolveNearest(IList edges, int[] destinationIndices, int nodeCount = -1) + { + if (edges == null) throw new ArgumentNullException(nameof(edges)); + if (destinationIndices == null) throw new ArgumentNullException(nameof(destinationIndices)); + if (destinationIndices.Length == 0) throw new ArgumentException("At least one destination index is required.", nameof(destinationIndices)); + + int nNodes = ResolveNodeCount(edges, nodeCount); + for (int i = 0; i < destinationIndices.Length; i++) + { + if (destinationIndices[i] < 0 || destinationIndices[i] >= nNodes) + throw new ArgumentOutOfRangeException(nameof(destinationIndices), $"The destination index {destinationIndices[i]} must be within [0, {nNodes})."); + } + + CompactAdjacency adjacency = CompactAdjacency.FromEdges(edges, nNodes, groupByEndNode: true, nameof(edges)); + + var next = new int[nNodes]; + var edgeIndexes = new int[nNodes]; + var dist = new float[nNodes]; + var state = new int[nNodes]; + var heap = new IndexedMinHeap(nNodes); + + for (int i = 0; i < nNodes; i++) + { + next[i] = -1; + edgeIndexes[i] = -1; + dist[i] = float.PositiveInfinity; + state[i] = 0; + } + heap.Clear(); + + for (int d = 0; d < destinationIndices.Length; d++) + { + int destination = destinationIndices[d]; + if (state[destination] == 2) continue; // duplicate destination + next[destination] = destination; + edgeIndexes[destination] = -1; + dist[destination] = 0f; + heap.Add(destination, 0f); + state[destination] = 2; + } + + RunToExhaustion(adjacency, null, next, edgeIndexes, dist, state, heap); + return WriteTable(next, edgeIndexes, dist); + } + /// /// Solves the shortest path from every node in the network of edges to the nearest of /// the given destinations. diff --git a/Test_Numerics/Mathematics/Optimization/Dynamic/Test_ShortestPath.cs b/Test_Numerics/Mathematics/Optimization/Dynamic/Test_ShortestPath.cs index 544e7783..9a411c0a 100644 --- a/Test_Numerics/Mathematics/Optimization/Dynamic/Test_ShortestPath.cs +++ b/Test_Numerics/Mathematics/Optimization/Dynamic/Test_ShortestPath.cs @@ -568,6 +568,152 @@ public void EmptyInputsPreserveDocumentedSemantics() Assert.IsTrue(float.IsPositiveInfinity(noDestinations[1, 2])); } + /// + /// The path walker returns the ordered edge indices to the destination, and the try + /// variant returns the total cost read from the table. + /// + [TestMethod] + public void GetPathWalksEdgeIndices() + { + var edges = new List + { + new Edge(0, 1, 2, 0), + new Edge(0, 2, 4, 2), + new Edge(1, 2, 1, 2), + new Edge(1, 3, 7, 3), + new Edge(2, 3, 3, 4), + new Edge(4, 0, 1, 5), + }; + var result = Dijkstra.Solve(edges, 3, 6); + + var path = Dijkstra.GetPath(result, 0); + Assert.IsNotNull(path); + CollectionAssert.AreEqual(new List { 0, 2, 4 }, path); + + Assert.IsTrue(Dijkstra.TryGetPath(result, 4, out var fromFour, out float cost)); + CollectionAssert.AreEqual(new List { 5, 0, 2, 4 }, fromFour); + Assert.AreEqual(7f, cost, 0f); + } + + /// + /// Starting the walk at the destination returns an empty path with zero cost, not null. + /// + [TestMethod] + public void GetPathStartAtDestinationReturnsEmpty() + { + var result = Dijkstra.Solve(new List { new Edge(0, 1, 1, 0) }, 1, 2); + + var path = Dijkstra.GetPath(result, 1); + Assert.IsNotNull(path); + Assert.AreEqual(0, path!.Count); + + Assert.IsTrue(Dijkstra.TryGetPath(result, 1, out var tryPath, out float cost)); + Assert.AreEqual(0, tryPath.Count); + Assert.AreEqual(0f, cost, 0f); + } + + /// + /// Walking from an unreachable node returns null (or false with an infinite cost). + /// + [TestMethod] + public void GetPathUnreachableReturnsNull() + { + var result = Dijkstra.Solve(new List { new Edge(0, 1, 1, 0) }, 1, 3); + + Assert.IsNull(Dijkstra.GetPath(result, 2)); + Assert.IsFalse(Dijkstra.TryGetPath(result, 2, out var path, out float cost)); + Assert.AreEqual(0, path.Count); + Assert.IsTrue(float.IsPositiveInfinity(cost)); + } + + /// + /// A cyclic or non-converging table fails the walk loudly instead of hanging. + /// + [TestMethod] + public void GetPathThrowsOnInconsistentTable() + { + var cyclic = new float[2, 3]; + cyclic[0, 0] = 1; cyclic[0, 1] = 0; cyclic[0, 2] = 1; + cyclic[1, 0] = 0; cyclic[1, 1] = 1; cyclic[1, 2] = 1; + + Assert.Throws(() => Dijkstra.GetPath(cyclic, 0)); + } + + /// + /// The single-pass nearest-destination solve matches the merged multi-destination solve: + /// costs are identical on random graphs, and every column is identical on a tie-free + /// graph. + /// + [TestMethod] + public void SolveNearestMatchesMergedSolve() + { + var edges = BuildRandomGraph(new MersenneTwister(106), 400, 1600); + int[] destinations = { 11, 222, 333 }; + var merged = Dijkstra.Solve(edges, destinations, 400); + var nearest = Dijkstra.SolveNearest(edges, destinations, 400); + for (int i = 0; i < 400; i++) + { + Assert.AreEqual(merged[i, 2], nearest[i, 2], 0f, $"Cost mismatch at node {i}."); + } + + // Tie-free line graph: 0 -1- 1 -1- 2 -10- 3 -1- 4; destinations 0 and 4. + var line = new List + { + new Edge(1, 0, 1, 0), + new Edge(2, 1, 1, 1), + new Edge(3, 2, 10, 2), + new Edge(3, 4, 1, 3), + new Edge(2, 3, 10, 4), + new Edge(1, 2, 1, 5), + new Edge(0, 1, 1, 6), + new Edge(4, 3, 1, 7), + }; + var mergedLine = Dijkstra.Solve(line, [0, 4], 5); + var nearestLine = Dijkstra.SolveNearest(line, [0, 4], 5); + for (int i = 0; i < 5; i++) + { + for (int c = 0; c < 3; c++) + { + Assert.AreEqual(mergedLine[i, c], nearestLine[i, c], 0f, $"Cell [{i},{c}] differs on the tie-free graph."); + } + } + } + + /// + /// The nearest-destination solve is deterministic across repeated calls and tolerates + /// duplicate destination indices. + /// + [TestMethod] + public void SolveNearestIsDeterministic() + { + var edges = BuildRandomGraph(new MersenneTwister(107), 250, 1000); + + var first = Dijkstra.SolveNearest(edges, [5, 5, 100], 250); + var second = Dijkstra.SolveNearest(edges, [5, 100], 250); + for (int i = 0; i < 250; i++) + { + for (int c = 0; c < 3; c++) + { + Assert.AreEqual(first[i, c], second[i, c], 0f, $"Cell [{i},{c}] differs with duplicate destinations."); + } + } + } + + /// + /// The nearest-destination solve rejects null and empty destination arrays and + /// out-of-range destinations. + /// + [TestMethod] + public void SolveNearestValidationThrows() + { + var edges = new List { new Edge(0, 1, 1, 0) }; + + Assert.Throws(() => Dijkstra.SolveNearest(null!, [0], 2)); + Assert.Throws(() => Dijkstra.SolveNearest(edges, null!, 2)); + Assert.Throws(() => Dijkstra.SolveNearest(edges, new int[0], 2)); + Assert.Throws(() => Dijkstra.SolveNearest(edges, [7], 2)); + } + /// /// Performance smoke: a 316 by 316 four-neighbor grid (99,856 nodes) solves to a corner /// destination without heap overflow, and the far corner's cost equals the Manhattan @@ -606,10 +752,16 @@ public void P2_LargeGridMultiDestination() stopwatch.Stop(); Console.WriteLine($"P2 grid {side}x{side} x4 destinations: {stopwatch.Elapsed.TotalSeconds:F3} s"); + stopwatch.Restart(); + var nearest = Dijkstra.SolveNearest(edges, corners, side * side); + stopwatch.Stop(); + Console.WriteLine($"P2 grid {side}x{side} SolveNearest: {stopwatch.Elapsed.TotalSeconds:F3} s"); + int center = (side / 2) * side + (side / 2); // The nearest corner from (158, 158) is (315, 315): 157 + 157 steps. float expected = 2f * (side - 1 - side / 2); Assert.AreEqual(expected, result[center, 2], 0f); + Assert.AreEqual(expected, nearest[center, 2], 0f); } /// From e9d8ece764eb09cbc53396966bc05342c75090b8 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 3 Aug 2026 15:19:10 -0600 Subject: [PATCH 046/222] Repair Network construction and custom-weight solves Network could not be constructed for any nonempty edge set: the node count stayed zero (its computation was commented out) while sizing every solve buffer, and the adjacency arrays were allocated one short of the largest node index. Custom-weight solves additionally passed the stale base-weight adjacency through, so the supplied weights were silently ignored. Construction now compiles the topology once - node count, incoming and outgoing compact adjacency, destination flags, and defensively copied inputs - and the frozen Solve overloads run the shared solver cores over it, with custom weights overlaid positionally at relaxation time with no rebuild. New additive members: NodeCount, a single-pass SolveNearest over the network destinations, and table-reuse Solve and SolveNearest overloads with zero steady-state allocation for per-timestep weight updates (documented not thread safe). One thousand nearest-destination re-solves with varying weights on a 100x100 grid run in 1.6 s, about 1.6 ms per solve, on the development machine. --- .../Optimization/Dynamic/Dijkstra.cs | 82 ++++- .../Optimization/Dynamic/Network.cs | 331 +++++++++++++++--- .../Optimization/Dynamic/Test_Network.cs | 276 +++++++++++++++ 3 files changed, 625 insertions(+), 64 deletions(-) create mode 100644 Test_Numerics/Mathematics/Optimization/Dynamic/Test_Network.cs diff --git a/Numerics/Mathematics/Optimization/Dynamic/Dijkstra.cs b/Numerics/Mathematics/Optimization/Dynamic/Dijkstra.cs index 2564a5f1..5c988543 100644 --- a/Numerics/Mathematics/Optimization/Dynamic/Dijkstra.cs +++ b/Numerics/Mathematics/Optimization/Dynamic/Dijkstra.cs @@ -186,9 +186,27 @@ public static bool TryGetPath(float[,] resultTable, int startNodeIndex, out List var next = new int[nNodes]; var edgeIndexes = new int[nNodes]; var dist = new float[nNodes]; - var state = new int[nNodes]; - var heap = new IndexedMinHeap(nNodes); + SolveNearestCore(adjacency, null, destinationIndices, next, edgeIndexes, dist, new int[nNodes], new IndexedMinHeap(nNodes)); + return WriteTable(next, edgeIndexes, dist); + } + /// + /// Runs the single-pass multi-source solve: every destination seeds at cost zero (in + /// array order, duplicates skipped) and one heap drain routes every node to its nearest + /// destination. Buffers are fully re-initialized, so they can be reused across calls. + /// + /// The incoming-edge compact adjacency. + /// Optional positional weight overlay; null uses the adjacency weights. + /// The destination nodes. + /// Receives the next node toward the nearest destination per node; -1 when unreachable. + /// Receives the edge index to take per node; -1 when unreachable. + /// Receives the cumulative cost per node; positive infinity when unreachable. + /// Scratch node states. + /// The scratch heap, sized at the node count. + internal static void SolveNearestCore(in CompactAdjacency adjacency, float[]? weightOverride, int[] destinationIndices, + int[] next, int[] edgeIndexes, float[] dist, int[] state, IndexedMinHeap heap) + { + int nNodes = adjacency.NodeCount; for (int i = 0; i < nNodes; i++) { next[i] = -1; @@ -209,8 +227,7 @@ public static bool TryGetPath(float[,] resultTable, int startNodeIndex, out List state[destination] = 2; } - RunToExhaustion(adjacency, null, next, edgeIndexes, dist, state, heap); - return WriteTable(next, edgeIndexes, dist); + RunToExhaustion(adjacency, weightOverride, next, edgeIndexes, dist, state, heap); } /// @@ -243,12 +260,37 @@ public static bool TryGetPath(float[,] resultTable, int startNodeIndex, out List } CompactAdjacency adjacency = BuildIncomingAdjacency(edges, nNodes, edgesFromNodes); - - // Accumulators start all-unreachable; each destination's solve merges in by - // strictly smaller cost, preserving the earlier-destination-wins tie rule. var bestNext = new int[nNodes]; var bestEdge = new int[nNodes]; var bestDist = new float[nNodes]; + SolveMergedCore(adjacency, null, destinationIndices, + new int[nNodes], new int[nNodes], new float[nNodes], new int[nNodes], new IndexedMinHeap(nNodes), + bestNext, bestEdge, bestDist); + return WriteTable(bestNext, bestEdge, bestDist); + } + + /// + /// Runs one destination-rooted solve per destination in array order and merges the + /// results per node by strictly smaller cost — the multi-destination semantics: on an + /// exact cost tie the earlier destination wins. The accumulators start all-unreachable + /// and the scratch buffers are reused across destinations. + /// + /// The incoming-edge compact adjacency. + /// Optional positional weight overlay; null uses the adjacency weights. + /// The destination nodes, in merge order. + /// Scratch per-node next-node buffer. + /// Scratch per-node edge-index buffer. + /// Scratch per-node cost buffer. + /// Scratch per-node state buffer. + /// The scratch heap, sized at the node count. + /// Receives the merged next node per node. + /// Receives the merged edge index per node. + /// Receives the merged cost per node. + internal static void SolveMergedCore(in CompactAdjacency adjacency, float[]? weightOverride, int[] destinationIndices, + int[] next, int[] edgeIndexes, float[] dist, int[] state, IndexedMinHeap heap, + int[] bestNext, int[] bestEdge, float[] bestDist) + { + int nNodes = adjacency.NodeCount; for (int i = 0; i < nNodes; i++) { bestNext[i] = -1; @@ -256,16 +298,9 @@ public static bool TryGetPath(float[,] resultTable, int startNodeIndex, out List bestDist[i] = float.PositiveInfinity; } - // One scratch set reused across destinations. - var next = new int[nNodes]; - var edgeIndexes = new int[nNodes]; - var dist = new float[nNodes]; - var state = new int[nNodes]; - var heap = new IndexedMinHeap(nNodes); - for (int d = 0; d < destinationIndices.Length; d++) { - SolveCore(adjacency, null, destinationIndices[d], next, edgeIndexes, dist, state, heap); + SolveCore(adjacency, weightOverride, destinationIndices[d], next, edgeIndexes, dist, state, heap); for (int j = 0; j < nNodes; j++) { if (dist[j] < bestDist[j]) @@ -276,7 +311,6 @@ public static bool TryGetPath(float[,] resultTable, int startNodeIndex, out List } } } - return WriteTable(bestNext, bestEdge, bestDist); } /// @@ -461,16 +495,28 @@ private static CompactAdjacency BuildIncomingAdjacency(IList edges, int no /// The per-node cumulative-cost buffer. /// The result table. internal static float[,] WriteTable(int[] next, int[] edgeIndexes, float[] dist) + { + var resultTable = new float[next.Length, 3]; + WriteTable(next, edgeIndexes, dist, resultTable); + return resultTable; + } + + /// + /// Writes the flat solve buffers into a caller-supplied three-column result table. + /// + /// The per-node next-node buffer. + /// The per-node edge-index buffer. + /// The per-node cumulative-cost buffer. + /// The table to fill; its row count must equal the buffer length. + internal static void WriteTable(int[] next, int[] edgeIndexes, float[] dist, float[,] resultTable) { int nNodes = next.Length; - var resultTable = new float[nNodes, 3]; for (int i = 0; i < nNodes; i++) { resultTable[i, NEXT_NODE] = next[i]; resultTable[i, EDGE_INDEX] = edgeIndexes[i]; resultTable[i, COST] = dist[i]; } - return resultTable; } } } diff --git a/Numerics/Mathematics/Optimization/Dynamic/Network.cs b/Numerics/Mathematics/Optimization/Dynamic/Network.cs index 40d3642b..4133e9ca 100644 --- a/Numerics/Mathematics/Optimization/Dynamic/Network.cs +++ b/Numerics/Mathematics/Optimization/Dynamic/Network.cs @@ -1,111 +1,350 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Numerics.Mathematics.Optimization { /// /// A network of edges used for shortest path optimization applications. /// + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + /// Description: + /// The network compiles its topology once at construction — the node count, the incoming and + /// outgoing adjacency, and the destination set are fixed for the instance's lifetime — so + /// repeated solves pay only the solve itself, and custom-weight solves overlay a positional + /// weight vector with no rebuild. The parameterless solve methods allocate their results + /// per call and are safe for concurrent use; the overloads that write into a caller-supplied + /// table reuse instance scratch buffers and are not thread safe. Weights follow the + /// conventions: non-negative weights are the correctness + /// precondition, positive infinity is impassable, and NaN severs its edge. + /// + /// public class Network { + /// The outgoing edges per node, in original edge order. private readonly List[] _outgoingEdges; + + /// The incoming edges per node, in original edge order. private readonly List[] _incomingEdges; + + /// The number of nodes, one past the largest referenced node index. private readonly int _nodeCount; + + /// The network's destination nodes. private readonly int[] _destinationIndices; - //private readonly RoadSegment[] _segments; + + /// The network's edges, in caller order. private readonly Edge[] _edges; + /// The compiled incoming adjacency (grouped by end node) the solvers walk. + private readonly CompactAdjacency _incomingAdjacency; + + /// The compiled outgoing adjacency (grouped by start node) forward routing walks. + private readonly CompactAdjacency _outgoingAdjacency; + + /// Marks the destination nodes for O(1) membership tests. + private readonly bool[] _isDestination; + + /// Lazily allocated scratch for the table-reuse solves (not thread safe). + private int[]? _scratchNext; + + /// Scratch per-node edge indices for the table-reuse solves. + private int[]? _scratchEdge; + + /// Scratch per-node costs for the table-reuse solves. + private float[]? _scratchDist; + + /// Scratch per-node states for the table-reuse solves. + private int[]? _scratchState; + + /// Scratch heap for the table-reuse solves. + private IndexedMinHeap? _scratchHeap; + + /// Scratch merged next nodes for the table-reuse multi-destination solve. + private int[]? _scratchBestNext; + + /// Scratch merged edge indices for the table-reuse multi-destination solve. + private int[]? _scratchBestEdge; + + /// Scratch merged costs for the table-reuse multi-destination solve. + private float[]? _scratchBestDist; + /// /// The destination node indices for shortest path computation. /// public int[] DestinationIndices { get => _destinationIndices; } /// - /// The incoming edges for each node, indexed by node index. + /// The incoming edges for each node, indexed by node index. Nodes with no incoming + /// edges hold an empty list. /// public List[] IncomingEdges { get => _incomingEdges; } /// - /// The outgoing edges for each node, indexed by node index. + /// The outgoing edges for each node, indexed by node index. Nodes with no outgoing + /// edges hold an empty list. /// public List[] OutgoingEdges { get => _outgoingEdges; } + /// + /// The number of nodes in the network, one past the largest node index the edges + /// reference. Caller-supplied result tables must have this many rows. + /// + public int NodeCount { get => _nodeCount; } + /// /// Creates a new network from the specified edges and destination indices. /// - /// The edges that define the network. - /// The destination node indices. + /// The edges that define the network. The array is copied. + /// The destination node indices. The array is copied. + /// Thrown when either array is null. + /// Thrown when either array is empty, or an edge references a negative node index. + /// Thrown when a destination index is outside the network. public Network(Edge[] edges, int[] destinationIndices) { - //_segments = roadSegments; - _edges = edges;//new Edge[edges.Length]; - _destinationIndices = destinationIndices; - _nodeCount = 0; - - //for (int i = 0; i < roadSegments.Length; i++) - //{ - // _edges[i] = new Edge(roadSegments[i].HeadIndex, roadSegments[i].TailIndex, (float)roadSegments[i].Source.TravelTimeSeconds, i); - // if (_edges[i].FromIndex > _nodeCount) { _nodeCount = _edges[i].FromIndex; } - // if (_edges[i].ToIndex > _nodeCount) { _nodeCount = _edges[i].ToIndex; } - //} - var max1 = edges.Select(x => x.FromIndex).Max(); - var max2 = edges.Select(x => x.ToIndex).Max(); - var max = Math.Max(max1, max2); - //// Add one to the count for the index offset. - //_nodeCount += 1; - _incomingEdges = new List[max]; - _outgoingEdges = new List[max]; - // + if (edges == null) throw new ArgumentNullException(nameof(edges)); + if (edges.Length == 0) throw new ArgumentException("At least one edge is required.", nameof(edges)); + if (destinationIndices == null) throw new ArgumentNullException(nameof(destinationIndices)); + if (destinationIndices.Length == 0) throw new ArgumentException("At least one destination index is required.", nameof(destinationIndices)); + + _edges = new Edge[edges.Length]; + Array.Copy(edges, _edges, edges.Length); + _destinationIndices = new int[destinationIndices.Length]; + Array.Copy(destinationIndices, _destinationIndices, destinationIndices.Length); + + int max = 0; for (int i = 0; i < _edges.Length; i++) { - if (_incomingEdges[_edges[i].ToIndex] == null) { _incomingEdges[_edges[i].ToIndex] = new List(); } - _incomingEdges[_edges[i].ToIndex].Add(_edges[i]); - - if (_outgoingEdges[_edges[i].FromIndex] == null) { _outgoingEdges[_edges[i].FromIndex] = new List(); } - _outgoingEdges[_edges[i].FromIndex].Add(_edges[i]); + if (_edges[i].FromIndex > max) max = _edges[i].FromIndex; + if (_edges[i].ToIndex > max) max = _edges[i].ToIndex; } + _nodeCount = max + 1; + // The compiled builds also range-check every referenced node index. + _incomingAdjacency = CompactAdjacency.FromEdges(_edges, _nodeCount, groupByEndNode: true, nameof(edges)); + _outgoingAdjacency = CompactAdjacency.FromEdges(_edges, _nodeCount, groupByEndNode: false, nameof(edges)); - } + _isDestination = new bool[_nodeCount]; + for (int d = 0; d < _destinationIndices.Length; d++) + { + int destination = _destinationIndices[d]; + if (destination < 0 || destination >= _nodeCount) + throw new ArgumentOutOfRangeException(nameof(destinationIndices), $"The destination index {destination} must be within [0, {_nodeCount})."); + _isDestination[destination] = true; + } + _incomingEdges = MaterializeLists(_incomingAdjacency); + _outgoingEdges = MaterializeLists(_outgoingAdjacency); + } /// /// Solves the shortest path from all nodes to the specified destination. /// /// The destination node index. - /// A result table with predecessor, edge index, and cumulative weight for each node. + /// A result table with the next node, edge index, and cumulative weight for each node. + /// Thrown when the destination index is outside the network. public float[,] Solve(int destinationIndex) { - return Dijkstra.Solve(_edges, destinationIndex, _nodeCount, _incomingEdges); + if (destinationIndex < 0 || destinationIndex >= _nodeCount) + throw new ArgumentOutOfRangeException(nameof(destinationIndex), $"The destination index must be within [0, {_nodeCount})."); + + var next = new int[_nodeCount]; + var edgeIndexes = new int[_nodeCount]; + var dist = new float[_nodeCount]; + Dijkstra.SolveCore(_incomingAdjacency, null, destinationIndex, next, edgeIndexes, dist, new int[_nodeCount], new IndexedMinHeap(_nodeCount)); + return Dijkstra.WriteTable(next, edgeIndexes, dist); } /// - /// Solves the shortest path from all nodes to the specified destinations. + /// Solves the shortest path from all nodes to the nearest of the specified destinations. /// /// The destination node indices. - /// A result table with predecessor, edge index, and cumulative weight for each node. + /// A result table with the next node, edge index, and cumulative weight for each node. + /// + /// Each destination is solved independently and the tables merge per node by strictly + /// smaller cost in destination order, so on an exact cost tie the earlier destination + /// wins — the same semantics as the static multi-destination solver. + /// + /// Thrown when the destination indices are null. + /// Thrown when a destination index is outside the network. public float[,] Solve(int[] destinationIndices) { - return Dijkstra.Solve(_edges, destinationIndices, _nodeCount, _incomingEdges); + ValidateDestinations(destinationIndices); + var bestNext = new int[_nodeCount]; + var bestEdge = new int[_nodeCount]; + var bestDist = new float[_nodeCount]; + Dijkstra.SolveMergedCore(_incomingAdjacency, null, destinationIndices, + new int[_nodeCount], new int[_nodeCount], new float[_nodeCount], new int[_nodeCount], new IndexedMinHeap(_nodeCount), + bestNext, bestEdge, bestDist); + return Dijkstra.WriteTable(bestNext, bestEdge, bestDist); } /// - /// Solves the shortest path using custom edge weights. + /// Solves the shortest path to the network's destinations using custom edge weights. /// - /// Custom weights for each edge. - /// A result table with predecessor, edge index, and cumulative weight for each node. + /// Custom weights, one per edge, positional with the constructor's edge array. + /// A result table with the next node, edge index, and cumulative weight for each node. + /// + /// The weights overlay the compiled topology positionally — nothing is rebuilt. The + /// destinations are the constructor's, merged in destination order exactly like + /// . + /// + /// Thrown when the weights are null. + /// Thrown when the weight count does not equal the edge count. public float[,] Solve(float[] edgeWeights) { - Edge[] edges = new Edge[_edges.Length]; - for (int i = 0; i < _edges.Length; i++) + ValidateWeights(edgeWeights); + var bestNext = new int[_nodeCount]; + var bestEdge = new int[_nodeCount]; + var bestDist = new float[_nodeCount]; + Dijkstra.SolveMergedCore(_incomingAdjacency, edgeWeights, _destinationIndices, + new int[_nodeCount], new int[_nodeCount], new float[_nodeCount], new int[_nodeCount], new IndexedMinHeap(_nodeCount), + bestNext, bestEdge, bestDist); + return Dijkstra.WriteTable(bestNext, bestEdge, bestDist); + } + + /// + /// Solves the shortest path from every node to its nearest network destination in a + /// single multi-source pass. + /// + /// A result table with the next node, edge index, and cumulative weight for each node. + /// + /// Costs match over the network's destinations exactly; the + /// routed next node and edge can differ only where two destinations are exactly + /// equidistant. One pass replaces one pass per destination. + /// + public float[,] SolveNearest() + { + var next = new int[_nodeCount]; + var edgeIndexes = new int[_nodeCount]; + var dist = new float[_nodeCount]; + Dijkstra.SolveNearestCore(_incomingAdjacency, null, _destinationIndices, next, edgeIndexes, dist, new int[_nodeCount], new IndexedMinHeap(_nodeCount)); + return Dijkstra.WriteTable(next, edgeIndexes, dist); + } + + /// + /// Solves the shortest path to the network's destinations using custom edge weights, + /// writing the results into a caller-supplied table. Reuses instance scratch buffers — + /// zero allocation per call at steady state — and is therefore not thread safe. + /// + /// Custom weights, one per edge, positional with the constructor's edge array. + /// The table to fill; its dimensions must be [, 3]. + /// Thrown when the weights or table are null. + /// Thrown when the weight count or table dimensions are wrong. + public void Solve(float[] edgeWeights, float[,] resultTable) + { + ValidateWeights(edgeWeights); + ValidateResultTable(resultTable); + EnsureScratch(); + Dijkstra.SolveMergedCore(_incomingAdjacency, edgeWeights, _destinationIndices, + _scratchNext!, _scratchEdge!, _scratchDist!, _scratchState!, _scratchHeap!, + _scratchBestNext!, _scratchBestEdge!, _scratchBestDist!); + Dijkstra.WriteTable(_scratchBestNext!, _scratchBestEdge!, _scratchBestDist!, resultTable); + } + + /// + /// Solves the shortest path from every node to its nearest network destination using + /// custom edge weights, writing the results into a caller-supplied table. Reuses + /// instance scratch buffers — zero allocation per call at steady state — and is + /// therefore not thread safe. + /// + /// Custom weights, one per edge, positional with the constructor's edge array. + /// The table to fill; its dimensions must be [, 3]. + /// Thrown when the weights or table are null. + /// Thrown when the weight count or table dimensions are wrong. + public void SolveNearest(float[] edgeWeights, float[,] resultTable) + { + ValidateWeights(edgeWeights); + ValidateResultTable(resultTable); + EnsureScratch(); + Dijkstra.SolveNearestCore(_incomingAdjacency, edgeWeights, _destinationIndices, + _scratchNext!, _scratchEdge!, _scratchDist!, _scratchState!, _scratchHeap!); + Dijkstra.WriteTable(_scratchNext!, _scratchEdge!, _scratchDist!, resultTable); + } + + /// + /// Materializes the per-node edge lists the public properties expose from a compiled + /// adjacency, preserving slot order and giving every node a list. + /// + /// The compiled adjacency. + /// The per-node edge lists. + private static List[] MaterializeLists(in CompactAdjacency adjacency) + { + var lists = new List[adjacency.NodeCount]; + for (int n = 0; n < adjacency.NodeCount; n++) + { + int start = adjacency.RowStart[n]; + int end = adjacency.RowStart[n + 1]; + var list = new List(end - start); + for (int k = start; k < end; k++) + { + list.Add(new Edge(adjacency.FromNode[k], adjacency.ToNode[k], adjacency.Weight[k], adjacency.EdgeIndex[k])); + } + lists[n] = list; + } + return lists; + } + + /// + /// Validates a caller-supplied destination array against the network. + /// + /// The destination node indices. + /// Thrown when the array is null. + /// Thrown when an index is outside the network. + private void ValidateDestinations(int[] destinationIndices) + { + if (destinationIndices == null) throw new ArgumentNullException(nameof(destinationIndices)); + for (int i = 0; i < destinationIndices.Length; i++) { - edges[i] = new Edge(_edges[i].FromIndex, _edges[i].ToIndex, edgeWeights[i], _edges[i].Index); + if (destinationIndices[i] < 0 || destinationIndices[i] >= _nodeCount) + throw new ArgumentOutOfRangeException(nameof(destinationIndices), $"The destination index {destinationIndices[i]} must be within [0, {_nodeCount})."); } - // - return Dijkstra.Solve(edges, _destinationIndices, _nodeCount, _incomingEdges); + } + + /// + /// Validates a positional custom-weight vector against the network's edge count. + /// + /// The custom weights. + /// Thrown when the weights are null. + /// Thrown when the weight count does not equal the edge count. + private void ValidateWeights(float[] edgeWeights) + { + if (edgeWeights == null) throw new ArgumentNullException(nameof(edgeWeights)); + if (edgeWeights.Length != _edges.Length) + throw new ArgumentException($"The weight count ({edgeWeights.Length}) must equal the edge count ({_edges.Length}).", nameof(edgeWeights)); + } + + /// + /// Validates a caller-supplied result table's dimensions. + /// + /// The table to fill. + /// Thrown when the table is null. + /// Thrown when the dimensions are not [NodeCount, 3]. + private void ValidateResultTable(float[,] resultTable) + { + if (resultTable == null) throw new ArgumentNullException(nameof(resultTable)); + if (resultTable.GetLength(0) != _nodeCount || resultTable.GetLength(1) != 3) + throw new ArgumentException($"The result table must be [{_nodeCount}, 3].", nameof(resultTable)); + } + + /// + /// Allocates the reuse-solve scratch buffers on first use. + /// + private void EnsureScratch() + { + if (_scratchNext != null) return; + _scratchNext = new int[_nodeCount]; + _scratchEdge = new int[_nodeCount]; + _scratchDist = new float[_nodeCount]; + _scratchState = new int[_nodeCount]; + _scratchHeap = new IndexedMinHeap(_nodeCount); + _scratchBestNext = new int[_nodeCount]; + _scratchBestEdge = new int[_nodeCount]; + _scratchBestDist = new float[_nodeCount]; } /// diff --git a/Test_Numerics/Mathematics/Optimization/Dynamic/Test_Network.cs b/Test_Numerics/Mathematics/Optimization/Dynamic/Test_Network.cs new file mode 100644 index 00000000..c073efa6 --- /dev/null +++ b/Test_Numerics/Mathematics/Optimization/Dynamic/Test_Network.cs @@ -0,0 +1,276 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Mathematics.Optimization; + +namespace Mathematics.Optimization +{ + /// + /// Tests the compiled-topology network wrapper over the Dijkstra solvers. + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + [TestClass] + public class Test_Network + { + /// + /// Builds the two-way five-node test network used throughout: a short chain 0-1-2 with a + /// slow bypass 0-3-2 and a spur 2-4. + /// + /// The edges and the network destinations (node 4). + private static (Edge[] Edges, int[] Destinations) BuildFixture() + { + var edges = new[] + { + new Edge(0, 1, 1, 0), + new Edge(1, 0, 1, 0), + new Edge(1, 2, 1, 1), + new Edge(2, 1, 1, 1), + new Edge(0, 3, 5, 2), + new Edge(3, 0, 5, 2), + new Edge(3, 2, 5, 3), + new Edge(2, 3, 5, 3), + new Edge(2, 4, 1, 4), + new Edge(4, 2, 1, 4), + }; + return (edges, new[] { 4 }); + } + + /// + /// Construction succeeds for a non-empty edge set, groups the adjacency correctly with + /// empty lists for edge-free directions, and reports the node count. + /// + [TestMethod] + public void CtorBuildsAdjacencyForNonEmptyEdges() + { + var (edges, destinations) = BuildFixture(); + var network = new Network(edges, destinations); + + Assert.AreEqual(5, network.NodeCount); + Assert.AreEqual(5, network.IncomingEdges.Length); + Assert.AreEqual(5, network.OutgoingEdges.Length); + Assert.AreEqual(2, network.OutgoingEdges[0].Count); // 0->1 and 0->3 + Assert.AreEqual(1, network.IncomingEdges[4].Count); + Assert.AreEqual(2, network.IncomingEdges[4][0].FromIndex); + CollectionAssert.AreEqual(new[] { 4 }, network.DestinationIndices); + + // An isolated node (index 5 present only via the node count) is impossible here; + // instead verify a node with no incoming edges gets an empty list, not null. + var oneWay = new Network(new[] { new Edge(0, 1, 1, 0) }, new[] { 1 }); + Assert.IsNotNull(oneWay.IncomingEdges[0]); + Assert.AreEqual(0, oneWay.IncomingEdges[0].Count); + Assert.IsNotNull(oneWay.OutgoingEdges[1]); + Assert.AreEqual(0, oneWay.OutgoingEdges[1].Count); + } + + /// + /// The constructor copies both input arrays: mutating them afterward changes nothing. + /// + [TestMethod] + public void CtorCopiesInputs() + { + var (edges, destinations) = BuildFixture(); + var network = new Network(edges, destinations); + var before = network.Solve(4); + + edges[0] = new Edge(0, 1, 100, 0); + destinations[0] = 0; + + var after = network.Solve(4); + Assert.AreEqual(before[0, 2], after[0, 2], 0f); + CollectionAssert.AreEqual(new[] { 4 }, network.DestinationIndices); + } + + /// + /// Malformed construction inputs fail with clear argument exceptions. + /// + [TestMethod] + public void CtorValidationThrows() + { + var edges = new[] { new Edge(0, 1, 1, 0) }; + + Assert.Throws(() => new Network(null!, [0])); + Assert.Throws(() => new Network(edges, null!)); + Assert.Throws(() => new Network(new Edge[0], [0])); + Assert.Throws(() => new Network(edges, new int[0])); + Assert.Throws(() => new Network(edges, [5])); + Assert.Throws(() => new Network(new[] { new Edge(-1, 1, 1, 0) }, [0])); + } + + /// + /// The network solves equal the static Dijkstra solves on the same inputs, cell for + /// cell, for single and multiple destinations. + /// + [TestMethod] + public void SolveMatchesDijkstra() + { + var (edges, destinations) = BuildFixture(); + var network = new Network(edges, destinations); + + var networkSingle = network.Solve(4); + var directSingle = Dijkstra.Solve(edges, 4, network.NodeCount); + var networkMulti = network.Solve(new[] { 0, 4 }); + var directMulti = Dijkstra.Solve(edges, new[] { 0, 4 }, network.NodeCount); + + for (int i = 0; i < network.NodeCount; i++) + { + for (int c = 0; c < 3; c++) + { + Assert.AreEqual(directSingle[i, c], networkSingle[i, c], 0f, $"Single cell [{i},{c}]."); + Assert.AreEqual(directMulti[i, c], networkMulti[i, c], 0f, $"Multi cell [{i},{c}]."); + } + } + + Assert.Throws(() => network.Solve(9)); + Assert.Throws(() => network.Solve((int[])null!)); + Assert.Throws(() => network.Solve(new[] { 9 })); + } + + /// + /// Custom edge weights are actually applied: raising the short chain's weights reroutes + /// traffic over the bypass. + /// + [TestMethod] + public void SolveCustomWeightsAreApplied() + { + var (edges, destinations) = BuildFixture(); + var network = new Network(edges, destinations); + + // Base network: node 0 routes 0-1-2-4 at cost 3. + var baseTable = network.Solve(4); + Assert.AreEqual(3f, baseTable[0, 2], 0f); + Assert.AreEqual(1f, baseTable[0, 0], 0f); + + // Flood the 0-1 and 1-2 links: node 0 must reroute 0-3-2-4 at cost 11. + var weights = new float[] { 100, 100, 100, 100, 5, 5, 5, 5, 1, 1 }; + var flooded = network.Solve(weights); + Assert.AreEqual(11f, flooded[0, 2], 0f); + Assert.AreEqual(3f, flooded[0, 0], 0f); + + Assert.Throws(() => network.Solve((float[])null!)); + Assert.Throws(() => network.Solve(new float[3])); + } + + /// + /// An infinite custom weight severs its edge: with every route to the destination + /// flooded to infinity, the start node becomes unreachable. + /// + [TestMethod] + public void SolveCustomWeightsInfinityBlocksEdge() + { + var (edges, destinations) = BuildFixture(); + var network = new Network(edges, destinations); + + float inf = float.PositiveInfinity; + // Sever 1->2 and 3->2 (positions 2 and 6): node 0 cannot reach node 4. + var weights = new float[] { 1, 1, inf, 1, 5, 5, inf, 5, 1, 1 }; + var severed = network.Solve(weights); + + Assert.IsTrue(float.IsPositiveInfinity(severed[0, 2])); + Assert.IsTrue(float.IsPositiveInfinity(severed[1, 2])); + Assert.AreEqual(1f, severed[2, 2], 0f); + } + + /// + /// The table-reuse overloads produce cell-identical results to the allocating solves, + /// repeated reuse into the same buffer stays identical, and wrong dimensions throw. + /// + [TestMethod] + public void SolveReuseOverloadIsBitIdentical() + { + var (edges, destinations) = BuildFixture(); + var network = new Network(edges, destinations); + var weights = new float[] { 2, 2, 3, 3, 4, 4, 6, 6, 1, 1 }; + + var allocated = network.Solve(weights); + var reused = new float[network.NodeCount, 3]; + network.Solve(weights, reused); + network.Solve(weights, reused); // steady-state second pass into the same buffer + + var nearestAllocated = network.SolveNearest(); + var nearestReused = new float[network.NodeCount, 3]; + var baseWeights = new float[] { 1, 1, 1, 1, 5, 5, 5, 5, 1, 1 }; + network.SolveNearest(baseWeights, nearestReused); + + for (int i = 0; i < network.NodeCount; i++) + { + for (int c = 0; c < 3; c++) + { + Assert.AreEqual(allocated[i, c], reused[i, c], 0f, $"Reuse cell [{i},{c}]."); + Assert.AreEqual(nearestAllocated[i, c], nearestReused[i, c], 0f, $"Nearest reuse cell [{i},{c}]."); + } + } + + Assert.Throws(() => network.Solve(weights, null!)); + Assert.Throws(() => network.Solve(weights, new float[2, 3])); + Assert.Throws(() => network.SolveNearest(weights, new float[network.NodeCount, 2])); + } + + /// + /// The single-pass nearest solve matches the merged solve costs over the network's + /// destinations. + /// + [TestMethod] + public void SolveNearestMatchesMergedNetworkSolve() + { + var (edges, _) = BuildFixture(); + var network = new Network(edges, new[] { 0, 4 }); + + var merged = network.Solve(new[] { 0, 4 }); + var nearest = network.SolveNearest(); + + for (int i = 0; i < network.NodeCount; i++) + { + Assert.AreEqual(merged[i, 2], nearest[i, 2], 0f, $"Cost mismatch at node {i}."); + } + } + + /// + /// Performance smoke: one thousand custom-weight re-solves through the table-reuse + /// overload on a 100 by 100 grid. + /// + [TestMethod] + [TestCategory("Performance")] + public void P3_RepeatedWeightResolves() + { + const int side = 100; + var edges = new List(); + int index = 0; + for (int r = 0; r < side; r++) + { + for (int c = 0; c < side; c++) + { + int node = r * side + c; + if (c + 1 < side) + { + edges.Add(new Edge(node, node + 1, 1, index++)); + edges.Add(new Edge(node + 1, node, 1, index++)); + } + if (r + 1 < side) + { + edges.Add(new Edge(node, node + side, 1, index++)); + edges.Add(new Edge(node + side, node, 1, index++)); + } + } + } + var network = new Network(edges.ToArray(), new[] { 0, side * side - 1 }); + var weights = new float[edges.Count]; + for (int j = 0; j < weights.Length; j++) weights[j] = 1f; + var table = new float[network.NodeCount, 3]; + + var stopwatch = Stopwatch.StartNew(); + for (int pass = 0; pass < 1000; pass++) + { + weights[pass % weights.Length] = 1f + (pass % 5); + network.SolveNearest(weights, table); + } + stopwatch.Stop(); + Console.WriteLine($"P3 1000 re-solves on {side}x{side}: {stopwatch.Elapsed.TotalSeconds:F3} s ({stopwatch.Elapsed.TotalMilliseconds / 1000.0:F3} ms per solve)"); + + Assert.AreEqual(0f, table[0, 2], 0f); + } + } +} From aa177f9b030226dae7fd9cf58bafc9b8d25ddfcf Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 3 Aug 2026 15:22:11 -0600 Subject: [PATCH 047/222] Reimplement Network.GetPath alternate-path routing Both overloads were inoperative: the removal check binary-searched an integer array for an Edge value and threw at runtime, the drain and path-walk loop conditions were inverted, the full solve re-ran inside the inner loops, node zero doubled as an unreachable sentinel, and one reconstruction read the cost column as a node index. The routing is now a forward Dijkstra over the compiled outgoing adjacency that skips every edge bearing an excluded index, stops at the first settled destination (the nearest), and reconstructs the edge path. The table overload keeps its contract as the fast variant: a recorded route untouched by the exclusions is returned without a solve, since exclusions only remove paths, and severed routes return null or an empty list per each overload's documented convention. The caller's exclusion array is never mutated. A future refinement recorded here: the re-solve could run A-star with the table costs as the heuristic, which is admissible and consistent for the excluded-edge problem. --- .../Optimization/Dynamic/Network.cs | 559 ++++-------------- .../Optimization/Dynamic/Test_Network.cs | 197 ++++++ 2 files changed, 306 insertions(+), 450 deletions(-) diff --git a/Numerics/Mathematics/Optimization/Dynamic/Network.cs b/Numerics/Mathematics/Optimization/Dynamic/Network.cs index 4133e9ca..a1074d65 100644 --- a/Numerics/Mathematics/Optimization/Dynamic/Network.cs +++ b/Numerics/Mathematics/Optimization/Dynamic/Network.cs @@ -350,498 +350,157 @@ private void EnsureScratch() /// /// Finds an alternative path avoiding the specified edges. /// - /// Edge indices to exclude from the path. + /// Edge indices to exclude from the path. The array is not modified; every edge bearing a listed index is excluded. /// The starting node index. - /// A list of edge indices forming the alternative path, or null if no path exists. + /// + /// The ordered edge indices from the start node to its nearest network destination + /// avoiding the excluded edges; an empty list when the start node is itself a + /// destination; null if no path exists. + /// + /// Thrown when the edge indices are null. + /// Thrown when the start node is outside the network. public List? GetPath(int[] edgesToRemove, int startNodeIndex) { - int[] nodeState = new int[_nodeCount]; - float[] nodeWeightToDestination = new float[_nodeCount]; - BinaryHeap heap = new BinaryHeap(100000); + if (edgesToRemove == null) throw new ArgumentNullException(nameof(edgesToRemove)); + if (startNodeIndex < 0 || startNodeIndex >= _nodeCount) + throw new ArgumentOutOfRangeException(nameof(startNodeIndex), $"The start node index must be within [0, {_nodeCount})."); - //backwards Dijkstra - float[,] resultTable = new float[_nodeCount, 3]; - resultTable[startNodeIndex, 0] = startNodeIndex; - resultTable[startNodeIndex, 1] = 0; - resultTable[startNodeIndex, 2] = 0; - nodeState[startNodeIndex] = 1; + var removed = new HashSet(); + for (int i = 0; i < edgesToRemove.Length; i++) removed.Add(edgesToRemove[i]); - int previousValue = startNodeIndex; - int nodeIndex; - bool foundPath = false; - - Array.Sort(edgesToRemove); + return FindDetourPath(removed, startNodeIndex); + } - // Loading up the heap starting from destination - if (_incomingEdges[previousValue] != null) - { - foreach (Edge edge in _incomingEdges[previousValue]) - { - if (Array.BinarySearch(edgesToRemove, edge) < 0) - { - if (previousValue == edge.FromIndex) - { - nodeIndex = edge.ToIndex; - } - else - { - nodeIndex = edge.FromIndex; - } - switch (nodeState[nodeIndex]) - { - case 0: //it has not been scanned yet - BinaryHeap.Node inputNode = new BinaryHeap.Node(edge.Weight, nodeIndex, edge); - heap.Add(inputNode); - nodeState[nodeIndex] = 2; - nodeWeightToDestination[nodeIndex] = inputNode.Weight; - break; - case 1: //do nothing it has already been solved for - break; - case 2: //it has been scanned but not solved - if (nodeWeightToDestination[nodeIndex] > edge.Weight) - { - BinaryHeap.Node inputNode2 = new BinaryHeap.Node(edge.Weight, nodeIndex, edge); - nodeWeightToDestination[nodeIndex] = inputNode2.Weight; - heap.Replace(inputNode2); - } - break; - } - } - } - } - // if n = 0, then no roads to escape to - if (heap.Count == 0) return null!; + /// + /// Finds an alternative path avoiding the specified edges, using a pre-computed results + /// table to skip the solve when the recorded route is unaffected. + /// + /// Edge indices to exclude from the path. The array is not modified; every edge bearing a listed index is excluded. + /// The starting node index. + /// A result table previously solved on this network toward its destinations, without exclusions. + /// + /// The ordered edge indices from the start node to its nearest network destination + /// avoiding the excluded edges; an empty list when the start node is itself a + /// destination or when no path exists. + /// + /// + /// When the table's recorded route from the start node avoids every excluded edge it is + /// returned directly — exclusions only remove paths, so a surviving unexcluded optimum + /// stays optimal. Otherwise the path is re-solved with the exclusions applied. + /// + /// Thrown when the edge indices or table are null. + /// Thrown when the table dimensions are not [, 3]. + /// Thrown when the start node is outside the network. + public List? GetPath(int[] edgesToRemove, int startNodeIndex, float[,] existingResultsTable) + { + if (edgesToRemove == null) throw new ArgumentNullException(nameof(edgesToRemove)); + ValidateResultTable(existingResultsTable); + if (startNodeIndex < 0 || startNodeIndex >= _nodeCount) + throw new ArgumentOutOfRangeException(nameof(startNodeIndex), $"The start node index must be within [0, {_nodeCount})."); - float tempWeight; - int tempIndex; - float FoundDistance = 99999999; - int PotentialToIndex = 0; + // Exclusions only shrink reachability: unreachable without them means unreachable with them. + if (float.IsPositiveInfinity(existingResultsTable[startNodeIndex, 2])) return new List(); - BinaryHeap.Node resultNode; - float cumulativeWeight = 0; + var removed = new HashSet(); + for (int i = 0; i < edgesToRemove.Length; i++) removed.Add(edgesToRemove[i]); - do + List? recorded = Dijkstra.GetPath(existingResultsTable, startNodeIndex); + if (recorded != null) { - resultNode = heap.RemoveMin(); - - if (Solve(startNodeIndex)[resultNode.Index, 0] == 0) continue; - - if (resultNode.Weight + Solve(startNodeIndex)[resultNode.Index, 2] < FoundDistance) + bool blocked = false; + for (int i = 0; i < recorded.Count; i++) { - previousValue = resultNode.Index; - nodeState[resultNode.Index] = 1; - nodeWeightToDestination[resultNode.Index] = resultNode.Weight; - - foreach (Edge edge in _incomingEdges[previousValue]) + if (removed.Contains(recorded[i])) { - if (edge.ToIndex == resultNode.Index) resultTable[resultNode.Index, 0] = edge.FromIndex; - else resultTable[resultNode.Index, 0] = edge.ToIndex; - - resultTable[resultNode.Index, 1] = edge.Index; - resultTable[resultNode.Index, 2] = resultNode.Weight; - - if (Solve(startNodeIndex)[edge.ToIndex, 0] == edge.FromIndex) - { - if (_incomingEdges[previousValue] != null) - { - if (Array.BinarySearch(edgesToRemove, edge) < 0) - { - if (previousValue == edge.FromIndex) nodeIndex = edge.ToIndex; - else nodeIndex = edge.FromIndex; - - switch (nodeState[nodeIndex]) - { - case 0: //has not been scanned yet - cumulativeWeight = edge.Weight + resultNode.Weight; - heap.Add(new BinaryHeap.Node(cumulativeWeight, nodeIndex, edge)); - nodeState[nodeIndex] = 2; - nodeWeightToDestination[nodeIndex] = cumulativeWeight; - break; - case 1: break; - case 2: - if (nodeWeightToDestination[nodeIndex] > cumulativeWeight) - { - nodeWeightToDestination[nodeIndex] = cumulativeWeight; - heap.Replace(new BinaryHeap.Node(cumulativeWeight, nodeIndex, edge)); - } - break; - } - } - } - } - else if (edge.FromIndex != startNodeIndex && Solve(startNodeIndex)[edge.FromIndex, 0] == resultNode.Index) - { - //Already on the lookup table going forwards - if (_incomingEdges[previousValue] != null) - { - foreach (Edge edge2 in _incomingEdges[previousValue]) - { - if (Array.BinarySearch(edgesToRemove, edge2) < 0) - { - if (previousValue == edge2.FromIndex) nodeIndex = edge2.ToIndex; - else nodeIndex = edge2.FromIndex; - - switch (nodeState[nodeIndex]) - { - case 0: - heap.Add(new BinaryHeap.Node(cumulativeWeight, nodeIndex, edge)); - nodeState[nodeIndex] = 2; - nodeWeightToDestination[nodeIndex] = cumulativeWeight; - break; - case 1: break; - case 2: - if (nodeWeightToDestination[nodeIndex] > cumulativeWeight) - { - nodeWeightToDestination[nodeIndex] = cumulativeWeight; - heap.Replace(new BinaryHeap.Node(cumulativeWeight, nodeIndex, edge)); - } - break; - } - } - } - } - } - else - { - //Potential new path, check path viability - tempWeight = Solve(startNodeIndex)[resultNode.Index, 2]; - tempIndex = resultNode.Index; - - do - { - if (Array.BinarySearch(edgesToRemove, (int)Solve(startNodeIndex)[tempIndex, 1]) >= 0) - { - if (_incomingEdges[previousValue] != null) - { - foreach (Edge edge3 in _incomingEdges[previousValue]) - { - if (Array.BinarySearch(edgesToRemove, edge3) < 0) - { - if (previousValue == edge3.FromIndex) nodeIndex = edge3.ToIndex; - else nodeIndex = edge3.FromIndex; - - switch (nodeState[nodeIndex]) - { - case 0: - heap.Add(new BinaryHeap.Node(cumulativeWeight, nodeIndex, edge)); - nodeState[nodeIndex] = 2; - nodeWeightToDestination[nodeIndex] = cumulativeWeight; - break; - case 1: break; - case 2: - if (nodeWeightToDestination[nodeIndex] > cumulativeWeight) - { - nodeWeightToDestination[nodeIndex] = cumulativeWeight; - heap.Replace(new BinaryHeap.Node(cumulativeWeight, nodeIndex, edge)); - } - break; - } - } - } - } - break; - } - tempWeight = Solve(startNodeIndex)[tempIndex, 2]; - tempIndex = (int)Solve(startNodeIndex)[tempIndex, 0]; - } while (tempWeight == 0); - - if (tempWeight == 0) - { - FoundDistance = resultNode.Weight + Solve(startNodeIndex)[resultNode.Index, 2]; - PotentialToIndex = resultNode.Index; - foundPath = true; - } - } + blocked = true; + break; } - } - } while (heap.Count == 0); - - // Check to see if a destination was reached, if so then create a path to the nearest destination - if (foundPath) - { - List UpdatedPath = new List(); - float tempLen = resultTable[PotentialToIndex, 2]; - int tempEdge = (int)resultTable[PotentialToIndex, 1]; - int tempNode = PotentialToIndex; - - while (tempLen == 0) - { - UpdatedPath.Add(tempEdge); - tempNode = (int)resultTable[tempNode, 0]; - tempEdge = (int)resultTable[tempNode, 1]; - tempLen = resultTable[tempNode, 2]; - } - - UpdatedPath.Reverse(); - - tempLen = Solve(startNodeIndex)[PotentialToIndex, 2]; - tempEdge = (int)Solve(startNodeIndex)[PotentialToIndex, 1]; - tempNode = PotentialToIndex; - - while (tempLen == 0) - { - UpdatedPath.Add(tempEdge); - tempNode = (int)Solve(startNodeIndex)[tempNode, 0]; - tempEdge = (int)Solve(startNodeIndex)[tempNode, 1]; - tempLen = Solve(startNodeIndex)[tempNode, 2]; - } - - return UpdatedPath; + if (!blocked) return recorded; } - else return null!; + + return FindDetourPath(removed, startNodeIndex) ?? new List(); } /// - /// Finds an alternative path avoiding the specified edges, using a pre-computed results table. + /// Runs a forward Dijkstra search from the start node over the outgoing adjacency, + /// skipping excluded edges, stopping at the first settled destination (the nearest one), + /// and reconstructing the edge-index path. /// - /// Edge indices to exclude from the path. + /// The excluded edge indices. /// The starting node index. - /// A pre-computed shortest path results table. - /// A list of edge indices forming the alternative path, or an empty list if no path exists. - public List? GetPath(int[] edgesToRemove, int startNodeIndex, float[,] existingResultsTable) + /// The ordered edge indices to the nearest destination; an empty list when the start node is a destination; null when every destination is unreachable. + private List? FindDetourPath(HashSet removed, int startNodeIndex) { - int[] nodeState = new int[_nodeCount]; - float[] nodeWeightToDestination = new float[_nodeCount]; - BinaryHeap heap = new BinaryHeap(100000); - int nodeIndex; - - - //backwards Dijkstra - float[,] resultTable = new float[_nodeCount, 3]; - resultTable[startNodeIndex, 0] = startNodeIndex; - resultTable[startNodeIndex, 1] = 0; - resultTable[startNodeIndex, 2] = 0; - nodeState[startNodeIndex] = 1; + if (_isDestination[startNodeIndex]) return new List(); - int previousValue = startNodeIndex; - bool foundPath = false; - - Array.Sort(edgesToRemove); - - // Loading up the heap starting from destination - if (_incomingEdges[previousValue] != null) + var dist = new float[_nodeCount]; + var state = new int[_nodeCount]; + var previousSlot = new int[_nodeCount]; + for (int i = 0; i < _nodeCount; i++) { - foreach (Edge edge in _incomingEdges[previousValue]) - { - if (Array.BinarySearch(edgesToRemove, edge) < 0) - { - if (previousValue == edge.FromIndex) - { - nodeIndex = edge.ToIndex; - } - else - { - nodeIndex = edge.FromIndex; - } - switch (nodeState[nodeIndex]) - { - case 0: //it has not been scanned yet - BinaryHeap.Node inputNode = new BinaryHeap.Node(edge.Weight, nodeIndex, edge); - heap.Add(inputNode); - nodeState[nodeIndex] = 2; - nodeWeightToDestination[nodeIndex] = inputNode.Weight; - break; - case 1: //do nothing it has already been solved for - break; - case 2: //it has been scanned but not solved - if (nodeWeightToDestination[nodeIndex] > edge.Weight) - { - BinaryHeap.Node inputNode2 = new BinaryHeap.Node(edge.Weight, nodeIndex, edge); - nodeWeightToDestination[nodeIndex] = inputNode2.Weight; - heap.Replace(inputNode2); - } - break; - } - } - } + dist[i] = float.PositiveInfinity; + state[i] = 0; + previousSlot[i] = -1; } + var heap = new IndexedMinHeap(_nodeCount); - //if n = 0 then no roads to escape to - if (heap.Count == 0) return null!; - - float tempWeight; - int tempIndex; - float FoundDistance = 99999999; - int PotentialToIndex = 0; + dist[startNodeIndex] = 0f; + heap.Add(startNodeIndex, 0f); + state[startNodeIndex] = 2; - BinaryHeap.Node resultNode; - float cumulativeWeight = 0; + int[] rowStart = _outgoingAdjacency.RowStart; + int[] toNodes = _outgoingAdjacency.ToNode; + float[] weights = _outgoingAdjacency.Weight; + int[] edgeIndexes = _outgoingAdjacency.EdgeIndex; - do + int reachedDestination = -1; + while (heap.Count > 0) { - resultNode = heap.RemoveMin(); + heap.RemoveMin(out int current, out float cost); + if (state[current] == 1) continue; + state[current] = 1; - if (existingResultsTable[resultNode.Index, 0] == 0) continue; - - if (resultNode.Weight + existingResultsTable[resultNode.Index, 2] < FoundDistance) + if (_isDestination[current]) { - previousValue = resultNode.Index; - nodeState[resultNode.Index] = 1; - nodeWeightToDestination[resultNode.Index] = resultNode.Weight; - - foreach (Edge edge in _incomingEdges[previousValue]) - { - if (edge.ToIndex == resultNode.Index) resultTable[resultNode.Index, 0] = edge.FromIndex; - else resultTable[resultNode.Index, 0] = edge.ToIndex; - - resultTable[resultNode.Index, 1] = edge.Index; - resultTable[resultNode.Index, 2] = resultNode.Weight; - - if (existingResultsTable[edge.ToIndex, 0] == edge.FromIndex) - { - if (_incomingEdges[previousValue] != null) - { - if (Array.BinarySearch(edgesToRemove, edge) < 0) - { - if (previousValue == edge.FromIndex) nodeIndex = edge.ToIndex; - else nodeIndex = edge.FromIndex; - - switch (nodeState[nodeIndex]) - { - case 0: //has not been scanned yet - cumulativeWeight = edge.Weight + resultNode.Weight; - heap.Add(new BinaryHeap.Node(cumulativeWeight, nodeIndex, edge)); - nodeState[nodeIndex] = 2; - nodeWeightToDestination[nodeIndex] = cumulativeWeight; - break; - case 1: break; - case 2: - if (nodeWeightToDestination[nodeIndex] > cumulativeWeight) - { - nodeWeightToDestination[nodeIndex] = cumulativeWeight; - heap.Replace(new BinaryHeap.Node(cumulativeWeight, nodeIndex, edge)); - } - break; - } - } - } - } - } + reachedDestination = current; + break; } - else if (heap.Count != 0) + + int rowEnd = rowStart[current + 1]; + for (int k = rowStart[current]; k < rowEnd; k++) { - foreach (Edge edge in _incomingEdges[previousValue]) + if (removed.Contains(edgeIndexes[k])) continue; + int to = toNodes[k]; + float newCost = cost + weights[k]; + if (newCost < dist[to]) { - if (existingResultsTable[edge.FromIndex, 0] == resultNode.Index) + dist[to] = newCost; + if (state[to] != 2) { - if (_incomingEdges[previousValue] != null) - { - if (Array.BinarySearch(edgesToRemove, edge) < 0) - { - if (previousValue == edge.FromIndex) nodeIndex = edge.ToIndex; - else nodeIndex = edge.FromIndex; - - switch (nodeState[nodeIndex]) - { - case 0: //has not been scanned yet - cumulativeWeight = edge.Weight + resultNode.Weight; - heap.Add(new BinaryHeap.Node(cumulativeWeight, nodeIndex, edge)); - nodeState[nodeIndex] = 2; - nodeWeightToDestination[nodeIndex] = cumulativeWeight; - break; - case 1: break; - case 2: - if (nodeWeightToDestination[nodeIndex] > cumulativeWeight) - { - nodeWeightToDestination[nodeIndex] = cumulativeWeight; - heap.Replace(new BinaryHeap.Node(cumulativeWeight, nodeIndex, edge)); - } - break; - } - } - } + heap.Add(to, newCost); + state[to] = 2; } - } - } - else - { - // check viability of route - tempWeight = existingResultsTable[resultNode.Index, 2]; - tempIndex = resultNode.Index; - - do - { - // check to see if the current route has a blocked segment - if (Array.BinarySearch(edgesToRemove, (int)existingResultsTable[tempIndex, 1]) >= 0) + else { - if (_incomingEdges[previousValue] != null) - { - foreach (Edge edge in _incomingEdges[previousValue]) - { - if (previousValue == edge.FromIndex) nodeIndex = edge.ToIndex; - else nodeIndex = edge.FromIndex; - - switch (nodeState[nodeIndex]) - { - case 0: //has not been scanned yet - cumulativeWeight = edge.Weight + resultNode.Weight; - heap.Add(new BinaryHeap.Node(cumulativeWeight, nodeIndex, edge)); - nodeState[nodeIndex] = 2; - nodeWeightToDestination[nodeIndex] = cumulativeWeight; - break; - case 1: break; - case 2: - if (nodeWeightToDestination[nodeIndex] > cumulativeWeight) - { - nodeWeightToDestination[nodeIndex] = cumulativeWeight; - heap.Replace(new BinaryHeap.Node(cumulativeWeight, nodeIndex, edge)); - } - break; - } - } - } + heap.DecreaseKey(to, newCost); } - tempWeight = existingResultsTable[tempIndex, 2]; - tempIndex = (int)existingResultsTable[tempIndex, 0]; - } while (tempWeight == 0); - - if (tempWeight == 0) - { - FoundDistance = resultNode.Weight + existingResultsTable[resultNode.Index, 2]; - PotentialToIndex = resultNode.Index; - foundPath = true; + previousSlot[to] = k; } } - } while (heap.Count == 0); - - // Check to see if the destination was reached, if so then create a path to the nearest destination - if (foundPath) - { - List updatedPath = new List(); - float tempLen = resultTable[PotentialToIndex, 2]; - int tempEdge = (int)resultTable[PotentialToIndex, 1]; - int tempNode = PotentialToIndex; - - while (tempLen == 0) - { - updatedPath.Add(tempEdge); - tempNode = (int)resultTable[tempNode, 0]; - tempEdge = (int)resultTable[tempNode, 1]; - tempLen = resultTable[tempNode, 2]; - } - - // updatedPath.Add(startingEdge); - updatedPath.Reverse(); - - tempLen = existingResultsTable[PotentialToIndex, 2]; - tempEdge = (int)existingResultsTable[PotentialToIndex, 1]; - tempNode = PotentialToIndex; - - while (tempLen == 0) - { - updatedPath.Add(tempEdge); - tempNode = (int)existingResultsTable[tempNode, 2]; - tempEdge = (int)existingResultsTable[tempNode, 1]; - tempLen = existingResultsTable[tempNode, 2]; - } - - return updatedPath; } - else + if (reachedDestination < 0) return null; + + var path = new List(); + int node = reachedDestination; + while (node != startNodeIndex) { - return new List(); + int slot = previousSlot[node]; + path.Add(edgeIndexes[slot]); + node = _outgoingAdjacency.FromNode[slot]; } + path.Reverse(); + return path; } } } diff --git a/Test_Numerics/Mathematics/Optimization/Dynamic/Test_Network.cs b/Test_Numerics/Mathematics/Optimization/Dynamic/Test_Network.cs index c073efa6..9b94e939 100644 --- a/Test_Numerics/Mathematics/Optimization/Dynamic/Test_Network.cs +++ b/Test_Numerics/Mathematics/Optimization/Dynamic/Test_Network.cs @@ -228,6 +228,203 @@ public void SolveNearestMatchesMergedNetworkSolve() } } + /// + /// With nothing removed, GetPath returns the exact unblocked edge sequence to the + /// nearest destination through both overloads. + /// + [TestMethod] + public void GetPathHappyPath() + { + var (edges, destinations) = BuildFixture(); + var network = new Network(edges, destinations); + + var direct = network.GetPath(new int[0], 0); + Assert.IsNotNull(direct); + CollectionAssert.AreEqual(new List { 0, 1, 4 }, direct); + + var table = network.Solve(destinations); + var viaTable = network.GetPath(new int[0], 0, table); + Assert.IsNotNull(viaTable); + CollectionAssert.AreEqual(new List { 0, 1, 4 }, viaTable); + } + + /// + /// Removing an edge index on the recorded route forces the detour over the bypass, and + /// every edge bearing the removed index is excluded. + /// + [TestMethod] + public void GetPathDetoursAroundRemovedEdges() + { + var (edges, destinations) = BuildFixture(); + var network = new Network(edges, destinations); + + var detour = network.GetPath(new[] { 1 }, 0); + Assert.IsNotNull(detour); + CollectionAssert.AreEqual(new List { 2, 3, 4 }, detour); + + var table = network.Solve(destinations); + var detourViaTable = network.GetPath(new[] { 1 }, 0, table); + Assert.IsNotNull(detourViaTable); + CollectionAssert.AreEqual(new List { 2, 3, 4 }, detourViaTable); + } + + /// + /// When every route is severed, the first overload returns null and the second returns + /// an empty list, per their respective contracts. + /// + [TestMethod] + public void GetPathReturnsNullOrEmptyWhenSevered() + { + var (edges, destinations) = BuildFixture(); + var network = new Network(edges, destinations); + + Assert.IsNull(network.GetPath(new[] { 1, 3 }, 0)); + + var table = network.Solve(destinations); + var severed = network.GetPath(new[] { 1, 3 }, 0, table); + Assert.IsNotNull(severed); + Assert.AreEqual(0, severed!.Count); + } + + /// + /// Starting at a destination returns an empty path through both overloads. + /// + [TestMethod] + public void GetPathStartIsDestinationReturnsEmpty() + { + var (edges, destinations) = BuildFixture(); + var network = new Network(edges, destinations); + + var direct = network.GetPath(new[] { 1 }, 4); + Assert.IsNotNull(direct); + Assert.AreEqual(0, direct!.Count); + + var table = network.Solve(destinations); + var viaTable = network.GetPath(new[] { 1 }, 4, table); + Assert.IsNotNull(viaTable); + Assert.AreEqual(0, viaTable!.Count); + } + + /// + /// The caller's removal array is never sorted or otherwise mutated. + /// + [TestMethod] + public void GetPathDoesNotMutateEdgesToRemove() + { + var (edges, destinations) = BuildFixture(); + var network = new Network(edges, destinations); + + var removals = new[] { 4, 1, 3 }; + network.GetPath(removals, 0); + CollectionAssert.AreEqual(new[] { 4, 1, 3 }, removals); + } + + /// + /// A valid route passing through node zero is returned — zero is a real node index, + /// not an unreachable sentinel. + /// + [TestMethod] + public void GetPathThroughNodeZero() + { + var edges = new[] + { + new Edge(1, 0, 1, 0), + new Edge(0, 2, 1, 1), + }; + var network = new Network(edges, new[] { 2 }); + + var path = network.GetPath(new int[0], 1); + Assert.IsNotNull(path); + CollectionAssert.AreEqual(new List { 0, 1 }, path); + } + + /// + /// With several destinations, GetPath routes to the nearest one, and an exact tie + /// resolves deterministically across repeated calls. + /// + [TestMethod] + public void GetPathPicksNearestDestination() + { + // Chain 0-1-2-3 with destinations 0 and 3: node 1 is nearer to 0. + var edges = new[] + { + new Edge(1, 0, 1, 0), + new Edge(0, 1, 1, 0), + new Edge(1, 2, 1, 1), + new Edge(2, 1, 1, 1), + new Edge(2, 3, 1, 2), + new Edge(3, 2, 1, 2), + }; + var network = new Network(edges, new[] { 0, 3 }); + + var fromOne = network.GetPath(new int[0], 1); + Assert.IsNotNull(fromOne); + CollectionAssert.AreEqual(new List { 0 }, fromOne); + + // The middle of an even chain ties exactly; the choice must repeat. + var tieNetwork = new Network(new[] + { + new Edge(1, 0, 1, 0), + new Edge(1, 2, 1, 1), + }, new[] { 0, 2 }); + var first = tieNetwork.GetPath(new int[0], 1); + var second = tieNetwork.GetPath(new int[0], 1); + Assert.IsNotNull(first); + Assert.AreEqual(1, first!.Count); + CollectionAssert.AreEqual(first, second); + } + + /// + /// Removing the only edge index into the destination severs the route even though two + /// directed edges bear that index. + /// + [TestMethod] + public void GetPathHandlesDuplicateEdgeIndices() + { + var (edges, destinations) = BuildFixture(); + var network = new Network(edges, destinations); + + Assert.IsNull(network.GetPath(new[] { 4 }, 0)); + } + + /// + /// The table fast path returns the identical route to the full solve when the removals + /// miss the recorded route. + /// + [TestMethod] + public void GetPathFastPathMatchesFullSolve() + { + var (edges, destinations) = BuildFixture(); + var network = new Network(edges, destinations); + var table = network.Solve(destinations); + + // Removing the bypass leaves the recorded route 0-1-2-4 untouched. + var viaTable = network.GetPath(new[] { 3 }, 0, table); + var direct = network.GetPath(new[] { 3 }, 0); + Assert.IsNotNull(viaTable); + Assert.IsNotNull(direct); + CollectionAssert.AreEqual(direct, viaTable); + } + + /// + /// GetPath rejects null inputs, out-of-range start nodes, and wrong table dimensions + /// with clear argument exceptions. + /// + [TestMethod] + public void GetPathValidationThrows() + { + var (edges, destinations) = BuildFixture(); + var network = new Network(edges, destinations); + var table = network.Solve(destinations); + + Assert.Throws(() => network.GetPath(null!, 0)); + Assert.Throws(() => network.GetPath(new int[0], 9)); + Assert.Throws(() => network.GetPath(null!, 0, table)); + Assert.Throws(() => network.GetPath(new int[0], 0, null!)); + Assert.Throws(() => network.GetPath(new int[0], 0, new float[2, 3])); + Assert.Throws(() => network.GetPath(new int[0], 9, table)); + } + /// /// Performance smoke: one thousand custom-weight re-solves through the table-reuse /// overload on a 100 by 100 grid. From c361f2864428a98a33d6072ffa9bc11ac360839d Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 3 Aug 2026 15:23:23 -0600 Subject: [PATCH 048/222] Document the shortest-path cluster The optimization page gains the Dijkstra section: the three-column routing-table contract with its sentinels, the weight conventions (non-negative precondition, infinite-weight impassable encoding, NaN severing), the single-pass nearest-destination query, the table walkers, and the compiled-network pattern for per-timestep weight updates in evacuation modeling. --- docs/mathematics/optimization.md | 82 ++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/docs/mathematics/optimization.md b/docs/mathematics/optimization.md index 433d24fb..d23c0e56 100644 --- a/docs/mathematics/optimization.md +++ b/docs/mathematics/optimization.md @@ -451,6 +451,86 @@ Console.WriteLine($"Constrained optimum: [{constrained.BestParameterSet.Values[0 $"{constrained.BestParameterSet.Values[1]:F4}]"); ``` +## Shortest Path (Dijkstra) + +The `Dijkstra`, `Network`, `Edge`, and `BinaryHeap` types solve destination-rooted shortest +paths over directed networks [9] — the routing kernel for agent-based evacuation modeling, where +every agent needs its route to the nearest destination and edge costs (travel times) change as +conditions evolve. + +### The result table + +Every solver returns one `float[,]` routing table with a row per node and three columns: + +| Column | Contents | +|--------|----------| +| `[i, 0]` | The next node toward the destination | +| `[i, 1]` | The index of the edge to take (`Edge.Index`) | +| `[i, 2]` | The cumulative cost to the destination | + +The destination row is `(itself, -1, 0)`; an unreachable node's row is `(-1, -1, +∞)`. +`Dijkstra.PathExists` tests reachability, and `Dijkstra.GetPath` / `Dijkstra.TryGetPath` walk a +table into the ordered edge-index list with the total cost. + +Weight conventions: Dijkstra's algorithm assumes **non-negative weights** (negative weights are +not rejected, but routes computed from them are undefined); a weight of **positive infinity +makes an edge impassable** — the natural encoding for a flooded road segment — and a NaN weight +never relaxes, severing its edge. + +```cs +using Numerics.Mathematics.Optimization; + +var edges = new List +{ + new Edge(0, 1, 2.0f, 0), // from, to, weight (e.g. travel time), edge index + new Edge(1, 2, 1.5f, 1), + new Edge(0, 2, 5.0f, 2), +}; + +// Route every node to node 2. +float[,] table = Dijkstra.Solve(edges, destinationIndex: 2); + +// Route every node to the nearest of several destinations in ONE pass. +float[,] nearest = Dijkstra.SolveNearest(edges, new[] { 0, 2 }); + +// Walk a route out of the table. +if (Dijkstra.TryGetPath(table, startNodeIndex: 0, out List route, out float cost)) + Console.WriteLine($"Route: {string.Join(" -> ", route)}, cost {cost:F2}"); +``` + +`Dijkstra.Solve(edges, destinationIndices, ...)` (the multi-destination overload) solves each +destination independently and keeps each node's strictly cheapest route, so on an exact cost tie +the earlier destination in the array wins. `SolveNearest` produces the same costs in a single +multi-source pass and is the faster form of the nearest-destination query. + +### Compiled networks and time-varying weights + +`Network` compiles a fixed topology once — node count, incoming and outgoing adjacency, and the +destination set — so a simulation can re-solve every time step against updated edge weights with +no rebuild. Weights are positional with the constructor's edge array, and the table-reuse +overloads run with zero steady-state allocation (they share instance scratch buffers and are not +thread safe; the allocating overloads remain safe for concurrent use): + +```cs +var network = new Network(edgeArray, destinationIndices); +var table = new float[network.NodeCount, 3]; +var weights = new float[edgeArray.Length]; + +for (int t = 0; t < timeSteps; t++) +{ + UpdateTravelTimes(weights, t); // flooded edges -> float.PositiveInfinity + network.SolveNearest(weights, table); // zero-allocation re-solve + RouteAgents(table); // each agent walks its row +} + +// Detour routing around blocked segments, splicing onto the precomputed table when possible. +List? detour = network.GetPath(blockedEdgeIndices, agentNodeIndex, table); +``` + +`Network.GetPath` finds the cheapest route to the nearest destination that avoids every edge +bearing an excluded edge index; when the precomputed table's recorded route is untouched by the +exclusions it is returned directly, with no solve. + ## Practical Example: Calibrating a Hydrological Model A complete example of using optimization to calibrate a watershed model: @@ -644,6 +724,8 @@ else if (optimizer.Status == OptimizationStatus.MaximumIterationsReached) [8] Birgin, E. G., & Martínez, J. M. (2014). *Practical Augmented Lagrangian Methods for Constrained Optimization*. SIAM. +[9] Dijkstra, E. W. (1959). A note on two problems in connexion with graphs. *Numerische Mathematik*, 1, 269-271. + --- [← Previous: Numerical Differentiation](differentiation.md) | [Back to Index](../index.md) | [Next: Root Finding →](root-finding.md) From e57af201b011f765dd8ad13638db9f8c9eba0fd8 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 3 Aug 2026 17:32:09 -0600 Subject: [PATCH 049/222] Preserve log distribution base when cloning --- Numerics/Distributions/Univariate/LogNormal.cs | 4 ++-- .../Distributions/Univariate/Test_LogNormal.cs | 17 +++++++++++++++++ .../Univariate/Test_LogPearsonTypeIII.cs | 18 ++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/Numerics/Distributions/Univariate/LogNormal.cs b/Numerics/Distributions/Univariate/LogNormal.cs index 86c80da9..577e99a1 100644 --- a/Numerics/Distributions/Univariate/LogNormal.cs +++ b/Numerics/Distributions/Univariate/LogNormal.cs @@ -590,7 +590,7 @@ public override double InverseCDF(double probability) /// public override UnivariateDistributionBase Clone() { - return new LogNormal(Mu, Sigma); + return new LogNormal(Mu, Sigma) { Base = Base }; } /// @@ -674,4 +674,4 @@ public double[] QuantileGradient(double probability) } -} \ No newline at end of file +} diff --git a/Test_Numerics/Distributions/Univariate/Test_LogNormal.cs b/Test_Numerics/Distributions/Univariate/Test_LogNormal.cs index a33b0636..0cf6bae5 100644 --- a/Test_Numerics/Distributions/Univariate/Test_LogNormal.cs +++ b/Test_Numerics/Distributions/Univariate/Test_LogNormal.cs @@ -163,6 +163,23 @@ public void Test_Construction() Assert.AreEqual(1,LogN2.Sigma); } + /// + /// Verifies cloning preserves the configured logarithm base and resulting distribution. + /// + [TestMethod] + public void Test_Clone_PreservesBase() + { + var source = new LogNormal(4.2d, 0.4d) { Base = Math.E }; + + var clone = (LogNormal)source.Clone(); + + Assert.AreNotSame(source, clone); + Assert.AreEqual(source.Mu, clone.Mu, 0d); + Assert.AreEqual(source.Sigma, clone.Sigma, 0d); + Assert.AreEqual(source.Base, clone.Base, 0d); + Assert.AreEqual(source.CDF(75d), clone.CDF(75d), 0d); + } + /// /// Testing Log-Normal with bad parameters. /// diff --git a/Test_Numerics/Distributions/Univariate/Test_LogPearsonTypeIII.cs b/Test_Numerics/Distributions/Univariate/Test_LogPearsonTypeIII.cs index a59e223b..3fc6cf36 100644 --- a/Test_Numerics/Distributions/Univariate/Test_LogPearsonTypeIII.cs +++ b/Test_Numerics/Distributions/Univariate/Test_LogPearsonTypeIII.cs @@ -26,6 +26,24 @@ namespace Distributions.Univariate public class Test_LogPearsonTypeIII { + /// + /// Verifies cloning preserves the configured logarithm base and resulting distribution. + /// + [TestMethod] + public void Test_Clone_PreservesBase() + { + var source = new LogPearsonTypeIII(4.2d, 0.4d, 0.25d) { Base = Math.E }; + + var clone = (LogPearsonTypeIII)source.Clone(); + + Assert.AreNotSame(source, clone); + Assert.AreEqual(source.Mu, clone.Mu, 0d); + Assert.AreEqual(source.Sigma, clone.Sigma, 0d); + Assert.AreEqual(source.Gamma, clone.Gamma, 0d); + Assert.AreEqual(source.Base, clone.Base, 0d); + Assert.AreEqual(source.CDF(75d), clone.CDF(75d), 0d); + } + // Reference: "The Gamma Family and Derived Distributions Applied in Hydrology", B. Bobee & F. Ashkar, Water Resources Publications, 1991. // Table 1.2 Maximum annual peak discharge values in cms, observed at the Harricana River at Amos (Quebec, Canada) private double[] sample = new double[] { 122d, 244d, 214d, 173d, 229d, 156d, 212d, 263d, 146d, 183d, 161d, 205d, 135d, 331d, 225d, 174d, 98.8d, 149d, 238d, 262d, 132d, 235d, 216d, 240d, 230d, 192d, 195d, 172d, 173d, 172d, 153d, 142d, 317d, 161d, 201d, 204d, 194d, 164d, 183d, 161d, 167d, 179d, 185d, 117d, 192d, 337d, 125d, 166d, 99.1d, 202d, 230d, 158d, 262d, 154d, 164d, 182d, 164d, 183d, 171d, 250d, 184d, 205d, 237d, 177d, 239d, 187d, 180d, 173d, 174d }; From e92c6a3059d8ec8ffe620669aa3e526a756eb817 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 3 Aug 2026 17:45:37 -0600 Subject: [PATCH 050/222] Satisfy the MSTEST0037 collection-assert analyzer in the Dynamic tests The MSTest collection/comparison analyzer began flagging fifteen asserts in Test_IndexedMinHeap, Test_Network, and Test_ShortestPath on the net481 build leg. Replace Assert.IsTrue comparisons with IsLessThanOrEqualTo / IsGreaterThanOrEqualTo and Count/Length equality asserts with HasCount / IsEmpty. Assertion semantics are unchanged; the scoped Dynamic classes remain 63/63 green. --- .../Dynamic/Test_IndexedMinHeap.cs | 4 ++-- .../Optimization/Dynamic/Test_Network.cs | 20 +++++++++---------- .../Optimization/Dynamic/Test_ShortestPath.cs | 6 +++--- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Test_Numerics/Mathematics/Optimization/Dynamic/Test_IndexedMinHeap.cs b/Test_Numerics/Mathematics/Optimization/Dynamic/Test_IndexedMinHeap.cs index c4e0673c..e8ee4718 100644 --- a/Test_Numerics/Mathematics/Optimization/Dynamic/Test_IndexedMinHeap.cs +++ b/Test_Numerics/Mathematics/Optimization/Dynamic/Test_IndexedMinHeap.cs @@ -100,7 +100,7 @@ public void IndexedHeapCapacityInvariant() { weights[node] = (float)randy.NextDouble(); heap.Add(node, weights[node]); - Assert.IsTrue(heap.Count <= capacity); + Assert.IsLessThanOrEqualTo(capacity, heap.Count); } Assert.AreEqual(capacity, heap.Count); Assert.Throws(() => heap.Add(0, 0f)); @@ -117,7 +117,7 @@ public void IndexedHeapCapacityInvariant() for (int i = 0; i < capacity; i++) { heap.RemoveMin(out _, out float weight); - Assert.IsTrue(weight >= previous, "The drain must be non-decreasing."); + Assert.IsGreaterThanOrEqualTo(previous, weight, "The drain must be non-decreasing."); previous = weight; } Assert.AreEqual(0, heap.Count); diff --git a/Test_Numerics/Mathematics/Optimization/Dynamic/Test_Network.cs b/Test_Numerics/Mathematics/Optimization/Dynamic/Test_Network.cs index 9b94e939..ab66f2a2 100644 --- a/Test_Numerics/Mathematics/Optimization/Dynamic/Test_Network.cs +++ b/Test_Numerics/Mathematics/Optimization/Dynamic/Test_Network.cs @@ -50,10 +50,10 @@ public void CtorBuildsAdjacencyForNonEmptyEdges() var network = new Network(edges, destinations); Assert.AreEqual(5, network.NodeCount); - Assert.AreEqual(5, network.IncomingEdges.Length); - Assert.AreEqual(5, network.OutgoingEdges.Length); - Assert.AreEqual(2, network.OutgoingEdges[0].Count); // 0->1 and 0->3 - Assert.AreEqual(1, network.IncomingEdges[4].Count); + Assert.HasCount(5, network.IncomingEdges); + Assert.HasCount(5, network.OutgoingEdges); + Assert.HasCount(2, network.OutgoingEdges[0]); // 0->1 and 0->3 + Assert.HasCount(1, network.IncomingEdges[4]); Assert.AreEqual(2, network.IncomingEdges[4][0].FromIndex); CollectionAssert.AreEqual(new[] { 4 }, network.DestinationIndices); @@ -61,9 +61,9 @@ public void CtorBuildsAdjacencyForNonEmptyEdges() // instead verify a node with no incoming edges gets an empty list, not null. var oneWay = new Network(new[] { new Edge(0, 1, 1, 0) }, new[] { 1 }); Assert.IsNotNull(oneWay.IncomingEdges[0]); - Assert.AreEqual(0, oneWay.IncomingEdges[0].Count); + Assert.IsEmpty(oneWay.IncomingEdges[0]); Assert.IsNotNull(oneWay.OutgoingEdges[1]); - Assert.AreEqual(0, oneWay.OutgoingEdges[1].Count); + Assert.IsEmpty(oneWay.OutgoingEdges[1]); } /// @@ -283,7 +283,7 @@ public void GetPathReturnsNullOrEmptyWhenSevered() var table = network.Solve(destinations); var severed = network.GetPath(new[] { 1, 3 }, 0, table); Assert.IsNotNull(severed); - Assert.AreEqual(0, severed!.Count); + Assert.IsEmpty(severed!); } /// @@ -297,12 +297,12 @@ public void GetPathStartIsDestinationReturnsEmpty() var direct = network.GetPath(new[] { 1 }, 4); Assert.IsNotNull(direct); - Assert.AreEqual(0, direct!.Count); + Assert.IsEmpty(direct!); var table = network.Solve(destinations); var viaTable = network.GetPath(new[] { 1 }, 4, table); Assert.IsNotNull(viaTable); - Assert.AreEqual(0, viaTable!.Count); + Assert.IsEmpty(viaTable!); } /// @@ -370,7 +370,7 @@ public void GetPathPicksNearestDestination() var first = tieNetwork.GetPath(new int[0], 1); var second = tieNetwork.GetPath(new int[0], 1); Assert.IsNotNull(first); - Assert.AreEqual(1, first!.Count); + Assert.HasCount(1, first!); CollectionAssert.AreEqual(first, second); } diff --git a/Test_Numerics/Mathematics/Optimization/Dynamic/Test_ShortestPath.cs b/Test_Numerics/Mathematics/Optimization/Dynamic/Test_ShortestPath.cs index 9a411c0a..56f5a2ec 100644 --- a/Test_Numerics/Mathematics/Optimization/Dynamic/Test_ShortestPath.cs +++ b/Test_Numerics/Mathematics/Optimization/Dynamic/Test_ShortestPath.cs @@ -605,10 +605,10 @@ public void GetPathStartAtDestinationReturnsEmpty() var path = Dijkstra.GetPath(result, 1); Assert.IsNotNull(path); - Assert.AreEqual(0, path!.Count); + Assert.IsEmpty(path!); Assert.IsTrue(Dijkstra.TryGetPath(result, 1, out var tryPath, out float cost)); - Assert.AreEqual(0, tryPath.Count); + Assert.IsEmpty(tryPath); Assert.AreEqual(0f, cost, 0f); } @@ -622,7 +622,7 @@ public void GetPathUnreachableReturnsNull() Assert.IsNull(Dijkstra.GetPath(result, 2)); Assert.IsFalse(Dijkstra.TryGetPath(result, 2, out var path, out float cost)); - Assert.AreEqual(0, path.Count); + Assert.IsEmpty(path); Assert.IsTrue(float.IsPositiveInfinity(cost)); } From 2530f9196035118a5e6aa4bd4cb68a44a99a6036 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 3 Aug 2026 17:46:05 -0600 Subject: [PATCH 051/222] Add forward conditional and scalar inverse-conditional copula functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add ConditionalCDF(u, v) to BivariateCopula: the forward conditional h-function h(v|u) = dC(u,v)/du on non-exceedance probabilities, virtual with a clamped central-finite-difference fallback over CDF (eps = 1e-6) so external subclasses keep working, and overridden analytically by every shipped family — the generic Archimedean ratio GeneratorPrime(u)/GeneratorPrime(C(u,v)) (per-family closed forms recorded in the override remarks), the Gaussian and Student-t elliptical conditionals, with parameter validation matching the existing compute methods. Add the scalar, non-allocating InverseConditionalCDF(u, t) and recompose every family's double[] InverseCDF(u, v) on top of it as [u, scalar]. Every existing override slot and signature is preserved; the moved bodies are arithmetic- identical, so conditional simulation values do not change — with one deliberate exception: FrankCopula's conditional inversion for theta > 0 reflected the output of the negative-theta evaluation without complementing the input conditional probability, so it returned the inverse at 1 - t: for theta = 4, InverseCDF(0.3, 0.7) gave v = 0.21481 with h(v|0.3) = 0.30 instead of 0.70. Sampling was distributionally unaffected (t and 1 - t are equally uniform), which is why simulation never surfaced it, but the value violates the conditional-inverse contract h(v|u) = t. The reflection identity h_theta(v|u) = 1 - h_{-theta}(1-v|u) requires the negative-theta evaluation to receive 1 - t; with the complement in place the inverse matches pyvinecopulib 0.7.6 hinv1 to 1e-8 on both dependency branches and the round trip closes to 1e-10. Fixed-seed Frank simulation streams with theta > 0 draw different (distributionally identical) pairs; no committed test pinned such values. Every family gains Test_ConditionalCDF and Test_InverseConditionalCDF: reference pins computed with the Python package pyvinecopulib 0.7.6 (Bicop.hfunc1/hinv1), cross-checked at generation against 50-digit numerical dC/du of each family's CDF with mpmath 1.4.1; the Ali-Mikhail-Haq family (not in pyvinecopulib) is pinned directly against the mpmath derivative and root solve. In-test cross-checks cover the central finite difference of CDF, the exact upper edge h(1|u) = 1, the round trip, and bit-identical recomposition of the array form over the scalar. Tolerances are derived per assert in the test docs. The scoped copula classes are 89/89 green on net10.0. --- .../Bivariate Copulas/AMHCopula.cs | 19 +++-- .../Base/ArchimedeanCopula.cs | 46 +++++++++-- .../Bivariate Copulas/Base/BivariateCopula.cs | 43 +++++++++++ .../Bivariate Copulas/ClaytonCopula.cs | 15 +++- .../Bivariate Copulas/FrankCopula.cs | 23 +++++- .../Bivariate Copulas/GumbelCopula.cs | 20 +++-- .../Bivariate Copulas/JoeCopula.cs | 21 +++-- .../Bivariate Copulas/NormalCopula.cs | 31 +++++++- .../Bivariate Copulas/StudentTCopula.cs | 48 ++++++++++-- .../Bivariate Copulas/Test_AMHCopula.cs | 74 ++++++++++++++++++ .../Bivariate Copulas/Test_ClaytonCopula.cs | 72 +++++++++++++++++ .../Bivariate Copulas/Test_FrankCopula.cs | 73 ++++++++++++++++++ .../Bivariate Copulas/Test_GumbelCopula.cs | 72 +++++++++++++++++ .../Bivariate Copulas/Test_JoeCopula.cs | 62 +++++++++++++++ .../Bivariate Copulas/Test_NormalCopula.cs | 77 +++++++++++++++++++ .../Bivariate Copulas/Test_StudentTCopula.cs | 58 ++++++++++++++ 16 files changed, 713 insertions(+), 41 deletions(-) diff --git a/Numerics/Distributions/Bivariate Copulas/AMHCopula.cs b/Numerics/Distributions/Bivariate Copulas/AMHCopula.cs index c236a875..0dccf6ef 100644 --- a/Numerics/Distributions/Bivariate Copulas/AMHCopula.cs +++ b/Numerics/Distributions/Bivariate Copulas/AMHCopula.cs @@ -143,19 +143,28 @@ public override double PDF(double u, double v) } /// - public override double[] InverseCDF(double u, double v) + /// + /// Uses the Ali-Mikhail-Haq closed-form conditional inversion, which solves the + /// quadratic of Johnson (1987, p.362) in 1 − v. + /// + public override double InverseConditionalCDF(double u, double t) { // Validate parameters if (_parametersValid == false) ValidateParameter(Theta, true); //Johnson (1987, p.362). - double w = v; + double w = t; double b = 1d - u; double A = w * Math.Pow(Theta * b, 2) - Theta; double B = Theta + 1d - 2d * Theta * b * w; double C = w - 1d; - v = (-B + Math.Sqrt(B * B - 4d * A * C)) / 2d / A; - v = 1d - v; - return [u, v]; + double v = (-B + Math.Sqrt(B * B - 4d * A * C)) / 2d / A; + return 1d - v; + } + + /// + public override double[] InverseCDF(double u, double v) + { + return [u, InverseConditionalCDF(u, v)]; } /// diff --git a/Numerics/Distributions/Bivariate Copulas/Base/ArchimedeanCopula.cs b/Numerics/Distributions/Bivariate Copulas/Base/ArchimedeanCopula.cs index cf887913..dd62bbea 100644 --- a/Numerics/Distributions/Bivariate Copulas/Base/ArchimedeanCopula.cs +++ b/Numerics/Distributions/Bivariate Copulas/Base/ArchimedeanCopula.cs @@ -106,24 +106,54 @@ public override double CDF(double u, double v) return GeneratorInverse(Generator(u) + Generator(v)); } + /// + /// + /// + /// For an Archimedean copula with generator φ, differentiating C(u,v) = φ⁻¹(φ(u) + φ(v)) + /// with respect to u gives the exact generic conditional h(v|u) = φ′(u) / φ′(C(u,v)). + /// The ratio is insensitive to a family's internal sign convention for φ′. + /// + /// + /// Per family the ratio reduces to the closed forms: + /// Clayton h = u^(−θ−1)·(u^(−θ) + v^(−θ) − 1)^(−1−1/θ); + /// Frank h = e^(−θu)(e^(−θv) − 1) / [(e^(−θ) − 1) + (e^(−θu) − 1)(e^(−θv) − 1)]; + /// Gumbel h = C(u,v)·A^(1/θ−1)·(−ln u)^(θ−1)/u with A = (−ln u)^θ + (−ln v)^θ; + /// Joe h = (1−u)^(θ−1)·[1 − (1−v)^θ]·A^(1/θ−1) with A = (1−u)^θ + (1−v)^θ − (1−u)^θ(1−v)^θ; + /// Ali-Mikhail-Haq h = v(1 − θ(1−v))/D² with D = 1 − θ(1−u)(1−v). + /// + /// + public override double ConditionalCDF(double u, double v) + { + // Validate parameters + if (_parametersValid == false) ValidateParameter(Theta, true); + return GeneratorPrime(u) / GeneratorPrime(CDF(u, v)); + } + /// /// /// This method is based on Genest et al. 1986 - /// 1) Two independent uniformly distributed U(0,1) random variates, u and v, are generated. - /// 2) Two new variables, s and w, are obtained as s = GeneratorPrime(u) / v and w = GeneratorPrimeInverse(s). - /// 3) Another variable v is obtained as v = GeneratorInverse(Generator(w) - Generator(u)) + /// 1) Two independent uniformly distributed U(0,1) random variates, u and t, are generated. + /// 2) Two new variables, s and w, are obtained as s = GeneratorPrime(u) / t and w = GeneratorPrimeInverse(s). + /// 3) The dependent variate is obtained as v = GeneratorInverse(Generator(w) - Generator(u)) /// 4) The pairs u and v are the simulated pair, preserving the dependence structure. - /// 5) Both these u and v in the range [0,1]. These simulated pairs of u and v are then + /// 5) Both these u and v are in the range [0,1]. Simulated pairs of u and v are then /// back-transformed through their corresponding marginal distributions. + /// This is the exact inverse of the generic conditional h(v|u) = φ′(u)/φ′(C(u,v)): + /// setting h = t and solving gives C = φ′⁻¹(φ′(u)/t) and v = φ⁻¹(φ(C) − φ(u)). /// - public override double[] InverseCDF(double u, double v) + public override double InverseConditionalCDF(double u, double t) { // Validate parameters if (_parametersValid == false) ValidateParameter(Theta, true); - double s = GeneratorPrime(u) / v; + double s = GeneratorPrime(u) / t; double w = GeneratorPrimeInverse(s); - v = GeneratorInverse(Generator(w) - Generator(u)); - return [u, v]; + return GeneratorInverse(Generator(w) - Generator(u)); + } + + /// + public override double[] InverseCDF(double u, double v) + { + return [u, InverseConditionalCDF(u, v)]; } } diff --git a/Numerics/Distributions/Bivariate Copulas/Base/BivariateCopula.cs b/Numerics/Distributions/Bivariate Copulas/Base/BivariateCopula.cs index a955fdc6..cc545365 100644 --- a/Numerics/Distributions/Bivariate Copulas/Base/BivariateCopula.cs +++ b/Numerics/Distributions/Bivariate Copulas/Base/BivariateCopula.cs @@ -118,6 +118,49 @@ public double LogPDF(double u, double v) /// public abstract double CDF(double u, double v); + /// + /// Returns the forward conditional cumulative distribution function (h-function), + /// h(v|u) = ∂C(u,v)/∂u = P(V ≤ v | U = u). + /// + /// The conditioning variate, a non-exceedance probability between 0 and 1. + /// The dependent variate, a non-exceedance probability between 0 and 1. + /// The conditional non-exceedance probability P(V ≤ v | U = u). + /// + /// Both arguments follow the copula convention of non-exceedance probabilities. + /// The base implementation approximates the partial derivative with a central finite + /// difference over using ε = 1E-6, clamping u ± ε to [0, 1] and dividing + /// by the realized step width. Every copula shipped with the library overrides this method + /// with its exact analytic conditional; the finite-difference fallback exists so an external + /// subclass that only implements still gets a correct approximation. + /// inverts this function in v. + /// + public virtual double ConditionalCDF(double u, double v) + { + double uPlus = Math.Min(u + 1E-6, 1d); + double uMinus = Math.Max(u - 1E-6, 0d); + return (CDF(uPlus, v) - CDF(uMinus, v)) / (uPlus - uMinus); + } + + /// + /// Returns the inverse of the forward conditional CDF with respect to v: the value v + /// such that h(v|u) = t, where h is . + /// + /// The conditioning variate, a non-exceedance probability between 0 and 1. + /// The conditional non-exceedance probability between 0 and 1. + /// The dependent variate v such that P(V ≤ v | U = u) = t. + /// + /// This is the scalar, non-allocating form of the conditional simulation that + /// returns as its second element. The base + /// implementation delegates to that array form; every copula shipped with the library + /// overrides this method with the scalar computation and recomposes the array form on + /// top of it, so hot loops can invert conditional probabilities without per-call + /// allocation. + /// + public virtual double InverseConditionalCDF(double u, double t) + { + return InverseCDF(u, t)[1]; + } + /// public abstract double[] InverseCDF(double u, double v); diff --git a/Numerics/Distributions/Bivariate Copulas/ClaytonCopula.cs b/Numerics/Distributions/Bivariate Copulas/ClaytonCopula.cs index ade724e6..86ee0768 100644 --- a/Numerics/Distributions/Bivariate Copulas/ClaytonCopula.cs +++ b/Numerics/Distributions/Bivariate Copulas/ClaytonCopula.cs @@ -122,12 +122,21 @@ public override double CDF(double u, double v) } /// - public override double[] InverseCDF(double u, double v) + /// + /// Uses the Clayton closed-form conditional inversion + /// v = (u^(−θ)·(t^(−θ/(θ+1)) − 1) + 1)^(−1/θ). + /// + public override double InverseConditionalCDF(double u, double t) { // Validate parameters if (_parametersValid == false) ValidateParameter(Theta, true); - v = Math.Pow(Math.Pow(u, -Theta) * (Math.Pow(v, -Theta / (Theta + 1d)) - 1d) + 1d, -1d / Theta); - return [u, v]; + return Math.Pow(Math.Pow(u, -Theta) * (Math.Pow(t, -Theta / (Theta + 1d)) - 1d) + 1d, -1d / Theta); + } + + /// + public override double[] InverseCDF(double u, double v) + { + return [u, InverseConditionalCDF(u, v)]; } /// diff --git a/Numerics/Distributions/Bivariate Copulas/FrankCopula.cs b/Numerics/Distributions/Bivariate Copulas/FrankCopula.cs index 15868b7f..670862d0 100644 --- a/Numerics/Distributions/Bivariate Copulas/FrankCopula.cs +++ b/Numerics/Distributions/Bivariate Copulas/FrankCopula.cs @@ -150,14 +150,29 @@ public override double CDF(double u, double v) } /// - public override double[] InverseCDF(double u, double v) + /// + /// Uses the Frank closed-form conditional inversion. The evaluation always runs at the + /// negated dependency −|θ|, where the logarithm's argument is well conditioned, and maps + /// a positive-θ request through the Frank reflection identity + /// h_θ(v|u) = 1 − h_{−θ}(1−v|u): solving h_θ(v|u) = t therefore requires handing the + /// negative-θ evaluation the complemented conditional 1 − t and reflecting its result, + /// so that the round trip ConditionalCDF(u, InverseConditionalCDF(u, t)) = t holds on + /// both dependency branches. + /// + public override double InverseConditionalCDF(double u, double t) { // Validate parameters if (_parametersValid == false) ValidateParameter(Theta, true); double a = -Math.Abs(Theta); - v = -1d / a * Math.Log((-v * (Math.Exp(-a) - 1d) / (Math.Exp(-a * u) * (v - 1d) - v)) + 1d); - v = Theta > 0d ? 1d - v : v; - return [u, v]; + double s = Theta > 0d ? 1d - t : t; + double v = -1d / a * Math.Log((-s * (Math.Exp(-a) - 1d) / (Math.Exp(-a * u) * (s - 1d) - s)) + 1d); + return Theta > 0d ? 1d - v : v; + } + + /// + public override double[] InverseCDF(double u, double v) + { + return [u, InverseConditionalCDF(u, v)]; } /// diff --git a/Numerics/Distributions/Bivariate Copulas/GumbelCopula.cs b/Numerics/Distributions/Bivariate Copulas/GumbelCopula.cs index d2bd32d5..c96fbf64 100644 --- a/Numerics/Distributions/Bivariate Copulas/GumbelCopula.cs +++ b/Numerics/Distributions/Bivariate Copulas/GumbelCopula.cs @@ -114,19 +114,29 @@ public override double GeneratorPrimeInverse(double t) } /// - public override double[] InverseCDF(double u, double v) + /// + /// The Gumbel conditional has no closed-form inverse, so the conditional probability + /// function h(x|u) = C(u,x)·A^(1/θ−1)·(−ln u)^(θ−1)/u, A = (−ln u)^θ + (−ln x)^θ, + /// is inverted numerically with Brent's method on x ∈ [0, 1]. + /// + public override double InverseConditionalCDF(double u, double t) { // Validate parameters if (_parametersValid == false) ValidateParameter(Theta, true); - // Use conditional probability function - double p = v; - v = Brent.Solve(x => + // Use conditional probability function + double p = t; + return Brent.Solve(x => { double vu = Math.Pow(-Math.Log(u), Theta - 1d) * Math.Exp(-Math.Pow(Math.Pow(-Math.Log(u), Theta) + Math.Pow(-Math.Log(x), Theta), 1d / Theta)) * Math.Pow(Math.Pow(-Math.Log(u), Theta) + Math.Pow(-Math.Log(x), Theta), 1d / Theta - 1d) / u; return vu - p; }, 0d, 1d); - return [u, v]; + } + + /// + public override double[] InverseCDF(double u, double v) + { + return [u, InverseConditionalCDF(u, v)]; } /// diff --git a/Numerics/Distributions/Bivariate Copulas/JoeCopula.cs b/Numerics/Distributions/Bivariate Copulas/JoeCopula.cs index 32fe731e..b9ac6a06 100644 --- a/Numerics/Distributions/Bivariate Copulas/JoeCopula.cs +++ b/Numerics/Distributions/Bivariate Copulas/JoeCopula.cs @@ -116,19 +116,30 @@ public override double GeneratorPrimeInverse(double t) } /// - public override double[] InverseCDF(double u, double v) + /// + /// The Joe conditional has no closed-form inverse, so the conditional probability + /// function h(x|u) = (1−u)^(θ−1)·[1 − (1−x)^θ]·A^(1/θ−1), + /// A = (1−u)^θ + (1−x)^θ − (1−u)^θ(1−x)^θ, is inverted numerically with Brent's + /// method on x ∈ [0, 1]. + /// + public override double InverseConditionalCDF(double u, double t) { // Validate parameters if (_parametersValid == false) ValidateParameter(Theta, true); - // Use conditional probability function - double p = v; - v = Brent.Solve(x => + // Use conditional probability function + double p = t; + return Brent.Solve(x => { double vu = -(Math.Pow(1d - x, Theta) - 1d) * Math.Pow(Math.Pow(1d - u, Theta) - Math.Pow(1d - u, Theta) * Math.Pow(1d - x, Theta) + Math.Pow(1d - x, Theta), (-Theta + 1d) / Theta) * Math.Pow(1d - u, Theta - 1d); return vu - p; }, 0d, 1d); - return [u, v]; + } + + /// + public override double[] InverseCDF(double u, double v) + { + return [u, InverseConditionalCDF(u, v)]; } /// diff --git a/Numerics/Distributions/Bivariate Copulas/NormalCopula.cs b/Numerics/Distributions/Bivariate Copulas/NormalCopula.cs index 9adf1991..d5e7e7c8 100644 --- a/Numerics/Distributions/Bivariate Copulas/NormalCopula.cs +++ b/Numerics/Distributions/Bivariate Copulas/NormalCopula.cs @@ -157,16 +157,39 @@ public override double CDF(double u, double v) } /// - public override double[] InverseCDF(double u, double v) + /// + /// For the Gaussian copula, the conditional distribution of the underlying standard + /// normal Z₂ | Z₁ = z₁ is Normal(ρz₁, 1 − ρ²), so + /// h(v|u) = Φ((Φ⁻¹(v) − ρΦ⁻¹(u)) / √(1 − ρ²)). + /// + public override double ConditionalCDF(double u, double v) + { + // Validate parameters + if (_parametersValid == false) ValidateParameter(Theta, true); + double r = _theta; + return Normal.StandardCDF((Normal.StandardZ(v) - r * Normal.StandardZ(u)) / Math.Sqrt(1d - r * r)); + } + + /// + /// + /// Uses the Gaussian closed-form conditional inversion + /// v = Φ(ρΦ⁻¹(u) + √(1 − ρ²)·Φ⁻¹(t)). + /// + public override double InverseConditionalCDF(double u, double t) { // Validate parameters if (_parametersValid == false) ValidateParameter(Theta, true); double z1 = Normal.StandardZ(u); - double z2 = Normal.StandardZ(v); + double z2 = Normal.StandardZ(t); double r = _theta; double w2 = r * z1 + Math.Sqrt(1d - r * r) * z2; - v = Normal.StandardCDF(w2); - return [u, v]; + return Normal.StandardCDF(w2); + } + + /// + public override double[] InverseCDF(double u, double v) + { + return [u, InverseConditionalCDF(u, v)]; } /// diff --git a/Numerics/Distributions/Bivariate Copulas/StudentTCopula.cs b/Numerics/Distributions/Bivariate Copulas/StudentTCopula.cs index 4235229d..34b85393 100644 --- a/Numerics/Distributions/Bivariate Copulas/StudentTCopula.cs +++ b/Numerics/Distributions/Bivariate Copulas/StudentTCopula.cs @@ -337,8 +337,43 @@ public override double CDF(double u, double v) /// The first uniform variate in (0, 1). /// The second uniform variate in (0, 1), used as the conditional probability. /// - /// A 2-element array [u, v'] where v' is the conditionally sampled variate. + /// A 2-element array [u, v'] where v' is the conditionally sampled variate, + /// computed by . /// + public override double[] InverseCDF(double u, double v) + { + return [u, InverseConditionalCDF(u, v)]; + } + + /// + /// + /// + /// Uses the conditional distribution of the bivariate Student's t: + /// X₂ | X₁ = x₁ ~ t_{ν+1}(ρ·x₁, √((1-ρ²)(ν+x₁²)/(ν+1))), so + /// h(v|u) = T_{ν+1}((x₂ − ρx₁)/s) with x₁ = T⁻¹_ν(u), x₂ = T⁻¹_ν(v), and + /// s = √((1-ρ²)(ν+x₁²)/(ν+1)). + /// + /// + public override double ConditionalCDF(double u, double v) + { + // Validate parameters + if (_parametersValid == false) ValidateParameter(Theta, true); + + double r = _theta; + double nu = _nu; + + // Transform to t-quantiles + var tNu = new StudentT(0, 1, _nu); + double x1 = tNu.InverseCDF(u); + double x2 = tNu.InverseCDF(v); + + // Evaluate the conditional t_{ν+1} CDF at the standardized residual + double conditionalScale = Math.Sqrt((1.0 - r * r) * (nu + x1 * x1) / (nu + 1.0)); + var tNu1 = new StudentT(0, 1, _nu + 1); + return tNu1.CDF((x2 - r * x1) / conditionalScale); + } + + /// /// /// /// Uses the conditional distribution of the bivariate Student's t: @@ -348,12 +383,12 @@ public override double CDF(double u, double v) /// The algorithm is: /// /// Transform u to t-quantile: x₁ = t⁻¹_ν(u) - /// Sample from the conditional t_{ν+1} distribution using v - /// Transform the conditional sample back to uniform: v' = t_ν(x₂) + /// Sample from the conditional t_{ν+1} distribution using t + /// Transform the conditional sample back to uniform: v = t_ν(x₂) /// /// /// - public override double[] InverseCDF(double u, double v) + public override double InverseConditionalCDF(double u, double t) { // Validate parameters if (_parametersValid == false) ValidateParameter(Theta, true); @@ -367,14 +402,13 @@ public override double[] InverseCDF(double u, double v) // Conditional distribution: X2|X1=x1 ~ t_{ν+1} with location = ρ·x1, scale = √((1-ρ²)(ν+x1²)/(ν+1)) var tNu1 = new StudentT(0, 1, _nu + 1); - double z2 = tNu1.InverseCDF(v); + double z2 = tNu1.InverseCDF(t); double conditionalScale = Math.Sqrt((1.0 - r * r) * (nu + x1 * x1) / (nu + 1.0)); double x2 = r * x1 + conditionalScale * z2; // Transform back to uniform - v = tNu.CDF(x2); - return [u, v]; + return tNu.CDF(x2); } /// diff --git a/Test_Numerics/Distributions/Bivariate Copulas/Test_AMHCopula.cs b/Test_Numerics/Distributions/Bivariate Copulas/Test_AMHCopula.cs index 9f06993f..d067109c 100644 --- a/Test_Numerics/Distributions/Bivariate Copulas/Test_AMHCopula.cs +++ b/Test_Numerics/Distributions/Bivariate Copulas/Test_AMHCopula.cs @@ -215,6 +215,80 @@ public void Test_Clone() clone.Theta = 0.8; Assert.AreEqual(0.4, copula.Theta); } + + /// + /// Test the forward conditional CDF (h-function), h(v|u) = ∂C(u,v)/∂u, covering both + /// positive and negative dependence. The Ali-Mikhail-Haq family is not available in + /// pyvinecopulib, so reference values were computed with the Python package mpmath + /// 1.4.1 as a 50-digit numerical ∂C/∂u of the AMH CDF C(u,v) = uv/(1 − θ(1−u)(1−v)), + /// cross-checked at generation against the closed form v(1 − θ(1−v))/D², + /// D = 1 − θ(1−u)(1−v), to 1E-30. The analytic conditional is also cross-checked + /// against a central finite difference of the closed-form CDF (ε = 1E-6; noise floor + /// ~1E-9, so 1E-7 is asserted) and pinned at the exact upper edge h(1|u) = 1. + /// + [TestMethod] + public void Test_ConditionalCDF() + { + var copula = new AMHCopula(0.5); + Assert.AreEqual(0.742798289691333, copula.ConditionalCDF(0.3, 0.7), 1E-12); + Assert.AreEqual(0.24343809494085702, copula.ConditionalCDF(0.7, 0.3), 1E-12); + Assert.AreEqual(0.9424018848037696, copula.ConditionalCDF(0.05, 0.9), 1E-12); + Assert.AreEqual(0.028933391200115736, copula.ConditionalCDF(0.9, 0.05), 1E-12); + + copula = new AMHCopula(-0.7); + Assert.AreEqual(0.6438083047470791, copula.ConditionalCDF(0.3, 0.7), 1E-12); + Assert.AreEqual(0.3397666023871834, copula.ConditionalCDF(0.7, 0.3), 1E-12); + Assert.AreEqual(0.8466512766037415, copula.ConditionalCDF(0.05, 0.9), 1E-12); + Assert.AreEqual(0.07319181596808046, copula.ConditionalCDF(0.9, 0.05), 1E-12); + + foreach (double theta in new[] { 0.5, -0.7 }) + { + var c = new AMHCopula(theta); + foreach (double u in new[] { 0.1, 0.3, 0.5, 0.7, 0.9 }) + { + foreach (double v in new[] { 0.1, 0.5, 0.9 }) + { + double fd = (c.CDF(u + 1E-6, v) - c.CDF(u - 1E-6, v)) / 2E-6; + Assert.AreEqual(fd, c.ConditionalCDF(u, v), 1E-7); + } + Assert.AreEqual(1d, c.ConditionalCDF(u, 1d), 1E-12); + } + } + } + + /// + /// Test the scalar inverse conditional CDF, covering both positive and negative + /// dependence. Reference values were computed with the Python package mpmath 1.4.1 + /// as a 50-digit root solve of the conditional CDF (the AMH family is not available + /// in pyvinecopulib). The closed-form round trip + /// h(InverseConditionalCDF(u, t) | u) = t is asserted at 1E-10, and + /// InverseCDF(u, t)[1] must recompose the scalar bit-for-bit. + /// + [TestMethod] + public void Test_InverseConditionalCDF() + { + var copula = new AMHCopula(0.5); + Assert.AreEqual(0.6538170057580951, copula.InverseConditionalCDF(0.3, 0.7), 1E-10); + Assert.AreEqual(0.08399461695722253, copula.InverseConditionalCDF(0.9, 0.05), 1E-10); + + copula = new AMHCopula(-0.7); + Assert.AreEqual(0.750531409535383, copula.InverseConditionalCDF(0.3, 0.7), 1E-10); + Assert.AreEqual(0.033999893497902646, copula.InverseConditionalCDF(0.9, 0.05), 1E-10); + + foreach (double theta in new[] { 0.5, -0.7 }) + { + var c = new AMHCopula(theta); + foreach (double u in new[] { 0.1, 0.3, 0.5, 0.7, 0.9 }) + { + foreach (double t in new[] { 0.1, 0.5, 0.9 }) + { + double v = c.InverseConditionalCDF(u, t); + Assert.AreEqual(t, c.ConditionalCDF(u, v), 1E-10); + Assert.AreEqual(v, c.InverseCDF(u, t)[1], 0d); + } + } + } + } } } diff --git a/Test_Numerics/Distributions/Bivariate Copulas/Test_ClaytonCopula.cs b/Test_Numerics/Distributions/Bivariate Copulas/Test_ClaytonCopula.cs index add4d080..c628d8b9 100644 --- a/Test_Numerics/Distributions/Bivariate Copulas/Test_ClaytonCopula.cs +++ b/Test_Numerics/Distributions/Bivariate Copulas/Test_ClaytonCopula.cs @@ -224,5 +224,77 @@ public void Test_Clone() clone.Theta = 4.0; Assert.AreEqual(2.0, copula.Theta); } + + /// + /// Test the forward conditional CDF (h-function), h(v|u) = ∂C(u,v)/∂u. + /// Reference values were computed with the Python package pyvinecopulib 0.7.6 + /// (Bicop.hfunc1, family clayton), cross-checked at generation against a 50-digit + /// numerical ∂C/∂u of the Clayton CDF computed with mpmath 1.4.1 (agreement ≤ 1E-15). + /// The analytic conditional is also cross-checked against a central finite difference + /// of the closed-form CDF (ε = 1E-6; the difference-quotient noise floor is ~1E-9, so + /// 1E-7 is asserted) and pinned at the exact upper edge h(1|u) = 1. + /// + [TestMethod] + public void Test_ConditionalCDF() + { + var copula = new ClaytonCopula(2d); + Assert.AreEqual(0.8743161176077272, copula.ConditionalCDF(0.3, 0.7), 1E-10); + Assert.AreEqual(0.06882371771256163, copula.ConditionalCDF(0.7, 0.3), 1E-10); + Assert.AreEqual(0.9991210147197616, copula.ConditionalCDF(0.05, 0.9), 1E-10); + Assert.AreEqual(0.0001713170464197122, copula.ConditionalCDF(0.9, 0.05), 1E-10); + + copula = new ClaytonCopula(5d); + Assert.AreEqual(0.985754642959101, copula.ConditionalCDF(0.3, 0.7), 1E-10); + Assert.AreEqual(0.006108127860986364, copula.ConditionalCDF(0.7, 0.3), 1E-10); + Assert.AreEqual(0.9999997399342684, copula.ConditionalCDF(0.05, 0.9), 1E-10); + Assert.AreEqual(2.9401186465615086E-08, copula.ConditionalCDF(0.9, 0.05), 1E-10); + + foreach (double theta in new[] { 2d, 5d }) + { + var c = new ClaytonCopula(theta); + foreach (double u in new[] { 0.1, 0.3, 0.5, 0.7, 0.9 }) + { + foreach (double v in new[] { 0.1, 0.5, 0.9 }) + { + double fd = (c.CDF(u + 1E-6, v) - c.CDF(u - 1E-6, v)) / 2E-6; + Assert.AreEqual(fd, c.ConditionalCDF(u, v), 1E-7); + } + Assert.AreEqual(1d, c.ConditionalCDF(u, 1d), 1E-12); + } + } + } + + /// + /// Test the scalar inverse conditional CDF. Reference values were computed with the + /// Python package pyvinecopulib 0.7.6 (Bicop.hinv1, family clayton). Also verifies the + /// closed-form round trip h(InverseConditionalCDF(u, t) | u) = t (both directions are + /// closed forms, so 1E-10 is asserted) and that InverseCDF(u, t)[1] recomposes the + /// scalar bit-for-bit. + /// + [TestMethod] + public void Test_InverseConditionalCDF() + { + var copula = new ClaytonCopula(2d); + Assert.AreEqual(0.5010908594248752, copula.InverseConditionalCDF(0.3, 0.7), 1E-9); + Assert.AreEqual(0.3359223320941678, copula.InverseConditionalCDF(0.9, 0.05), 1E-9); + + copula = new ClaytonCopula(5d); + Assert.AreEqual(0.37039687871242977, copula.InverseConditionalCDF(0.3, 0.7), 1E-9); + Assert.AreEqual(0.5500280933001612, copula.InverseConditionalCDF(0.9, 0.05), 1E-9); + + foreach (double theta in new[] { 2d, 5d }) + { + var c = new ClaytonCopula(theta); + foreach (double u in new[] { 0.1, 0.3, 0.5, 0.7, 0.9 }) + { + foreach (double t in new[] { 0.1, 0.5, 0.9 }) + { + double v = c.InverseConditionalCDF(u, t); + Assert.AreEqual(t, c.ConditionalCDF(u, v), 1E-10); + Assert.AreEqual(v, c.InverseCDF(u, t)[1], 0d); + } + } + } + } } } diff --git a/Test_Numerics/Distributions/Bivariate Copulas/Test_FrankCopula.cs b/Test_Numerics/Distributions/Bivariate Copulas/Test_FrankCopula.cs index 0362eec2..d32cbc33 100644 --- a/Test_Numerics/Distributions/Bivariate Copulas/Test_FrankCopula.cs +++ b/Test_Numerics/Distributions/Bivariate Copulas/Test_FrankCopula.cs @@ -194,5 +194,78 @@ public void Test_Clone() clone.Theta = 2.0; Assert.AreEqual(4.2, copula.Theta); } + + /// + /// Test the forward conditional CDF (h-function), h(v|u) = ∂C(u,v)/∂u, covering both + /// the positive- and negative-dependence branches. Reference values were computed with + /// the Python package pyvinecopulib 0.7.6 (Bicop.hfunc1, family frank), cross-checked + /// at generation against a 50-digit numerical ∂C/∂u of the Frank CDF computed with + /// mpmath 1.4.1 (agreement ≤ 1E-15). The analytic conditional is also cross-checked + /// against a central finite difference of the closed-form CDF (ε = 1E-6; noise floor + /// ~1E-9, so 1E-7 is asserted) and pinned at the exact upper edge h(1|u) = 1. + /// + [TestMethod] + public void Test_ConditionalCDF() + { + var copula = new FrankCopula(4d); + Assert.AreEqual(0.8693978167836881, copula.ConditionalCDF(0.3, 0.7), 1E-10); + Assert.AreEqual(0.1306021832163123, copula.ConditionalCDF(0.7, 0.3), 1E-10); + Assert.AreEqual(0.9888149481553941, copula.ConditionalCDF(0.05, 0.9), 1E-10); + Assert.AreEqual(0.0061499217658669445, copula.ConditionalCDF(0.9, 0.05), 1E-10); + + copula = new FrankCopula(-3d); + Assert.AreEqual(0.5965731714099825, copula.ConditionalCDF(0.3, 0.7), 1E-10); + Assert.AreEqual(0.4034268285900173, copula.ConditionalCDF(0.7, 0.3), 1E-10); + Assert.AreEqual(0.755959800956752, copula.ConditionalCDF(0.05, 0.9), 1E-10); + Assert.AreEqual(0.11288571261377746, copula.ConditionalCDF(0.9, 0.05), 1E-10); + + foreach (double theta in new[] { 4d, -3d }) + { + var c = new FrankCopula(theta); + foreach (double u in new[] { 0.1, 0.3, 0.5, 0.7, 0.9 }) + { + foreach (double v in new[] { 0.1, 0.5, 0.9 }) + { + double fd = (c.CDF(u + 1E-6, v) - c.CDF(u - 1E-6, v)) / 2E-6; + Assert.AreEqual(fd, c.ConditionalCDF(u, v), 1E-7); + } + Assert.AreEqual(1d, c.ConditionalCDF(u, 1d), 1E-12); + } + } + } + + /// + /// Test the scalar inverse conditional CDF, covering both the positive- and + /// negative-dependence branches of the reflected closed form. Reference values were + /// computed with the Python package pyvinecopulib 0.7.6 (Bicop.hinv1, family frank), + /// whose internal inversion carries ~3E-11 residual, so 1E-8 deltas are asserted for + /// the pins. The closed-form round trip h(InverseConditionalCDF(u, t) | u) = t is + /// asserted at 1E-10, and InverseCDF(u, t)[1] must recompose the scalar bit-for-bit. + /// + [TestMethod] + public void Test_InverseConditionalCDF() + { + var copula = new FrankCopula(4d); + Assert.AreEqual(0.5090047384437639, copula.InverseConditionalCDF(0.3, 0.7), 1E-8); + Assert.AreEqual(0.2597601357556414, copula.InverseConditionalCDF(0.9, 0.05), 1E-8); + + copula = new FrankCopula(-3d); + Assert.AreEqual(0.7771017148916144, copula.InverseConditionalCDF(0.3, 0.7), 1E-8); + Assert.AreEqual(0.02170136771746911, copula.InverseConditionalCDF(0.9, 0.05), 1E-8); + + foreach (double theta in new[] { 4d, -3d }) + { + var c = new FrankCopula(theta); + foreach (double u in new[] { 0.1, 0.3, 0.5, 0.7, 0.9 }) + { + foreach (double t in new[] { 0.1, 0.5, 0.9 }) + { + double v = c.InverseConditionalCDF(u, t); + Assert.AreEqual(t, c.ConditionalCDF(u, v), 1E-10); + Assert.AreEqual(v, c.InverseCDF(u, t)[1], 0d); + } + } + } + } } } diff --git a/Test_Numerics/Distributions/Bivariate Copulas/Test_GumbelCopula.cs b/Test_Numerics/Distributions/Bivariate Copulas/Test_GumbelCopula.cs index 210e5c58..b7b8a44f 100644 --- a/Test_Numerics/Distributions/Bivariate Copulas/Test_GumbelCopula.cs +++ b/Test_Numerics/Distributions/Bivariate Copulas/Test_GumbelCopula.cs @@ -225,5 +225,77 @@ public void Test_Clone() Assert.AreEqual(2.0, copula.Theta); } + /// + /// Test the forward conditional CDF (h-function), h(v|u) = ∂C(u,v)/∂u. + /// Reference values were computed with the Python package pyvinecopulib 0.7.6 + /// (Bicop.hfunc1, family gumbel), cross-checked at generation against a 50-digit + /// numerical ∂C/∂u of the Gumbel CDF computed with mpmath 1.4.1 (agreement ≤ 1E-15). + /// The analytic conditional is also cross-checked against a central finite difference + /// of the closed-form CDF (ε = 1E-6; noise floor ~1E-9, so 1E-7 is asserted) and + /// pinned at the exact upper edge h(1|u) = 1. + /// + [TestMethod] + public void Test_ConditionalCDF() + { + var copula = new GumbelCopula(2d); + Assert.AreEqual(0.9104803864754554, copula.ConditionalCDF(0.3, 0.7), 1E-10); + Assert.AreEqual(0.11559784394154599, copula.ConditionalCDF(0.7, 0.3), 1E-10); + Assert.AreEqual(0.9975327563905108, copula.ConditionalCDF(0.05, 0.9), 1E-10); + Assert.AreEqual(0.001949079483034616, copula.ConditionalCDF(0.9, 0.05), 1E-10); + + copula = new GumbelCopula(3.5); + Assert.AreEqual(0.9852294418984343, copula.ConditionalCDF(0.3, 0.7), 1E-10); + Assert.AreEqual(0.0201697450039637, copula.ConditionalCDF(0.7, 0.3), 1E-10); + Assert.AreEqual(0.9999871895213038, copula.ConditionalCDF(0.05, 0.9), 1E-10); + Assert.AreEqual(1.2887217392734802E-05, copula.ConditionalCDF(0.9, 0.05), 1E-10); + + foreach (double theta in new[] { 2d, 3.5 }) + { + var c = new GumbelCopula(theta); + foreach (double u in new[] { 0.1, 0.3, 0.5, 0.7, 0.9 }) + { + foreach (double v in new[] { 0.1, 0.5, 0.9 }) + { + double fd = (c.CDF(u + 1E-6, v) - c.CDF(u - 1E-6, v)) / 2E-6; + Assert.AreEqual(fd, c.ConditionalCDF(u, v), 1E-7); + } + Assert.AreEqual(1d, c.ConditionalCDF(u, 1d), 1E-12); + } + } + } + + /// + /// Test the scalar inverse conditional CDF. Reference values were computed with the + /// Python package pyvinecopulib 0.7.6 (Bicop.hinv1, family gumbel). The Gumbel inverse + /// is a Brent solve with tolerance 1E-8, so 1E-6 absolute deltas are asserted for the + /// reference pins and for the round trip h(InverseConditionalCDF(u, t) | u) = t. + /// InverseCDF(u, t)[1] must recompose the scalar bit-for-bit. + /// + [TestMethod] + public void Test_InverseConditionalCDF() + { + var copula = new GumbelCopula(2d); + Assert.AreEqual(0.48403043854946104, copula.InverseConditionalCDF(0.3, 0.7), 1E-6); + Assert.AreEqual(0.39821464719708766, copula.InverseConditionalCDF(0.9, 0.05), 1E-6); + + copula = new GumbelCopula(3.5); + Assert.AreEqual(0.39761414717848886, copula.InverseConditionalCDF(0.3, 0.7), 1E-6); + Assert.AreEqual(0.7272049794715586, copula.InverseConditionalCDF(0.9, 0.05), 1E-6); + + foreach (double theta in new[] { 2d, 3.5 }) + { + var c = new GumbelCopula(theta); + foreach (double u in new[] { 0.1, 0.3, 0.5, 0.7, 0.9 }) + { + foreach (double t in new[] { 0.1, 0.5, 0.9 }) + { + double v = c.InverseConditionalCDF(u, t); + Assert.AreEqual(t, c.ConditionalCDF(u, v), 1E-6); + Assert.AreEqual(v, c.InverseCDF(u, t)[1], 0d); + } + } + } + } + } } diff --git a/Test_Numerics/Distributions/Bivariate Copulas/Test_JoeCopula.cs b/Test_Numerics/Distributions/Bivariate Copulas/Test_JoeCopula.cs index d2dc3e19..63d7bb0e 100644 --- a/Test_Numerics/Distributions/Bivariate Copulas/Test_JoeCopula.cs +++ b/Test_Numerics/Distributions/Bivariate Copulas/Test_JoeCopula.cs @@ -214,5 +214,67 @@ public void Test_Clone() Assert.AreEqual(2.0, copula.Theta); } + /// + /// Test the forward conditional CDF (h-function), h(v|u) = ∂C(u,v)/∂u. + /// Reference values were computed with the Python package pyvinecopulib 0.7.6 + /// (Bicop.hfunc1, family joe), cross-checked at generation against a 50-digit + /// numerical ∂C/∂u of the Joe CDF computed with mpmath 1.4.1 (agreement ≤ 1E-15). + /// The analytic conditional is also cross-checked against a central finite difference + /// of the closed-form CDF (ε = 1E-6; noise floor ~1E-9, so 1E-7 is asserted) and + /// pinned at the exact upper edge h(1|u) = 1. + /// + [TestMethod] + public void Test_ConditionalCDF() + { + var copula = new JoeCopula(2.5); + Assert.AreEqual(0.9123991248836704, copula.ConditionalCDF(0.3, 0.7), 1E-10); + Assert.AreEqual(0.1588740110264739, copula.ConditionalCDF(0.7, 0.3), 1E-10); + Assert.AreEqual(0.9965790386549064, copula.ConditionalCDF(0.05, 0.9), 1E-10); + Assert.AreEqual(0.004109173736105264, copula.ConditionalCDF(0.9, 0.05), 1E-10); + + foreach (double theta in new[] { 1.5, 2.5 }) + { + var c = new JoeCopula(theta); + foreach (double u in new[] { 0.1, 0.3, 0.5, 0.7, 0.9 }) + { + foreach (double v in new[] { 0.1, 0.5, 0.9 }) + { + double fd = (c.CDF(u + 1E-6, v) - c.CDF(u - 1E-6, v)) / 2E-6; + Assert.AreEqual(fd, c.ConditionalCDF(u, v), 1E-7); + } + Assert.AreEqual(1d, c.ConditionalCDF(u, 1d), 1E-12); + } + } + } + + /// + /// Test the scalar inverse conditional CDF. Reference values were computed with the + /// Python package pyvinecopulib 0.7.6 (Bicop.hinv1, family joe). The Joe inverse is a + /// Brent solve with tolerance 1E-8, so 1E-6 absolute deltas are asserted for the + /// reference pins and for the round trip h(InverseConditionalCDF(u, t) | u) = t. + /// InverseCDF(u, t)[1] must recompose the scalar bit-for-bit. + /// + [TestMethod] + public void Test_InverseConditionalCDF() + { + var copula = new JoeCopula(2.5); + Assert.AreEqual(0.48484561599221265, copula.InverseConditionalCDF(0.3, 0.7), 1E-6); + Assert.AreEqual(0.40581706363375525, copula.InverseConditionalCDF(0.9, 0.05), 1E-6); + + foreach (double theta in new[] { 1.5, 2.5 }) + { + var c = new JoeCopula(theta); + foreach (double u in new[] { 0.1, 0.3, 0.5, 0.7, 0.9 }) + { + foreach (double t in new[] { 0.1, 0.5, 0.9 }) + { + double v = c.InverseConditionalCDF(u, t); + Assert.AreEqual(t, c.ConditionalCDF(u, v), 1E-6); + Assert.AreEqual(v, c.InverseCDF(u, t)[1], 0d); + } + } + } + } + } } diff --git a/Test_Numerics/Distributions/Bivariate Copulas/Test_NormalCopula.cs b/Test_Numerics/Distributions/Bivariate Copulas/Test_NormalCopula.cs index 56ac6357..1b499908 100644 --- a/Test_Numerics/Distributions/Bivariate Copulas/Test_NormalCopula.cs +++ b/Test_Numerics/Distributions/Bivariate Copulas/Test_NormalCopula.cs @@ -244,5 +244,82 @@ public void Test_Clone() clone.Theta = 0.9; Assert.AreEqual(0.5, copula.Theta); } + + /// + /// Test the forward conditional CDF (h-function), h(v|u) = Φ((Φ⁻¹(v) − ρΦ⁻¹(u))/√(1−ρ²)), + /// covering both positive and negative correlation. Reference values were computed with + /// the Python package pyvinecopulib 0.7.6 (Bicop.hfunc1, family gaussian), cross-checked + /// at generation against the 50-digit closed form computed with mpmath 1.4.1 + /// (agreement ≤ 1E-15). The analytic conditional is also cross-checked against a central + /// finite difference of the CDF (ε = 1E-6; the CDF is Genz's deterministic BVND, so the + /// noise floor is ~1E-9 and 1E-7 is asserted) and pinned at the exact upper edge + /// h(1|u) = 1 and at the independence reduction h(v|u) = v for ρ = 0. + /// + [TestMethod] + public void Test_ConditionalCDF() + { + var copula = new NormalCopula(0.5); + Assert.AreEqual(0.8181370471246912, copula.ConditionalCDF(0.3, 0.7), 1E-10); + Assert.AreEqual(0.18186295287530885, copula.ConditionalCDF(0.7, 0.3), 1E-10); + Assert.AreEqual(0.9924394369055246, copula.ConditionalCDF(0.05, 0.9), 1E-10); + Assert.AreEqual(0.00415488230042399, copula.ConditionalCDF(0.9, 0.05), 1E-10); + + copula = new NormalCopula(-0.6); + Assert.AreEqual(0.6034164721188054, copula.ConditionalCDF(0.3, 0.7), 1E-10); + Assert.AreEqual(0.39658352788119433, copula.ConditionalCDF(0.7, 0.3), 1E-10); + Assert.AreEqual(0.6436749391998347, copula.ConditionalCDF(0.05, 0.9), 1E-10); + Assert.AreEqual(0.1367794885453144, copula.ConditionalCDF(0.9, 0.05), 1E-10); + + // At ρ = 0 the Gaussian copula is the product copula, so h(v|u) = v exactly. + var independent = new NormalCopula(0d); + Assert.AreEqual(0.35, independent.ConditionalCDF(0.8, 0.35), 1E-12); + + foreach (double rho in new[] { 0.5, -0.6 }) + { + var c = new NormalCopula(rho); + foreach (double u in new[] { 0.1, 0.3, 0.5, 0.7, 0.9 }) + { + foreach (double v in new[] { 0.1, 0.5, 0.9 }) + { + double fd = (c.CDF(u + 1E-6, v) - c.CDF(u - 1E-6, v)) / 2E-6; + Assert.AreEqual(fd, c.ConditionalCDF(u, v), 1E-7); + } + Assert.AreEqual(1d, c.ConditionalCDF(u, 1d), 1E-12); + } + } + } + + /// + /// Test the scalar inverse conditional CDF, v = Φ(ρΦ⁻¹(u) + √(1−ρ²)·Φ⁻¹(t)). + /// Reference values were computed with the Python package pyvinecopulib 0.7.6 + /// (Bicop.hinv1, family gaussian). The closed-form round trip + /// h(InverseConditionalCDF(u, t) | u) = t is asserted at 1E-10, and + /// InverseCDF(u, t)[1] must recompose the scalar bit-for-bit. + /// + [TestMethod] + public void Test_InverseConditionalCDF() + { + var copula = new NormalCopula(0.5); + Assert.AreEqual(0.5761069289340888, copula.InverseConditionalCDF(0.3, 0.7), 1E-9); + Assert.AreEqual(0.21660536867655228, copula.InverseConditionalCDF(0.9, 0.05), 1E-9); + + copula = new NormalCopula(-0.6); + Assert.AreEqual(0.7685746042193584, copula.InverseConditionalCDF(0.3, 0.7), 1E-9); + Assert.AreEqual(0.01854310092874273, copula.InverseConditionalCDF(0.9, 0.05), 1E-9); + + foreach (double rho in new[] { 0.5, -0.6 }) + { + var c = new NormalCopula(rho); + foreach (double u in new[] { 0.1, 0.3, 0.5, 0.7, 0.9 }) + { + foreach (double t in new[] { 0.1, 0.5, 0.9 }) + { + double v = c.InverseConditionalCDF(u, t); + Assert.AreEqual(t, c.ConditionalCDF(u, v), 1E-10); + Assert.AreEqual(v, c.InverseCDF(u, t)[1], 0d); + } + } + } + } } } diff --git a/Test_Numerics/Distributions/Bivariate Copulas/Test_StudentTCopula.cs b/Test_Numerics/Distributions/Bivariate Copulas/Test_StudentTCopula.cs index 4b91d9a8..852f4f1a 100644 --- a/Test_Numerics/Distributions/Bivariate Copulas/Test_StudentTCopula.cs +++ b/Test_Numerics/Distributions/Bivariate Copulas/Test_StudentTCopula.cs @@ -511,5 +511,63 @@ public void Test_ParameterConstraints() Assert.AreEqual(30.0, constraints[1, 1]); } + /// + /// Test the forward conditional CDF (h-function), + /// h(v|u) = T_{ν+1}((x₂ − ρx₁)/s), s = √((1−ρ²)(ν+x₁²)/(ν+1)). + /// Reference values were computed with the Python package pyvinecopulib 0.7.6 + /// (Bicop.hfunc1, family student), cross-checked at generation against the 50-digit + /// closed form computed with mpmath 1.4.1 via the regularized incomplete beta + /// (agreement ≤ 1E-15). The finite-difference cross-check against CDF is asserted at + /// only 1E-3 because the copula CDF is itself a K = 200 stratified numerical + /// integration (MultivariateStudentT.CDF) whose discretization error dominates the + /// difference quotient — the precision anchor for the analytic conditional is the + /// pyvinecopulib pin set, not the finite difference. + /// + [TestMethod] + public void Test_ConditionalCDF() + { + var copula = new StudentTCopula(0.5, 5d); + Assert.AreEqual(0.8285717360290429, copula.ConditionalCDF(0.3, 0.7), 1E-8); + Assert.AreEqual(0.17142826397095706, copula.ConditionalCDF(0.7, 0.3), 1E-8); + Assert.AreEqual(0.9708221102006659, copula.ConditionalCDF(0.05, 0.9), 1E-8); + Assert.AreEqual(0.0135553651824768, copula.ConditionalCDF(0.9, 0.05), 1E-8); + + // The exact-edge identity h(1|u) = 1 is not asserted for this family because the + // underlying univariate Student's t quantile is unbounded at v = 1. + foreach (double u in new[] { 0.1, 0.3, 0.5, 0.7, 0.9 }) + { + foreach (double v in new[] { 0.1, 0.5, 0.9 }) + { + double fd = (copula.CDF(u + 1E-6, v) - copula.CDF(u - 1E-6, v)) / 2E-6; + Assert.AreEqual(fd, copula.ConditionalCDF(u, v), 1E-3); + } + } + } + + /// + /// Test the scalar inverse conditional CDF. Reference values were computed with the + /// Python package pyvinecopulib 0.7.6 (Bicop.hinv1, family student). The round trip + /// h(InverseConditionalCDF(u, t) | u) = t runs through the univariate Student's t + /// CDF/quantile pair in both directions, so 1E-8 is asserted, and + /// InverseCDF(u, t)[1] must recompose the scalar bit-for-bit. + /// + [TestMethod] + public void Test_InverseConditionalCDF() + { + var copula = new StudentTCopula(0.5, 5d); + Assert.AreEqual(0.5646309029414425, copula.InverseConditionalCDF(0.3, 0.7), 1E-8); + Assert.AreEqual(0.16018486883023958, copula.InverseConditionalCDF(0.9, 0.05), 1E-8); + + foreach (double u in new[] { 0.1, 0.3, 0.5, 0.7, 0.9 }) + { + foreach (double t in new[] { 0.1, 0.5, 0.9 }) + { + double v = copula.InverseConditionalCDF(u, t); + Assert.AreEqual(t, copula.ConditionalCDF(u, v), 1E-8); + Assert.AreEqual(v, copula.InverseCDF(u, t)[1], 0d); + } + } + } + } } From 242b3049ec44762c33a6be9cd2661c39c78cdaa3 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 3 Aug 2026 17:49:09 -0600 Subject: [PATCH 052/222] Add the zero-parameter Independence copula Add IndependenceCopula, the product copula C(u,v) = u*v, as a direct BivariateCopula subclass rather than an Archimedean one (the Archimedean base bakes in one dependency parameter, which would be vestigial). The copula has NumberOfCopulaParameters = 0, an empty parameter surface, a no-op SetCopulaParameters, and is permanently valid: ValidateParameter always returns null, so not even a NaN Theta assignment can invalidate it. PDF = 1, CDF = u*v, ConditionalCDF = v, the conditional inversion is the identity, both tail dependences are zero, and Clone deep-copies attached marginals. Append Independence to CopulaType after StudentT. The enum has no explicit values and is serialized by name in every consumer, so appending last is the compatibility-preserving addition; no existing member moves. Guard BivariateCopulaEstimation.Estimate to return immediately for zero-parameter copulas, which would otherwise reach the one-parameter BrentSearch branch with an empty constraint matrix. The guard runs before the full-likelihood path touches the marginals, so estimating an Independence copula is a benign no-op under all three methods. Test_IndependenceCopula pins the closed-form surface exactly (products at IEEE-representable expectations), the permanent-validity contract, seeded generation with a Kendall-tau independence bound derived in the test doc, clone depth, and the estimation no-op. Test_ParameterValidity gains a separate Independence block because its family loop asserts the inverse contract (NaN must invalidate), which the zero-parameter copula deliberately inverts. The scoped copula and validity classes are 104/104 green on net10.0. --- .../Base/BivariateCopulaEstimation.cs | 6 + .../Bivariate Copulas/Base/CopulaType.cs | 6 +- .../Bivariate Copulas/IndependenceCopula.cs | 201 +++++++++++++++ .../Test_IndependenceCopula.cs | 235 ++++++++++++++++++ .../Distributions/Test_ParameterValidity.cs | 20 ++ 5 files changed, 467 insertions(+), 1 deletion(-) create mode 100644 Numerics/Distributions/Bivariate Copulas/IndependenceCopula.cs create mode 100644 Test_Numerics/Distributions/Bivariate Copulas/Test_IndependenceCopula.cs diff --git a/Numerics/Distributions/Bivariate Copulas/Base/BivariateCopulaEstimation.cs b/Numerics/Distributions/Bivariate Copulas/Base/BivariateCopulaEstimation.cs index 47fab230..3f35ad56 100644 --- a/Numerics/Distributions/Bivariate Copulas/Base/BivariateCopulaEstimation.cs +++ b/Numerics/Distributions/Bivariate Copulas/Base/BivariateCopulaEstimation.cs @@ -33,8 +33,14 @@ public class BivariateCopulaEstimation /// The sample data for the X variable. /// The sample data for the Y variable. /// The estimation method to use. + /// + /// A zero-parameter copula (e.g., the ) has nothing to + /// estimate; for every estimation method the call is a benign no-op that returns + /// immediately and leaves the copula untouched. + /// public static void Estimate(ref BivariateCopula copula, IList sampleDataX, IList sampleDataY, CopulaEstimationMethod estimationMethod) { + if (copula.NumberOfCopulaParameters == 0) return; switch (estimationMethod) { case CopulaEstimationMethod.PseudoLikelihood: diff --git a/Numerics/Distributions/Bivariate Copulas/Base/CopulaType.cs b/Numerics/Distributions/Bivariate Copulas/Base/CopulaType.cs index fc24dab2..dfc32a0e 100644 --- a/Numerics/Distributions/Bivariate Copulas/Base/CopulaType.cs +++ b/Numerics/Distributions/Bivariate Copulas/Base/CopulaType.cs @@ -39,6 +39,10 @@ public enum CopulaType /// /// Student's t /// - StudentT + StudentT, + /// + /// Independence (product copula) + /// + Independence } } \ No newline at end of file diff --git a/Numerics/Distributions/Bivariate Copulas/IndependenceCopula.cs b/Numerics/Distributions/Bivariate Copulas/IndependenceCopula.cs new file mode 100644 index 00000000..d2a86f2a --- /dev/null +++ b/Numerics/Distributions/Bivariate Copulas/IndependenceCopula.cs @@ -0,0 +1,201 @@ +using System; +using System.Collections.Generic; + +namespace Numerics.Distributions.Copulas +{ + + /// + /// The Independence (product) copula, Π(u,v) = u·v. + /// + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + /// Description: + /// + /// + /// The Independence copula couples two marginal distributions with no dependence at all: + /// C(u,v) = u·v, the copula of any pair of independent random variables per Sklar's theorem. + /// It is the natural default when no dependence structure has been asserted, and the + /// identity against which dependence modeling is compared — every copula family that + /// admits independence in its parameter interior reduces to it there. + /// + /// + /// The copula has no parameters. It is therefore permanently valid: + /// always returns null, and no assignment to (including + /// non-finite values, which the parameterized families reject) can invalidate it, because the + /// dependency parameter carries no meaning here. is a no-op + /// and is empty, so parameter estimation has nothing to do. + /// + /// + /// References: + /// + /// + /// + /// + /// Nelsen, R.B. (2006). "An Introduction to Copulas." 2nd ed. Springer. Section 2.4. + /// + /// + /// + /// + /// + /// + /// + [Serializable] + public class IndependenceCopula : BivariateCopula + { + + /// + /// Constructs an Independence copula. + /// + public IndependenceCopula() + { + } + + /// + /// Constructs an Independence copula with marginal distributions. + /// + /// The X marginal distribution for the copula. + /// The Y marginal distribution for the copula. + public IndependenceCopula(IUnivariateDistribution? marginalDistributionX, IUnivariateDistribution? marginalDistributionY) + { + MarginalDistributionX = marginalDistributionX; + MarginalDistributionY = marginalDistributionY; + } + + /// + public override CopulaType Type + { + get { return CopulaType.Independence; } + } + + /// + public override string DisplayName + { + get { return "Independence"; } + } + + /// + public override string ShortDisplayName + { + get { return "Π"; } + } + + /// + public override string[,] ParameterToString + { + get { return new string[0, 2]; } + } + + /// + public override string ParameterNameShortForm + { + get { return ""; } + } + + /// + public override double ThetaMinimum + { + get { return 0.0d; } + } + + /// + public override double ThetaMaximum + { + get { return 0.0d; } + } + + /// + public override int NumberOfCopulaParameters => 0; + + /// + public override double[] GetCopulaParameters => Array.Empty(); + + /// + /// + /// The Independence copula has no parameters, so this method is a no-op and the + /// input is ignored. + /// + public override void SetCopulaParameters(double[] parameters) + { + } + + /// + /// + /// The Independence copula has no parameters, so every input is valid and this + /// method always returns null. The copula can never enter an invalid state. + /// + public override ArgumentOutOfRangeException? ValidateParameter(double parameter, bool throwException) + { + return null; + } + + /// + public override double[,] ParameterConstraints(IList sampleDataX, IList sampleDataY) + { + return new double[0, 2]; + } + + /// + public override double PDF(double u, double v) + { + return 1.0d; + } + + /// + public override double CDF(double u, double v) + { + return u * v; + } + + /// + /// + /// Under independence the conditional distribution of V given U = u is the + /// unconditional uniform, so h(v|u) = v. + /// + public override double ConditionalCDF(double u, double v) + { + return v; + } + + /// + /// + /// Under independence the conditional inversion is the identity in t. + /// + public override double InverseConditionalCDF(double u, double t) + { + return t; + } + + /// + /// + /// Under independence conditional simulation is the identity: the pair (u, v) is + /// returned unchanged. + /// + public override double[] InverseCDF(double u, double v) + { + return [u, v]; + } + + /// + /// Gets the upper tail dependence coefficient λ_U = 0. + /// Independent variables have no tail dependence. + /// + public override double UpperTailDependence => 0.0; + + /// + /// Gets the lower tail dependence coefficient λ_L = 0. + /// Independent variables have no tail dependence. + /// + public override double LowerTailDependence => 0.0; + + /// + public override BivariateCopula Clone() + { + return new IndependenceCopula(CloneMarginal(MarginalDistributionX), CloneMarginal(MarginalDistributionY)); + } + + } +} diff --git a/Test_Numerics/Distributions/Bivariate Copulas/Test_IndependenceCopula.cs b/Test_Numerics/Distributions/Bivariate Copulas/Test_IndependenceCopula.cs new file mode 100644 index 00000000..61b9a1f4 --- /dev/null +++ b/Test_Numerics/Distributions/Bivariate Copulas/Test_IndependenceCopula.cs @@ -0,0 +1,235 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Data.Statistics; +using Numerics.Distributions; +using Numerics.Distributions.Copulas; + +namespace Distributions.BivariateCopulas +{ + /// + /// Unit tests for the Independence (product) copula, Π(u,v) = u·v. The copula is exact, + /// so every statistical function is pinned against the closed form directly. + /// + /// + /// + /// Authors: + /// + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + /// + [TestClass] + public class Test_IndependenceCopula + { + /// + /// Test the zero-parameter surface: no parameters exist, the parameter accessors are + /// empty, and SetCopulaParameters ignores its input entirely. + /// + [TestMethod] + public void Test_ZeroParameterSurface() + { + var copula = new IndependenceCopula(); + Assert.AreEqual(CopulaType.Independence, copula.Type); + Assert.AreEqual("Independence", copula.DisplayName); + Assert.AreEqual("Π", copula.ShortDisplayName); + Assert.AreEqual(0, copula.NumberOfCopulaParameters); + Assert.IsEmpty(copula.GetCopulaParameters); + Assert.AreEqual(0, copula.ParameterToString.GetLength(0)); + Assert.AreEqual("", copula.ParameterNameShortForm); + Assert.AreEqual(0d, copula.ThetaMinimum); + Assert.AreEqual(0d, copula.ThetaMaximum); + Assert.AreEqual(0, copula.ParameterConstraints(new[] { 1d, 2d }, new[] { 3d, 4d }).GetLength(0)); + + // SetCopulaParameters is a no-op regardless of input + copula.SetCopulaParameters(new double[] { 5d }); + Assert.AreEqual(0d, copula.Theta); + Assert.IsTrue(copula.ParametersValid); + copula.SetCopulaParameters(System.Array.Empty()); + Assert.IsTrue(copula.ParametersValid); + } + + /// + /// Test that the copula is permanently valid: ValidateParameter always returns null + /// and never throws, and no Theta assignment — not even NaN, which every + /// parameterized family rejects — can invalidate it, because the dependency + /// parameter carries no meaning for a zero-parameter copula. + /// + [TestMethod] + public void Test_PermanentValidity() + { + var copula = new IndependenceCopula(); + Assert.IsTrue(copula.ParametersValid); + Assert.IsNull(copula.ValidateParameter(double.NaN, false)); + Assert.IsNull(copula.ValidateParameter(double.PositiveInfinity, true)); + + copula.Theta = double.NaN; + Assert.IsTrue(copula.ParametersValid); + copula.Theta = double.PositiveInfinity; + Assert.IsTrue(copula.ParametersValid); + + // Statistical functions remain usable in every state + Assert.AreEqual(0.35, copula.CDF(0.5, 0.7), 0d); + } + + /// + /// Test PDF: the product copula density is identically 1, so LogPDF is identically 0. + /// + [TestMethod] + public void Test_PDF() + { + var copula = new IndependenceCopula(); + Assert.AreEqual(1d, copula.PDF(0.2, 0.8), 0d); + Assert.AreEqual(1d, copula.PDF(0.5, 0.5), 0d); + Assert.AreEqual(1d, copula.PDF(0.99, 0.01), 0d); + Assert.AreEqual(0d, copula.LogPDF(0.2, 0.8), 0d); + } + + /// + /// Test CDF: C(u,v) = u·v exactly, with the copula boundary conditions + /// C(0,v) = 0, C(u,1) = u, and C(1,v) = v. + /// + [TestMethod] + public void Test_CDF() + { + var copula = new IndependenceCopula(); + Assert.AreEqual(0.2 * 0.8, copula.CDF(0.2, 0.8), 0d); + Assert.AreEqual(0.25, copula.CDF(0.5, 0.5), 0d); + Assert.AreEqual(0.045000000000000005, copula.CDF(0.9, 0.05), 0d); + Assert.AreEqual(0d, copula.CDF(0d, 0.7), 0d); + Assert.AreEqual(0.7, copula.CDF(0.7, 1d), 0d); + Assert.AreEqual(0.7, copula.CDF(1d, 0.7), 0d); + + // Joint exceedance identities: OR = 1 - uv, AND = (1-u)(1-v) + Assert.AreEqual(1d - 0.16, copula.ORJointExceedanceProbability(0.2, 0.8), 1E-15); + Assert.AreEqual(0.8 * 0.2, copula.ANDJointExceedanceProbability(0.2, 0.8), 1E-15); + } + + /// + /// Test the forward conditional CDF: under independence h(v|u) = v exactly, for any + /// conditioning value. + /// + [TestMethod] + public void Test_ConditionalCDF() + { + var copula = new IndependenceCopula(); + foreach (double u in new[] { 0.05, 0.3, 0.5, 0.7, 0.95 }) + { + foreach (double v in new[] { 0d, 0.1, 0.5, 0.9, 1d }) + { + Assert.AreEqual(v, copula.ConditionalCDF(u, v), 0d); + } + } + } + + /// + /// Test conditional inversion: the scalar inverse is the identity in t, the array + /// form returns the pair unchanged, and the array form recomposes the scalar + /// bit-for-bit. + /// + [TestMethod] + public void Test_InverseCDF() + { + var copula = new IndependenceCopula(); + foreach (double u in new[] { 0.05, 0.3, 0.5, 0.7, 0.95 }) + { + foreach (double t in new[] { 0.1, 0.5, 0.9 }) + { + Assert.AreEqual(t, copula.InverseConditionalCDF(u, t), 0d); + var pair = copula.InverseCDF(u, t); + Assert.AreEqual(u, pair[0], 0d); + Assert.AreEqual(t, pair[1], 0d); + } + } + } + + /// + /// Test the tail dependence coefficients: independent variables have none. + /// + [TestMethod] + public void Test_TailDependence() + { + var copula = new IndependenceCopula(); + Assert.AreEqual(0d, copula.UpperTailDependence, 0d); + Assert.AreEqual(0d, copula.LowerTailDependence, 0d); + } + + /// + /// Test random generation: a seeded sample has the right shape, stays inside the unit + /// square, reproduces bit-for-bit under the same seed, and carries no rank + /// dependence. With n = 2,000 the standard error of Kendall's tau under independence + /// is √(2(2n+5)/(9n(n−1))) ≈ 0.0149, so |τ| < 0.05 is a ≈3.4σ bound on the fixed + /// seed. + /// + [TestMethod] + public void Test_GenerateRandomValues() + { + var copula = new IndependenceCopula(); + int n = 2000; + var sample = copula.GenerateRandomValues(n, seed: 12345); + Assert.AreEqual(n, sample.GetLength(0)); + Assert.AreEqual(2, sample.GetLength(1)); + + var x = new double[n]; + var y = new double[n]; + for (int i = 0; i < n; i++) + { + x[i] = sample[i, 0]; + y[i] = sample[i, 1]; + Assert.IsTrue(sample[i, 0] >= 0d && sample[i, 0] <= 1d); + Assert.IsTrue(sample[i, 1] >= 0d && sample[i, 1] <= 1d); + } + + var repeat = copula.GenerateRandomValues(n, seed: 12345); + for (int i = 0; i < n; i++) + { + Assert.AreEqual(sample[i, 0], repeat[i, 0], 0d); + Assert.AreEqual(sample[i, 1], repeat[i, 1], 0d); + } + + double tau = Correlation.KendallsTau(x, y); + Assert.AreEqual(0d, tau, 0.05); + } + + /// + /// Test Clone produces an independent copy with deep-copied marginals. + /// + [TestMethod] + public void Test_Clone() + { + var copula = new IndependenceCopula(new Normal(100, 10), new Gumbel(50, 5)); + var clone = copula.Clone() as IndependenceCopula; + Assert.IsNotNull(clone); + Assert.AreEqual(CopulaType.Independence, clone.Type); + Assert.IsTrue(clone.ParametersValid); + + // Marginals are deep-copied (distributions memoize lazily, so clones must + // not share marginal instances), with identical quantiles + Assert.AreNotSame(copula.MarginalDistributionX, clone.MarginalDistributionX); + Assert.AreNotSame(copula.MarginalDistributionY, clone.MarginalDistributionY); + Assert.AreEqual(copula.MarginalDistributionY.InverseCDF(0.9), clone.MarginalDistributionY.InverseCDF(0.9)); + } + + /// + /// Test that parameter estimation is a benign no-op for the zero-parameter copula + /// under all three estimation methods: no exception is thrown (the full-likelihood + /// path would otherwise demand estimable marginals) and the copula state is + /// untouched. + /// + [TestMethod] + public void Test_Estimation_NoOp() + { + var dataX = new double[] { 1d, 2d, 3d, 4d, 5d }; + var dataY = new double[] { 2d, 1d, 4d, 3d, 5d }; + foreach (CopulaEstimationMethod method in new[] { CopulaEstimationMethod.PseudoLikelihood, CopulaEstimationMethod.InferenceFromMargins, CopulaEstimationMethod.FullLikelihood }) + { + BivariateCopula copula = new IndependenceCopula(); + BivariateCopulaEstimation.Estimate(ref copula, dataX, dataY, method); + Assert.IsInstanceOfType(copula, typeof(IndependenceCopula)); + Assert.AreEqual(0d, copula.Theta); + Assert.AreEqual(0, copula.NumberOfCopulaParameters); + Assert.IsTrue(copula.ParametersValid); + Assert.IsNull(copula.MarginalDistributionX); + Assert.IsNull(copula.MarginalDistributionY); + } + } + } +} diff --git a/Test_Numerics/Distributions/Test_ParameterValidity.cs b/Test_Numerics/Distributions/Test_ParameterValidity.cs index c0059f65..3865e7a2 100644 --- a/Test_Numerics/Distributions/Test_ParameterValidity.cs +++ b/Test_Numerics/Distributions/Test_ParameterValidity.cs @@ -209,6 +209,26 @@ public void CopulasRejectNonFiniteParametersAndRecover() Assert.IsTrue(student.ParametersValid); } + /// + /// Verifies the zero-parameter Independence copula is permanently valid. This + /// deliberately inverts the parameterized-copula contract asserted above: with no + /// parameters, not even a NaN dependency assignment can invalidate the copula. + /// + [TestMethod] + public void IndependenceCopulaRemainsPermanentlyValid() + { + var copula = new IndependenceCopula(); + Assert.IsTrue(copula.ParametersValid); + copula.Theta = double.NaN; + Assert.IsTrue(copula.ParametersValid); + copula.Theta = double.PositiveInfinity; + Assert.IsTrue(copula.ParametersValid); + copula.SetCopulaParameters(new[] { double.NaN }); + Assert.IsTrue(copula.ParametersValid); + Assert.IsNull(copula.ValidateParameter(double.NaN, true)); + Assert.AreEqual(0.35, copula.CDF(0.5, 0.7), 0d); + } + /// /// Determines whether a factory-created distribution supports flattened numeric parameter replacement. /// From 7673ac2d759abcbc08506b3b2bd25fd59d1856a2 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 3 Aug 2026 17:52:11 -0600 Subject: [PATCH 053/222] Add copula XML serialization and the copula factory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add BivariateCopula.ToXElement(): one virtual writer for all families emitting — the type by enumeration name (the append-only contract every consumer serializes by) and the parameters as a pipe-delimited, invariant-culture, round-trip (G17) string in SetCopulaParameters order; a zero-parameter copula writes an empty attribute. Marginal distributions are deliberately excluded: consumers own and attach their marginals separately, so the element captures the dependence structure alone. Add the static CopulaFactory beside the tree: CreateCopula(CopulaType) is a closed switch over all eight families with a throwing default, and CreateCopula(XElement) reconstructs a copula through the same hardening ladder as UnivariateDistributionFactory — null throw, name-parsed and defined type, parameter count matched to the copula type (a missing or empty Parameters attribute carries zero values), invariant-culture finite parses via Tools.IsFinite, and a post-assignment ParametersValid rejection. Test_CopulaFactory pins the CopulaType member names, contiguous values, and count through Enum.GetValues (runtime-evaluated so the assertion stays meaningful under the constant-condition analyzer), round-trips every family bitwise on irrational parameter values, covers the zero-parameter and missing-attribute forms both ways, and exercises the malformed ladder end to end. The scoped copula and validity classes are 109/109 green on net10.0. --- .../Bivariate Copulas/Base/BivariateCopula.cs | 30 ++++ .../Bivariate Copulas/Base/CopulaFactory.cs | 101 +++++++++++ .../Bivariate Copulas/Test_CopulaFactory.cs | 167 ++++++++++++++++++ 3 files changed, 298 insertions(+) create mode 100644 Numerics/Distributions/Bivariate Copulas/Base/CopulaFactory.cs create mode 100644 Test_Numerics/Distributions/Bivariate Copulas/Test_CopulaFactory.cs diff --git a/Numerics/Distributions/Bivariate Copulas/Base/BivariateCopula.cs b/Numerics/Distributions/Bivariate Copulas/Base/BivariateCopula.cs index cc545365..570ac9fa 100644 --- a/Numerics/Distributions/Bivariate Copulas/Base/BivariateCopula.cs +++ b/Numerics/Distributions/Bivariate Copulas/Base/BivariateCopula.cs @@ -1,6 +1,8 @@ using Numerics.Sampling; using System; using System.Collections.Generic; +using System.Globalization; +using System.Xml.Linq; namespace Numerics.Distributions.Copulas { @@ -302,6 +304,34 @@ public double LogLikelihood(IList sampleDataX, IList sampleDataY return LogLH; } + /// + /// Returns an XElement of the copula's type and parameters, which can be used for + /// serialization. + /// + /// + /// An XElement named "Copula" carrying the by enumeration name and + /// the copula parameters in a "Parameters" attribute as a pipe-delimited, + /// invariant-culture, round-trip ("G17") string in + /// order. A zero-parameter copula writes an empty "Parameters" attribute. + /// + /// + /// Marginal distributions are deliberately not serialized: consumers own their + /// marginals and attach them separately, so the element captures the dependence + /// structure alone. Serializing the type by name (never by numeric value) keeps the + /// payload valid as long as members are only ever appended. + /// reconstructs the copula. + /// + public virtual XElement ToXElement() + { + var result = new XElement("Copula"); + result.SetAttributeValue(nameof(Type), Type.ToString()); + var parameters = GetCopulaParameters; + var values = new string[parameters.Length]; + for (int i = 0; i < parameters.Length; i++) + values[i] = parameters[i].ToString("G17", CultureInfo.InvariantCulture); + result.SetAttributeValue("Parameters", string.Join("|", values)); + return result; + } #endregion } diff --git a/Numerics/Distributions/Bivariate Copulas/Base/CopulaFactory.cs b/Numerics/Distributions/Bivariate Copulas/Base/CopulaFactory.cs new file mode 100644 index 00000000..0575a947 --- /dev/null +++ b/Numerics/Distributions/Bivariate Copulas/Base/CopulaFactory.cs @@ -0,0 +1,101 @@ +using System; +using System.Globalization; +using System.Xml.Linq; + +namespace Numerics.Distributions.Copulas +{ + + /// + /// A bivariate copula factory class. + /// + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + public sealed class CopulaFactory + { + + /// + /// Create a bivariate copula based on the copula type, with its default parameters. + /// + /// Copula type. + /// + /// A bivariate copula. + /// + /// + /// is not a defined value. + /// + public static BivariateCopula CreateCopula(CopulaType copulaType) + { + switch (copulaType) + { + case CopulaType.AliMikhailHaq: + return new AMHCopula(); + case CopulaType.Clayton: + return new ClaytonCopula(); + case CopulaType.Frank: + return new FrankCopula(); + case CopulaType.Gumbel: + return new GumbelCopula(); + case CopulaType.Joe: + return new JoeCopula(); + case CopulaType.Normal: + return new NormalCopula(); + case CopulaType.StudentT: + return new StudentTCopula(); + case CopulaType.Independence: + return new IndependenceCopula(); + default: + throw new NotSupportedException($"The copula type {copulaType} is not supported."); + } + } + + /// + /// Creates a bivariate copula from its serialized representation. + /// + /// The element to deserialize, as written by + /// . + /// A validated bivariate copula. Marginal distributions are not part of the + /// serialized form and are left unattached. + /// Thrown when is null. + /// Thrown when the type or parameter data is missing or malformed. + public static BivariateCopula CreateCopula(XElement xElement) + { + if (xElement == null) throw new ArgumentNullException(nameof(xElement)); + + var typeAttribute = xElement.Attribute(nameof(BivariateCopula.Type)); + if (typeAttribute == null + || !Enum.TryParse(typeAttribute.Value, out CopulaType type) + || !Enum.IsDefined(typeof(CopulaType), type)) + throw new ArgumentException("The serialized copula type is missing or invalid.", nameof(xElement)); + + var copula = CreateCopula(type); + + // A missing or empty "Parameters" attribute carries zero values, so the count + // check below accepts it for a zero-parameter copula and rejects it otherwise. + var parametersAttribute = xElement.Attribute("Parameters"); + string parameterText = parametersAttribute == null ? string.Empty : parametersAttribute.Value; + string[] parts = parameterText.Length == 0 ? Array.Empty() : parameterText.Split('|'); + if (parts.Length != copula.NumberOfCopulaParameters) + throw new ArgumentException("The serialized copula parameter count does not match the copula type.", nameof(xElement)); + + if (parts.Length > 0) + { + var values = new double[parts.Length]; + for (int i = 0; i < parts.Length; i++) + { + if (!double.TryParse(parts[i], NumberStyles.Any, CultureInfo.InvariantCulture, out values[i]) + || !Tools.IsFinite(values[i])) + throw new ArgumentException("The serialized copula parameter at position " + i + " is missing or invalid.", nameof(xElement)); + } + copula.SetCopulaParameters(values); + if (!copula.ParametersValid) + throw new ArgumentException("The serialized parameters do not define a valid copula.", nameof(xElement)); + } + return copula; + } + + } +} diff --git a/Test_Numerics/Distributions/Bivariate Copulas/Test_CopulaFactory.cs b/Test_Numerics/Distributions/Bivariate Copulas/Test_CopulaFactory.cs new file mode 100644 index 00000000..142e7f2f --- /dev/null +++ b/Test_Numerics/Distributions/Bivariate Copulas/Test_CopulaFactory.cs @@ -0,0 +1,167 @@ +using System; +using System.Xml.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Distributions.Copulas; + +namespace Distributions.BivariateCopulas +{ + /// + /// Unit tests for the bivariate copula factory and the copula XML serialization surface. + /// + /// + /// + /// Authors: + /// + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + /// + [TestClass] + public class Test_CopulaFactory + { + /// + /// Pins the CopulaType member names and implicit values. The enumeration is serialized + /// by name and has no explicit values, so its order is append-only contract: a new + /// member may only ever be added after the last. + /// + [TestMethod] + public void Test_CopulaType_ValuesArePinned() + { + var values = (CopulaType[])Enum.GetValues(typeof(CopulaType)); + CollectionAssert.AreEqual(new[] + { + "AliMikhailHaq", "Clayton", "Frank", "Gumbel", "Joe", "Normal", "StudentT", "Independence" + }, Array.ConvertAll(values, v => v.ToString())); + + // The implicit values are contiguous from zero, so position and value agree. + for (int i = 0; i < values.Length; i++) + Assert.AreEqual(i, (int)values[i]); + } + + /// + /// Test that the factory creates every defined copula type and each instance reports + /// the type it was created from. + /// + [TestMethod] + public void Test_CreateCopula_EnumRoundTrip() + { + foreach (CopulaType type in Enum.GetValues(typeof(CopulaType))) + { + var copula = CopulaFactory.CreateCopula(type); + Assert.IsNotNull(copula, $"{type} must be constructible."); + Assert.AreEqual(type, copula.Type, $"{type} must round-trip through the factory."); + } + + Assert.Throws(() => CopulaFactory.CreateCopula((CopulaType)99)); + } + + /// + /// Test that ToXElement followed by CreateCopula round-trips every family bitwise: + /// parameters are written as pipe-delimited invariant-culture "G17" strings, which + /// round-trip IEEE doubles exactly, so the reconstructed parameters must be + /// bit-identical — asserted on deliberately irrational parameter values. + /// + [TestMethod] + public void Test_ToXElement_RoundTrip_Bitwise() + { + var copulas = new BivariateCopula[] + { + new AMHCopula(0.57721566490153287), + new ClaytonCopula(2.7182818284590452), + new FrankCopula(-3.1415926535897931), + new GumbelCopula(2.7182818284590452), + new JoeCopula(3.1415926535897931), + new NormalCopula(-0.57721566490153287), + new StudentTCopula(0.31415926535897931, 7.3890560989306495), + new IndependenceCopula() + }; + + foreach (var copula in copulas) + { + XElement element = copula.ToXElement(); + Assert.AreEqual("Copula", element.Name.LocalName); + Assert.AreEqual(copula.Type.ToString(), element.Attribute("Type")?.Value); + Assert.IsNotNull(element.Attribute("Parameters")); + + var restored = CopulaFactory.CreateCopula(element); + Assert.AreEqual(copula.Type, restored.Type); + Assert.IsTrue(restored.ParametersValid); + CollectionAssert.AreEqual(copula.GetCopulaParameters, restored.GetCopulaParameters, + $"{copula.Type} parameters must round-trip bit-for-bit."); + + // Marginals are never part of the serialized form + Assert.IsNull(restored.MarginalDistributionX); + Assert.IsNull(restored.MarginalDistributionY); + } + } + + /// + /// Test the zero-parameter round trip: the Independence copula writes an empty + /// Parameters attribute, and the reader also accepts a missing Parameters attribute + /// for a zero-parameter copula while rejecting it for a parameterized one. + /// + [TestMethod] + public void Test_ZeroParameter_RoundTrip() + { + var copula = new IndependenceCopula(); + XElement element = copula.ToXElement(); + Assert.AreEqual("", element.Attribute("Parameters")?.Value); + + var restored = CopulaFactory.CreateCopula(element); + Assert.AreEqual(CopulaType.Independence, restored.Type); + Assert.IsEmpty(restored.GetCopulaParameters); + + var bare = new XElement("Copula"); + bare.SetAttributeValue("Type", nameof(CopulaType.Independence)); + Assert.AreEqual(CopulaType.Independence, CopulaFactory.CreateCopula(bare).Type); + + var bareClayton = new XElement("Copula"); + bareClayton.SetAttributeValue("Type", nameof(CopulaType.Clayton)); + Assert.Throws(() => CopulaFactory.CreateCopula(bareClayton)); + } + + /// + /// Test that malformed serialized forms throw: a null element, a missing or unknown + /// or undefined type, a parameter count that does not match the copula type, an + /// unparseable or non-finite parameter, and parameters outside the copula's valid + /// domain. + /// + [TestMethod] + public void Test_CreateCopula_Malformed_Throws() + { + Assert.Throws(() => CopulaFactory.CreateCopula((XElement)null!)); + + Assert.Throws(() => CopulaFactory.CreateCopula(new XElement("Copula"))); + + var bogusType = new XElement("Copula"); + bogusType.SetAttributeValue("Type", "Bogus"); + Assert.Throws(() => CopulaFactory.CreateCopula(bogusType)); + + var undefinedType = new XElement("Copula"); + undefinedType.SetAttributeValue("Type", "99"); + Assert.Throws(() => CopulaFactory.CreateCopula(undefinedType)); + + var wrongCount = new XElement("Copula"); + wrongCount.SetAttributeValue("Type", nameof(CopulaType.Clayton)); + wrongCount.SetAttributeValue("Parameters", "1|2"); + Assert.Throws(() => CopulaFactory.CreateCopula(wrongCount)); + + var unparseable = new XElement("Copula"); + unparseable.SetAttributeValue("Type", nameof(CopulaType.Clayton)); + unparseable.SetAttributeValue("Parameters", "abc"); + Assert.Throws(() => CopulaFactory.CreateCopula(unparseable)); + + var nonFinite = new XElement("Copula"); + nonFinite.SetAttributeValue("Type", nameof(CopulaType.Clayton)); + nonFinite.SetAttributeValue("Parameters", "NaN"); + Assert.Throws(() => CopulaFactory.CreateCopula(nonFinite)); + + // Gumbel requires theta >= 1, so a structurally well-formed but out-of-domain + // parameter must be rejected by the post-assignment validity check. + var outOfDomain = new XElement("Copula"); + outOfDomain.SetAttributeValue("Type", nameof(CopulaType.Gumbel)); + outOfDomain.SetAttributeValue("Parameters", "0.5"); + Assert.Throws(() => CopulaFactory.CreateCopula(outOfDomain)); + } + } +} From 6062044d1796500121cc4f1e646305cc712c5bd4 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 3 Aug 2026 17:53:29 -0600 Subject: [PATCH 054/222] Document the Independence copula and conditional copula functions Add the Independence (product) copula section and its selection-guide row, rewrite the conditional-distributions section around the ConditionalCDF / InverseConditionalCDF surface with the per-family h-function closed forms (replacing the hand-rolled finite-difference example the API supersedes), and document the copula XML serialization element and CopulaFactory reconstruction. --- docs/distributions/copulas.md | 72 ++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 13 deletions(-) diff --git a/docs/distributions/copulas.md b/docs/distributions/copulas.md index 51c42926..54a80b0f 100644 --- a/docs/distributions/copulas.md +++ b/docs/distributions/copulas.md @@ -187,6 +187,20 @@ The AMH copula models **weak dependence structures** and has no tail dependence. var amhCopula = new AMHCopula(0.5); ``` +### Independence Copula + +The Independence (product) copula $\Pi(u,v) = u \cdot v$ is the copula of any pair of independent random variables. It is the natural default when no dependence structure has been asserted, and the identity against which dependence modeling is compared — every family that admits independence in its parameter interior reduces to it there (e.g., the Normal copula at $\rho = 0$, the Frank copula as $\theta \to 0$). + +The copula has **no parameters**: `NumberOfCopulaParameters` is zero, `SetCopulaParameters` is a no-op, and the copula is *permanently valid* — no assignment can invalidate it. Parameter estimation via `BivariateCopulaEstimation.Estimate` is a benign no-op for it. Its density is identically 1, its conditional CDF is $h(v|u) = v$, conditional simulation is the identity, and both tail dependence coefficients are zero. + +```cs +// Independence (product) copula — no parameters +var independence = new IndependenceCopula(); + +double cdf = independence.CDF(0.5, 0.7); // 0.35 = 0.5 · 0.7 +double pdf = independence.PDF(0.5, 0.7); // 1.0 +``` + ### Copula Selection Guide | Copula | Tail Dependence | Parameter Range | Best For | @@ -198,6 +212,7 @@ var amhCopula = new AMHCopula(0.5); | Frank | None | $\theta \in \mathbb{R} \setminus \lbrace 0\rbrace$ | Moderate symmetric dependence | | Joe | Upper tail | $\theta \in [1, \infty)$ | Strong upper tail dependence | | AMH | None | $\theta \in [-1, 1]$ | Weak dependence structures | +| Independence | None | (no parameters) | Independent variables; the no-dependence default | ## Fitting Copulas to Data @@ -243,6 +258,21 @@ Alternatively, copula parameters can be estimated by maximizing the pseudo-log-l // LogLikelihood — full log-likelihood (copula + marginals) ``` +## Serializing Copulas + +A copula's dependence structure serializes to a single XML element through `ToXElement()`: the copula type by enumeration name and the parameters as a pipe-delimited, invariant-culture, round-trip (`G17`) string. Marginal distributions are deliberately not part of the element — consumers own their marginals and attach them separately. `CopulaFactory` reconstructs a validated copula from either the element or a bare `CopulaType`: + +```cs +using System.Xml.Linq; +using Numerics.Distributions.Copulas; + +var copula = new ClaytonCopula(2.0); +XElement element = copula.ToXElement(); // + +BivariateCopula restored = CopulaFactory.CreateCopula(element); // parameters bit-identical +BivariateCopula fresh = CopulaFactory.CreateCopula(CopulaType.Gumbel); // default parameters +``` + ## Practical Example: Bivariate Distribution Construct a bivariate distribution with arbitrary marginals and specified dependence: @@ -313,26 +343,42 @@ The AND joint exceedance probability is computed as $P(X > x \text{ and } Y > y) ### Conditional Distributions -Given flow, what is the conditional distribution of stage? The conditional CDF can be computed numerically using the copula CDF via partial differentiation: $C(v|u) = \frac{\partial C(u,v)}{\partial u}$. +Given flow, what is the conditional distribution of stage? The forward conditional CDF (the **h-function** of the vine-copula literature [[2]](#2)) is the partial derivative of the copula with respect to the conditioning variable: + +```math +h(v|u) = \frac{\partial C(u,v)}{\partial u} = P(V \le v \mid U = u) +``` + +where $u$ and $v$ are non-exceedance probabilities. Every copula exposes it directly as `ConditionalCDF(u, v)`, with an exact analytic implementation per family: + +| Copula | $h(v\|u)$ | +|--------|-----------| +| Archimedean (generic) | $\varphi'(u) \, / \, \varphi'(C(u,v))$ | +| Clayton | $u^{-\theta-1}\left(u^{-\theta} + v^{-\theta} - 1\right)^{-1-1/\theta}$ | +| Frank | $\dfrac{e^{-\theta u}(e^{-\theta v} - 1)}{(e^{-\theta} - 1) + (e^{-\theta u} - 1)(e^{-\theta v} - 1)}$ | +| Gumbel | $C(u,v) \cdot A^{1/\theta - 1} (-\ln u)^{\theta-1}/u$, with $A = (-\ln u)^\theta + (-\ln v)^\theta$ | +| Joe | $(1-u)^{\theta-1}\left[1 - (1-v)^\theta\right] A^{1/\theta-1}$, with $A = (1-u)^\theta + (1-v)^\theta - (1-u)^\theta(1-v)^\theta$ | +| AMH | $v\left(1 - \theta(1-v)\right)/D^2$, with $D = 1 - \theta(1-u)(1-v)$ | +| Normal | $\Phi\!\left(\dfrac{\Phi^{-1}(v) - \rho\,\Phi^{-1}(u)}{\sqrt{1-\rho^2}}\right)$ | +| Student-t | $T_{\nu+1}\!\left(\dfrac{x_2 - \rho x_1}{s}\right)$, with $x_i = T_\nu^{-1}(\cdot)$, $s = \sqrt{\frac{(1-\rho^2)(\nu + x_1^2)}{\nu+1}}$ | +| Independence | $v$ | + +The scalar `InverseConditionalCDF(u, t)` inverts the h-function in $v$ without allocating, and the array form `InverseCDF(u, t)` returns the pair `[u, v]` on top of it — the conditional-simulation surface. The base-class `ConditionalCDF` also provides a central-finite-difference fallback over `CDF` for external copula subclasses that predate the analytic surface. ```cs // Observed flow double observedFlow = 12000; double uFlow = margin1.CDF(observedFlow); -// Approximate the conditional CDF: dC(u,v)/du via finite difference -Func conditionalCDF = (stage) => -{ - double uStage = margin2.CDF(stage); - double du = 1e-6; - double uPlus = Math.Min(uFlow + du, 1.0); - double uMinus = Math.Max(uFlow - du, 0.0); - return (copula.CDF(uPlus, uStage) - copula.CDF(uMinus, uStage)) / (uPlus - uMinus); -}; - -// Conditional probability at specific values +// Exact conditional CDF of stage given flow +double uStage = margin2.CDF(15); +double h = copula.ConditionalCDF(uFlow, uStage); Console.WriteLine($"Given flow = {observedFlow:F0} cfs:"); -Console.WriteLine($" P(Stage > 15 | Flow = {observedFlow}) = {1 - conditionalCDF(15):P1}"); +Console.WriteLine($" P(Stage > 15 | Flow = {observedFlow}) = {1 - h:P1}"); + +// Conditional quantile: the stage exceeded with 1% probability given the flow +double vStage = copula.InverseConditionalCDF(uFlow, 0.99); +Console.WriteLine($" 99th percentile stage given the flow = {margin2.InverseCDF(vStage):F1}"); ``` ## Tail Dependence From 5ea77d39b40f3c5a2c8bdfd5db4681b3111b1fc5 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 7 Aug 2026 16:59:00 -0600 Subject: [PATCH 055/222] Saturate the Gumbel and Joe conditional inverses at the probability bounds Both families invert their conditional CDF numerically with Brent over [0, 1]; every other copula has an analytic inverse. In exact arithmetic h(1|u) = 1, but the floating-point objective rounds below 1 -- for Gumbel by roughly |ln u| ulps -- so a requested conditional level within rounding distance of 1 left the bracket without a sign change and Brent threw "root is not bracketed". Measured over a uniform grid of 1,999 conditioning probabilities: Gumbel failed for 121 of them at theta = 2 (first at u = 0.006), and Joe, rare at moderate theta, failed for 1,648 at theta = 20. Callers that discretize a conditional distribution up to a clamped endpoint hit this on nearly every evaluation. Both inverses now evaluate the objective at the upper bound first and return the boundary when the requested level is unbracketed within rounding, leaving the interior solve untouched (the reference-value pins run the same path). The new boundary tests assert completion, range, and monotonicity in the conditional level across a dense grid and a theta sweep, deliberately not a round trip through ConditionalCDF: the Archimedean families evaluate that through the generator ratio phi'(u)/phi'(C(u,v)) rather than the closed form these inverses solve, and its precision degrades at v = 1 for large theta (Joe returns 0.5245 at theta = 20, u = 0.846 where the exact value is 1). That is a property of the generator-ratio form and is left unchanged here. --- .../Bivariate Copulas/GumbelCopula.cs | 11 +++-- .../Bivariate Copulas/JoeCopula.cs | 11 +++-- .../Bivariate Copulas/Test_GumbelCopula.cs | 41 +++++++++++++++++++ .../Bivariate Copulas/Test_JoeCopula.cs | 38 +++++++++++++++++ 4 files changed, 95 insertions(+), 6 deletions(-) diff --git a/Numerics/Distributions/Bivariate Copulas/GumbelCopula.cs b/Numerics/Distributions/Bivariate Copulas/GumbelCopula.cs index c96fbf64..45508818 100644 --- a/Numerics/Distributions/Bivariate Copulas/GumbelCopula.cs +++ b/Numerics/Distributions/Bivariate Copulas/GumbelCopula.cs @@ -117,7 +117,10 @@ public override double GeneratorPrimeInverse(double t) /// /// The Gumbel conditional has no closed-form inverse, so the conditional probability /// function h(x|u) = C(u,x)·A^(1/θ−1)·(−ln u)^(θ−1)/u, A = (−ln u)^θ + (−ln x)^θ, - /// is inverted numerically with Brent's method on x ∈ [0, 1]. + /// is inverted numerically with Brent's method on x ∈ [0, 1]. In exact arithmetic + /// h(1|u) = 1, but the floating-point evaluation rounds below 1 by roughly |ln u| ulps, + /// so a conditional probability within rounding distance of 1 would otherwise leave the + /// bracket without a sign change; the inverse saturates at the boundary there. /// public override double InverseConditionalCDF(double u, double t) { @@ -126,11 +129,13 @@ public override double InverseConditionalCDF(double u, double t) // Use conditional probability function double p = t; - return Brent.Solve(x => + Func f = x => { double vu = Math.Pow(-Math.Log(u), Theta - 1d) * Math.Exp(-Math.Pow(Math.Pow(-Math.Log(u), Theta) + Math.Pow(-Math.Log(x), Theta), 1d / Theta)) * Math.Pow(Math.Pow(-Math.Log(u), Theta) + Math.Pow(-Math.Log(x), Theta), 1d / Theta - 1d) / u; return vu - p; - }, 0d, 1d); + }; + if (f(1d) <= 0d) return 1d; + return Brent.Solve(f, 0d, 1d); } /// diff --git a/Numerics/Distributions/Bivariate Copulas/JoeCopula.cs b/Numerics/Distributions/Bivariate Copulas/JoeCopula.cs index b9ac6a06..35180a83 100644 --- a/Numerics/Distributions/Bivariate Copulas/JoeCopula.cs +++ b/Numerics/Distributions/Bivariate Copulas/JoeCopula.cs @@ -120,7 +120,10 @@ public override double GeneratorPrimeInverse(double t) /// The Joe conditional has no closed-form inverse, so the conditional probability /// function h(x|u) = (1−u)^(θ−1)·[1 − (1−x)^θ]·A^(1/θ−1), /// A = (1−u)^θ + (1−x)^θ − (1−u)^θ(1−x)^θ, is inverted numerically with Brent's - /// method on x ∈ [0, 1]. + /// method on x ∈ [0, 1]. In exact arithmetic h(1|u) = 1, but the floating-point + /// evaluation can round below 1, so a conditional probability within rounding distance + /// of 1 would otherwise leave the bracket without a sign change; the inverse saturates + /// at the boundary there. /// public override double InverseConditionalCDF(double u, double t) { @@ -129,11 +132,13 @@ public override double InverseConditionalCDF(double u, double t) // Use conditional probability function double p = t; - return Brent.Solve(x => + Func f = x => { double vu = -(Math.Pow(1d - x, Theta) - 1d) * Math.Pow(Math.Pow(1d - u, Theta) - Math.Pow(1d - u, Theta) * Math.Pow(1d - x, Theta) + Math.Pow(1d - x, Theta), (-Theta + 1d) / Theta) * Math.Pow(1d - u, Theta - 1d); return vu - p; - }, 0d, 1d); + }; + if (f(1d) <= 0d) return 1d; + return Brent.Solve(f, 0d, 1d); } /// diff --git a/Test_Numerics/Distributions/Bivariate Copulas/Test_GumbelCopula.cs b/Test_Numerics/Distributions/Bivariate Copulas/Test_GumbelCopula.cs index b7b8a44f..f251010e 100644 --- a/Test_Numerics/Distributions/Bivariate Copulas/Test_GumbelCopula.cs +++ b/Test_Numerics/Distributions/Bivariate Copulas/Test_GumbelCopula.cs @@ -297,5 +297,46 @@ public void Test_InverseConditionalCDF() } } + /// + /// Test the inverse conditional at conditional probabilities within rounding distance + /// of the boundaries. In exact arithmetic h(1|u) = 1, but the floating-point objective + /// rounds below 1 by roughly |ln u| ulps, so t = 1 − 1E-16 used to leave the Brent + /// bracket without a sign change and throw for u values with |ln u| ≳ 1 (first failure + /// near u = 0.006 at θ = 2). The asserted contract is the inverse relationship, not a + /// particular value: every call returns a probability in [0, 1] whose conditional CDF + /// reproduces the requested level. That phrasing is deliberate — in the far tail under + /// strong dependence the conditional CDF is numerically saturated across a band of v, + /// so several values satisfy h(v|u) = 1 − 1E-16 to full double precision and the + /// solver may legitimately return any of them. The asserted contract is therefore + /// completion, range, and monotonicity in the conditional level — deliberately not a + /// round trip through , which the + /// Archimedean families evaluate through the generator ratio φ′(u)/φ′(C(u,v)) rather + /// than the closed form this inverse solves, and whose precision degrades at v = 1 for + /// large θ. Interior levels are pinned against reference values above, which is where + /// the round trip belongs. + /// + [TestMethod] + public void Test_InverseConditionalCDF_BoundaryConditionals() + { + foreach (double theta in new[] { 1.2, 2d, 3.5, 10d, 20d }) + { + var copula = new GumbelCopula(theta); + for (int i = 1; i < 2000; i++) + { + double u = i / 2000d; + double top = copula.InverseConditionalCDF(u, 1d - 1E-16); + Assert.IsTrue(top >= 0d && top <= 1d, + $"Top-edge inverse left [0, 1] at θ = {theta}, u = {u}: {top}."); + Assert.IsGreaterThanOrEqualTo(copula.InverseConditionalCDF(u, 0.999), top, + $"The top-edge inverse must not fall below an interior level at θ = {theta}, u = {u}."); + double bottom = copula.InverseConditionalCDF(u, 1E-16); + Assert.IsTrue(bottom >= 0d && bottom <= 1d, + $"Bottom-edge inverse left [0, 1] at θ = {theta}, u = {u}: {bottom}."); + Assert.IsLessThanOrEqualTo(copula.InverseConditionalCDF(u, 0.001), bottom, + $"The bottom-edge inverse must not exceed an interior level at θ = {theta}, u = {u}."); + } + } + } + } } diff --git a/Test_Numerics/Distributions/Bivariate Copulas/Test_JoeCopula.cs b/Test_Numerics/Distributions/Bivariate Copulas/Test_JoeCopula.cs index 63d7bb0e..4fcd4ce6 100644 --- a/Test_Numerics/Distributions/Bivariate Copulas/Test_JoeCopula.cs +++ b/Test_Numerics/Distributions/Bivariate Copulas/Test_JoeCopula.cs @@ -276,5 +276,43 @@ public void Test_InverseConditionalCDF() } } + /// + /// Test the inverse conditional at conditional probabilities within rounding distance + /// of the boundaries. In exact arithmetic h(1|u) = 1, but the floating-point objective + /// can round below 1, so t = 1 − 1E-16 used to leave the Brent bracket without a sign + /// change and throw — rare at moderate θ but affecting most of the unit interval at + /// θ = 20 (measured 1,648 of 1,999 uniform u values). The asserted contract is the + /// inverse relationship, not a particular value: every call returns a probability in + /// [0, 1] whose value is monotone in the conditional level. The contract is + /// deliberately not a round trip through : + /// the Archimedean families evaluate that through the generator ratio + /// φ′(u)/φ′(C(u,v)) rather than the closed form this inverse solves, and its precision + /// degrades at v = 1 for large θ (measured: h(1|u) returns 0.5245 at θ = 20, u = 0.846, + /// where the exact value is 1 — a property of that formula, not of this inverse). + /// Interior levels are pinned against reference values above. + /// + [TestMethod] + public void Test_InverseConditionalCDF_BoundaryConditionals() + { + foreach (double theta in new[] { 1.2, 1.5, 2.5, 10d, 20d }) + { + var copula = new JoeCopula(theta); + for (int i = 1; i < 2000; i++) + { + double u = i / 2000d; + double top = copula.InverseConditionalCDF(u, 1d - 1E-16); + Assert.IsTrue(top >= 0d && top <= 1d, + $"Top-edge inverse left [0, 1] at θ = {theta}, u = {u}: {top}."); + Assert.IsGreaterThanOrEqualTo(copula.InverseConditionalCDF(u, 0.999), top, + $"The top-edge inverse must not fall below an interior level at θ = {theta}, u = {u}."); + double bottom = copula.InverseConditionalCDF(u, 1E-16); + Assert.IsTrue(bottom >= 0d && bottom <= 1d, + $"Bottom-edge inverse left [0, 1] at θ = {theta}, u = {u}: {bottom}."); + Assert.IsLessThanOrEqualTo(copula.InverseConditionalCDF(u, 0.001), bottom, + $"The bottom-edge inverse must not exceed an interior level at θ = {theta}, u = {u}."); + } + } + } + } } From 1753d2d3afc5e17a3102f0a088d4aa0da9d9a98c Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 21 Aug 2026 12:56:49 -0600 Subject: [PATCH 056/222] Stratify the competing-risks empirical CDF on log-spaced bins for any x-transform CreateEmpiricalCDF and the default cumulative-incidence bins now always use log-spaced stratification on the offset axis, so heavy-tailed components keep their quantile resolution when XTransform is None: the grid is no longer a uniform partition of a range that can span several orders of magnitude. Positive-support logarithmic cases are unchanged. A test compares the empirical inverse CDF with a root-solved inversion for heavy-tailed maxima, negative-support components and a positive-support minimum. --- .../Univariate/CompetingRisks.cs | 4 +- .../Univariate/Test_CompetingRisks.cs | 55 +++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/Numerics/Distributions/Univariate/CompetingRisks.cs b/Numerics/Distributions/Univariate/CompetingRisks.cs index ad2f8702..65e50d31 100644 --- a/Numerics/Distributions/Univariate/CompetingRisks.cs +++ b/Numerics/Distributions/Univariate/CompetingRisks.cs @@ -883,7 +883,7 @@ public List CumulativeIncidenceFunctions(List d.InverseCDF(minP)); double maxX = Distributions.Max(d => d.InverseCDF(maxP)); - bins = Stratify.XValues(new StratificationOptions(minX, maxX, 200, false), XTransform == Transform.Logarithmic ? true : false); + bins = Stratify.XValues(new StratificationOptions(minX, maxX, 200, false), true); } var D = Distributions.Count(); @@ -1161,7 +1161,7 @@ public void CreateEmpiricalCDF() int order = (int)Math.Floor(Math.Log10(max) - Math.Log10(min)); int binN = Math.Max(200, 100 * order) - 1; // Create bins - var bins = Stratify.XValues(new StratificationOptions(minX, maxX, binN, false), XTransform == Transform.Logarithmic ? true : false); + var bins = Stratify.XValues(new StratificationOptions(minX, maxX, binN, false), true); var xValues = new List(); var pValues = new List(); var x = bins.First().LowerBound; diff --git a/Test_Numerics/Distributions/Univariate/Test_CompetingRisks.cs b/Test_Numerics/Distributions/Univariate/Test_CompetingRisks.cs index d7329ca4..14cb1bad 100644 --- a/Test_Numerics/Distributions/Univariate/Test_CompetingRisks.cs +++ b/Test_Numerics/Distributions/Univariate/Test_CompetingRisks.cs @@ -1,4 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Data; using Numerics.Distributions; using Numerics.Data.Statistics; using Numerics.Mathematics; @@ -1185,6 +1186,60 @@ private static double ComputeKSStatistic(double[] sample, CompetingRisks distrib return maxDiff; } + /// + /// Test that the empirical inverse CDF resolves the quantiles of heavy-tailed, negative-support + /// and positive-support components to within half a percent of a root-solved inversion for + /// both x-transforms, because the empirical grid is log-spaced on the offset axis regardless + /// of the transform. + /// + [TestMethod] + public void Test_EmpiricalInverseCDF_ResolvesQuantilesRegardlessOfXTransform() + { + AssertEmpiricalInverseMatchesRootSolve( + new CompetingRisks(new UnivariateDistributionBase[] { new GeneralizedExtremeValue(100, 20, -0.2), new GeneralizedExtremeValue(130, 25, -0.15) }) + { + MinimumOfRandomVariables = false, + XTransform = Transform.None + }, + new[] { 0.5, 0.9, 0.99, 0.999 }, + "heavy-tailed maxima, no transform"); + AssertEmpiricalInverseMatchesRootSolve( + new CompetingRisks(new UnivariateDistributionBase[] { new Normal(0, 1), new Normal(5, 2) }) + { + MinimumOfRandomVariables = false, + XTransform = Transform.None + }, + new[] { 0.01, 0.5, 0.99 }, + "negative support, no transform"); + AssertEmpiricalInverseMatchesRootSolve( + new CompetingRisks(new UnivariateDistributionBase[] { new GeneralizedPareto(0, 10, -0.1), new Exponential(0, 8) }) + { + MinimumOfRandomVariables = true, + XTransform = Transform.Logarithmic + }, + new[] { 0.1, 0.5, 0.9, 0.99 }, + "positive support, logarithmic transform"); + } + + /// + /// Asserts the empirical inverse CDF of a competing-risks model matches the root-solved + /// inverse of an identical model without an empirical CDF. + /// + /// The model to evaluate through its empirical CDF. + /// The non-exceedance probabilities to check. + /// The assertion context. + private static void AssertEmpiricalInverseMatchesRootSolve(CompetingRisks competingRisks, double[] probabilities, string context) + { + var reference = (CompetingRisks)competingRisks.Clone(); + competingRisks.CreateEmpiricalCDF(); + foreach (double probability in probabilities) + { + double expected = reference.InverseCDF(probability); + double actual = competingRisks.InverseCDF(probability); + Assert.AreEqual(expected, actual, Math.Abs(expected) * 0.005 + 1E-9, $"{context}: p = {probability}"); + } + } + #endregion } } From 90a63a46394db9ef95e72b0fcbba943408110636 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 21 Aug 2026 14:31:13 -0600 Subject: [PATCH 057/222] Describe the copula test references and contracts without change narration --- .../Bivariate Copulas/Test_AMHCopula.cs | 2 +- .../Bivariate Copulas/Test_ClaytonCopula.cs | 6 +++--- .../Bivariate Copulas/Test_FrankCopula.cs | 2 +- .../Bivariate Copulas/Test_GumbelCopula.cs | 12 ++++++------ .../Bivariate Copulas/Test_JoeCopula.cs | 14 +++++++------- .../Bivariate Copulas/Test_NormalCopula.cs | 2 +- .../Bivariate Copulas/Test_StudentTCopula.cs | 2 +- 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Test_Numerics/Distributions/Bivariate Copulas/Test_AMHCopula.cs b/Test_Numerics/Distributions/Bivariate Copulas/Test_AMHCopula.cs index d067109c..2f516166 100644 --- a/Test_Numerics/Distributions/Bivariate Copulas/Test_AMHCopula.cs +++ b/Test_Numerics/Distributions/Bivariate Copulas/Test_AMHCopula.cs @@ -7,7 +7,7 @@ namespace Distributions.BivariateCopulas { /// - /// Unit tests for the AMH Copula. All tests are compared against the R 'copula' package. + /// Unit tests for the AMH Copula. Reference values come from the R 'copula' package for the original methods and from pyvinecopulib 0.7.6 and mpmath 1.4.1 for the conditional-distribution methods. /// /// /// diff --git a/Test_Numerics/Distributions/Bivariate Copulas/Test_ClaytonCopula.cs b/Test_Numerics/Distributions/Bivariate Copulas/Test_ClaytonCopula.cs index c628d8b9..9738ed3c 100644 --- a/Test_Numerics/Distributions/Bivariate Copulas/Test_ClaytonCopula.cs +++ b/Test_Numerics/Distributions/Bivariate Copulas/Test_ClaytonCopula.cs @@ -6,7 +6,7 @@ namespace Distributions.BivariateCopulas { /// - /// Unit tests for the Clayton Copula. All tests are compared against the R 'copula' package. + /// Unit tests for the Clayton Copula. Reference values come from the R 'copula' package for the original methods and from pyvinecopulib 0.7.6 and mpmath 1.4.1 for the conditional-distribution methods. /// /// /// @@ -186,8 +186,8 @@ public void Test_TailDependence() /// /// Test that ParametersValid tracks the dependency parameter's valid range. - /// Regression: the Archimedean base ValidateParameter returned a non-null - /// sentinel for valid parameters, leaving ParametersValid permanently false. + /// ValidateParameter returns null for a valid dependence parameter and a non-null + /// exception otherwise, so ParametersValid follows the parameter. /// [TestMethod] public void Test_ParametersValid() diff --git a/Test_Numerics/Distributions/Bivariate Copulas/Test_FrankCopula.cs b/Test_Numerics/Distributions/Bivariate Copulas/Test_FrankCopula.cs index d32cbc33..47d814c5 100644 --- a/Test_Numerics/Distributions/Bivariate Copulas/Test_FrankCopula.cs +++ b/Test_Numerics/Distributions/Bivariate Copulas/Test_FrankCopula.cs @@ -6,7 +6,7 @@ namespace Distributions.BivariateCopulas { /// - /// Unit tests for the Frank Copula. All tests are compared against the R 'copula' package. + /// Unit tests for the Frank Copula. Reference values come from the R 'copula' package for the original methods and from pyvinecopulib 0.7.6 and mpmath 1.4.1 for the conditional-distribution methods. /// /// /// diff --git a/Test_Numerics/Distributions/Bivariate Copulas/Test_GumbelCopula.cs b/Test_Numerics/Distributions/Bivariate Copulas/Test_GumbelCopula.cs index f251010e..b28e6d92 100644 --- a/Test_Numerics/Distributions/Bivariate Copulas/Test_GumbelCopula.cs +++ b/Test_Numerics/Distributions/Bivariate Copulas/Test_GumbelCopula.cs @@ -6,7 +6,7 @@ namespace Distributions.BivariateCopulas { /// - /// Unit tests for the Gumbel Copula. All tests are compared against the R 'copula' package. + /// Unit tests for the Gumbel Copula. Reference values come from the R 'copula' package for the original methods and from pyvinecopulib 0.7.6 and mpmath 1.4.1 for the conditional-distribution methods. /// /// /// @@ -186,8 +186,8 @@ public void Test_TailDependence() /// /// Test that ParametersValid tracks the dependency parameter's valid range. - /// Regression: the Archimedean base ValidateParameter returned a non-null - /// sentinel for valid parameters, leaving ParametersValid permanently false. + /// ValidateParameter returns null for a valid dependence parameter and a non-null + /// exception otherwise, so ParametersValid follows the parameter. /// [TestMethod] public void Test_ParametersValid() @@ -300,9 +300,9 @@ public void Test_InverseConditionalCDF() /// /// Test the inverse conditional at conditional probabilities within rounding distance /// of the boundaries. In exact arithmetic h(1|u) = 1, but the floating-point objective - /// rounds below 1 by roughly |ln u| ulps, so t = 1 − 1E-16 used to leave the Brent - /// bracket without a sign change and throw for u values with |ln u| ≳ 1 (first failure - /// near u = 0.006 at θ = 2). The asserted contract is the inverse relationship, not a + /// rounds below 1 by roughly |ln u| ulps, so a requested level of t = 1 − 1E-16 can exceed the + /// attainable maximum of the objective for u values with |ln u| ≳ 1; the solver must still + /// complete. The asserted contract is the inverse relationship, not a /// particular value: every call returns a probability in [0, 1] whose conditional CDF /// reproduces the requested level. That phrasing is deliberate — in the far tail under /// strong dependence the conditional CDF is numerically saturated across a band of v, diff --git a/Test_Numerics/Distributions/Bivariate Copulas/Test_JoeCopula.cs b/Test_Numerics/Distributions/Bivariate Copulas/Test_JoeCopula.cs index 4fcd4ce6..f02e00f1 100644 --- a/Test_Numerics/Distributions/Bivariate Copulas/Test_JoeCopula.cs +++ b/Test_Numerics/Distributions/Bivariate Copulas/Test_JoeCopula.cs @@ -6,7 +6,7 @@ namespace Distributions.BivariateCopulas { /// - /// Unit tests for the Joe Copula. All tests are compared against the R 'copula' package. + /// Unit tests for the Joe Copula. Reference values come from the R 'copula' package for the original methods and from pyvinecopulib 0.7.6 and mpmath 1.4.1 for the conditional-distribution methods. /// /// /// @@ -175,8 +175,8 @@ public void Test_TailDependence() /// /// Test that ParametersValid tracks the dependency parameter's valid range. - /// Regression: the Archimedean base ValidateParameter returned a non-null - /// sentinel for valid parameters, leaving ParametersValid permanently false. + /// ValidateParameter returns null for a valid dependence parameter and a non-null + /// exception otherwise, so ParametersValid follows the parameter. /// [TestMethod] public void Test_ParametersValid() @@ -279,15 +279,15 @@ public void Test_InverseConditionalCDF() /// /// Test the inverse conditional at conditional probabilities within rounding distance /// of the boundaries. In exact arithmetic h(1|u) = 1, but the floating-point objective - /// can round below 1, so t = 1 − 1E-16 used to leave the Brent bracket without a sign - /// change and throw — rare at moderate θ but affecting most of the unit interval at - /// θ = 20 (measured 1,648 of 1,999 uniform u values). The asserted contract is the + /// can round below 1, so a requested level of t = 1 − 1E-16 can exceed the attainable maximum of + /// the objective — rarely at moderate θ but for most of the unit interval at θ = 20; the solver + /// must still complete. The asserted contract is the /// inverse relationship, not a particular value: every call returns a probability in /// [0, 1] whose value is monotone in the conditional level. The contract is /// deliberately not a round trip through : /// the Archimedean families evaluate that through the generator ratio /// φ′(u)/φ′(C(u,v)) rather than the closed form this inverse solves, and its precision - /// degrades at v = 1 for large θ (measured: h(1|u) returns 0.5245 at θ = 20, u = 0.846, + /// degrades at v = 1 for large θ (the generator-ratio form returns about 0.52 at θ = 20, u = 0.846, /// where the exact value is 1 — a property of that formula, not of this inverse). /// Interior levels are pinned against reference values above. /// diff --git a/Test_Numerics/Distributions/Bivariate Copulas/Test_NormalCopula.cs b/Test_Numerics/Distributions/Bivariate Copulas/Test_NormalCopula.cs index 1b499908..9413f68d 100644 --- a/Test_Numerics/Distributions/Bivariate Copulas/Test_NormalCopula.cs +++ b/Test_Numerics/Distributions/Bivariate Copulas/Test_NormalCopula.cs @@ -6,7 +6,7 @@ namespace Distributions.BivariateCopulas { /// - /// Unit tests for the Normal Copula. All tests are compared against the R 'copula' package. + /// Unit tests for the Normal Copula. Reference values come from the R 'copula' package for the original methods and from pyvinecopulib 0.7.6 and mpmath 1.4.1 for the conditional-distribution methods. /// /// /// diff --git a/Test_Numerics/Distributions/Bivariate Copulas/Test_StudentTCopula.cs b/Test_Numerics/Distributions/Bivariate Copulas/Test_StudentTCopula.cs index 852f4f1a..44bf54ad 100644 --- a/Test_Numerics/Distributions/Bivariate Copulas/Test_StudentTCopula.cs +++ b/Test_Numerics/Distributions/Bivariate Copulas/Test_StudentTCopula.cs @@ -22,7 +22,7 @@ namespace Distributions.BivariateCopulas /// References: /// /// - /// Reference values verified against R 'copula' package (tCopula, dCopula, pCopula). + /// Reference values come from the R 'copula' package (tCopula, dCopula, pCopula) for the original methods and from pyvinecopulib 0.7.6 and mpmath 1.4.1 for the conditional-distribution methods. /// /// [TestClass] From 48b03806719f2ef7f437fa5a123d139a5b08fbdc Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 24 Aug 2026 14:22:13 -0600 Subject: [PATCH 058/222] Accumulate the L-moment probability weighted moment numerators in double --- Numerics/Data/Statistics/Statistics.cs | 18 +++++++++-- .../Data/Statistics/Test_Statistics.cs | 32 +++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/Numerics/Data/Statistics/Statistics.cs b/Numerics/Data/Statistics/Statistics.cs index 57a058a4..9051fbad 100644 --- a/Numerics/Data/Statistics/Statistics.cs +++ b/Numerics/Data/Statistics/Statistics.cs @@ -524,6 +524,16 @@ public static double[] ProductMoments(IList data) /// Returns the linear moments of a sample {L-Mean (λ1), L-Scale (λ2), L-Skewness (τ3), and L-Kurtosis (τ4)}, or returns NaN if data is empty or any entry is NaN. /// /// Sample of data, no sorting is assumed. + /// The linear moments {λ1, λ2, τ3, τ4}, or four NaN values when the sample has fewer than four entries. + /// Thrown when is null. + /// + /// The probability weighted moment numerators are accumulated in double precision. Evaluating + /// them in integer arithmetic overflows silently under the unchecked default: the b₃ numerator + /// (i-3)(i-2)(i-1) exceeds at i = 1,293 and the b₂ numerator + /// (i-2)(i-1) exceeds it at i = 46,343, which corrupts τ₃ and τ₄ for large samples. Below those + /// thresholds the products are exact integers well under 2⁵³, so the double accumulation + /// reproduces the integer results exactly. See USACE-RMC/Numerics#146. + /// public static double[] LinearMoments(IList data) { if (data == null) throw new ArgumentNullException(nameof(data)); @@ -537,15 +547,17 @@ public static double[] LinearMoments(IList data) double B0 = 0, B1 = 0, B2 = 0, B3 = 0; for (int i = 1; i <= N; i++) { + // Form the b2 and b3 numerators in double so that large samples do not overflow the int products. + double di = i; B0 += sortedData[i - 1]; if (i > 1) B1 += (i - 1) / (N - 1) * sortedData[i - 1]; if (i > 2) - B2 += (i - 2) * (i - 1) / ((N - 2) * (N - 1)) * sortedData[i - 1]; + B2 += (di - 2) * (di - 1) / ((N - 2) * (N - 1)) * sortedData[i - 1]; if (i > 3) - B3 += (i - 3) * (i - 2) * (i - 1) / ((N - 3) * (N - 2) * (N - 1)) * sortedData[i - 1]; + B3 += (di - 3) * (di - 2) * (di - 1) / ((N - 3) * (N - 2) * (N - 1)) * sortedData[i - 1]; } - + B0 /= N; B1 /= N; B2 /= N; diff --git a/Test_Numerics/Data/Statistics/Test_Statistics.cs b/Test_Numerics/Data/Statistics/Test_Statistics.cs index 473fba75..c2c5f415 100644 --- a/Test_Numerics/Data/Statistics/Test_Statistics.cs +++ b/Test_Numerics/Data/Statistics/Test_Statistics.cs @@ -336,6 +336,38 @@ public void Test_ComputeLinearMoments() Assert.AreEqual(trueVal4, lmoms[3], 1E-7); } + /// + /// Test that the LinearMoments probability weighted moment numerators do not overflow for large samples. + /// + /// + /// An evenly spaced sample xᵢ = 1 + 0.5 i is a linear function of the ranks, so its L-skewness + /// and L-kurtosis are analytically exactly zero at every sample length. The b₂ and b₃ numerators + /// exceed at n = 46,343 and n = 1,293 respectively, so evaluating them + /// in integer arithmetic wraps silently and corrupts τ₃ and τ₄. See USACE-RMC/Numerics#146. + /// + [TestMethod] + public void Test_ComputeLinearMoments_LargeSample() + { + // n = 1292 is the last length whose b₃ numerator fits in an int; 1293 is the first that does not. + foreach (int n in new[] { 1292, 1293, 1300 }) + { + var data = new double[n]; + for (int i = 0; i < n; i++) data[i] = 1d + 0.5d * i; + + var lmoms = Numerics.Data.Statistics.Statistics.LinearMoments(data); + Assert.AreEqual(0d, lmoms[2], 1E-12, $"L-skewness (τ₃) is not zero at n = {n}."); + Assert.AreEqual(0d, lmoms[3], 1E-12, $"L-kurtosis (τ₄) is not zero at n = {n}."); + } + + // Below the overflow the numerators are exact integers, so the published reference case is unchanged. + var reference = new double[] { 1953d, 1939d, 1677d, 1692d, 2051d, 2371d, 2022d, 1521d, 1448d, 1825d, 1363d, 1760d, 1672d, 1603d, 1244d, 1521d, 1783d, 1560d, 1357d, 1673d, 1625d, 1425d, 1688d, 1577d, 1736d, 1640d, 1584d, 1293d, 1277d, 1742d, 1491d }; + var referenceMoments = Numerics.Data.Statistics.Statistics.LinearMoments(reference); + Assert.AreEqual(1648.8064516d, referenceMoments[0], 1E-7); + Assert.AreEqual(138.2365591d, referenceMoments[1], 1E-7); + Assert.AreEqual(0.1033903d, referenceMoments[2], 1E-7); + Assert.AreEqual(0.1940943d, referenceMoments[3], 1E-7); + } + /// /// Test the Percentile method against R's "quantile()" method from the "stats" package. /// From 382c11c13b47869b9cdc97e2f31bf18d3c88c232 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 24 Aug 2026 14:33:04 -0600 Subject: [PATCH 059/222] Reject non-finite percentiles and make the BCa and kernel density reductions deterministic Reject a NaN percentile argument, which passed the range check and reached the interpolation index. Replace the BCa jackknife reduction with a fixed-chunk accumulation that reports failed replicates, throws when none succeed, and returns a zero acceleration for a statistic with no jackknife variation. Apply the same fixed-chunk reduction to the kernel density function. --- Numerics/Data/Statistics/Statistics.cs | 15 +- .../Distributions/Univariate/KernelDensity.cs | 49 ++++- Numerics/Sampling/Bootstrap/Bootstrap.cs | 129 +++++++++++-- .../Data/Statistics/Test_Statistics.cs | 22 +++ .../Univariate/Test_KernelDensity.cs | 70 +++++++ Test_Numerics/Sampling/Test_Bootstrap.cs | 175 ++++++++++++++++++ 6 files changed, 433 insertions(+), 27 deletions(-) diff --git a/Numerics/Data/Statistics/Statistics.cs b/Numerics/Data/Statistics/Statistics.cs index 9051fbad..06c7ee53 100644 --- a/Numerics/Data/Statistics/Statistics.cs +++ b/Numerics/Data/Statistics/Statistics.cs @@ -580,12 +580,19 @@ public static double[] LinearMoments(IList data) /// The k-th percentile to find. /// Boolean value indicating if the data is sorted or not. Assumed false, not sorted, by default. /// The k-th percentile. + /// Thrown when is null. + /// Thrown when is empty. + /// + /// Thrown when is not a finite value in [0,1]. Every comparison against NaN + /// is false, so NaN is rejected explicitly; without that test it would reach the interpolation + /// index, which saturates to zero on .NET Core and is undefined on .NET Framework. + /// public static double Percentile(IList data, double k, bool dataIsSorted = false) { if (data == null) throw new ArgumentNullException(nameof(data)); int n = data.Count; if (n == 0) throw new ArgumentException("Sequence contains no elements.", nameof(data)); - if (k < 0.0 || k > 1.0) throw new ArgumentOutOfRangeException(nameof(k), "k must be in [0,1]."); + if (double.IsNaN(k) || k < 0.0 || k > 1.0) throw new ArgumentOutOfRangeException(nameof(k), "k must be in [0,1]."); // Copy & sort if needed var sortedData = dataIsSorted ? data: data.OrderBy(x => x).ToArray(); @@ -609,8 +616,14 @@ public static double Percentile(IList data, double k, bool dataIsSorted /// The list of k-th percentiles to find. /// Boolean value indicating if the data is sorted or not. Assumed false, not sorted, by default. /// The k-th percentile. + /// Thrown when or is null. + /// Thrown when is empty. + /// Thrown when any entry of is not a finite value in [0,1]. public static double[] Percentile(IList data, IList k, bool dataIsSorted = false) { + if (data == null) throw new ArgumentNullException(nameof(data)); + if (k == null) throw new ArgumentNullException(nameof(k)); + // Copy & sort if needed var sortedData = dataIsSorted ? data : data.OrderBy(x => x).ToArray(); var result = new double[k.Count]; diff --git a/Numerics/Distributions/Univariate/KernelDensity.cs b/Numerics/Distributions/Univariate/KernelDensity.cs index 4f7cb6d4..d7fcc78a 100644 --- a/Numerics/Distributions/Univariate/KernelDensity.cs +++ b/Numerics/Distributions/Univariate/KernelDensity.cs @@ -148,6 +148,13 @@ public enum KernelType private double[]? _weights; // one weight per sample (unnormalised) private double _sumW = 1.0; // Σ wᵢ (defaults to 1 for un‑weighted case) + /// + /// The number of accumulation chunks used by the density reduction. Fixed, not derived from the + /// processor count, so the summation order — and therefore the returned density — does not vary + /// with the machine or the thread count. + /// + private const int ReductionChunks = 64; + /// /// Returns the array of X values. Points On the cumulative curve are specified @@ -732,27 +739,49 @@ private static void ValidateSampleData(IList sampleData) } /// + /// + /// The kernel contributions are accumulated over a fixed number of chunks and merged serially in + /// chunk order, so the density is bit-reproducible run to run. A scheduler-dependent reduction + /// would let the last bits of the density — and with them and the cached + /// interpolated CDF — vary between otherwise identical runs. + /// public override double PDF(double x) { + int n = SampleSize; + int chunks = Math.Min(ReductionChunks, n); + var chunkSums = new double[chunks]; if (_weights == null) { - double total = 0d; - Parallel.For(0, SampleSize, () => 0d, (i, loop, subtotal) => + Parallel.For(0, chunks, c => { - subtotal += _kernel.Function((x - _sampleData[i]) / Bandwidth); - return subtotal; - }, z => Tools.ParallelAdd(ref total, z)); + double subtotal = 0d; + int start = (int)((long)c * n / chunks); + int end = (int)((long)(c + 1) * n / chunks); + for (int i = start; i < end; i++) + subtotal += _kernel.Function((x - _sampleData[i]) / Bandwidth); + chunkSums[c] = subtotal; + }); + + double total = 0d; + for (int c = 0; c < chunks; c++) total += chunkSums[c]; return total / (SampleSize * Bandwidth); } else { - double total = 0d; - Parallel.For(0, SampleSize, () => 0.0, (i, loop, subtotal) => + var weights = _weights; + Parallel.For(0, chunks, c => { - subtotal += _weights[i] * _kernel.Function((x - _sampleData[i]) / Bandwidth); - return subtotal; - },z => Tools.ParallelAdd(ref total, z)); + double subtotal = 0d; + int start = (int)((long)c * n / chunks); + int end = (int)((long)(c + 1) * n / chunks); + for (int i = start; i < end; i++) + subtotal += weights[i] * _kernel.Function((x - _sampleData[i]) / Bandwidth); + chunkSums[c] = subtotal; + }); + + double total = 0d; + for (int c = 0; c < chunks; c++) total += chunkSums[c]; return total / (_sumW * Bandwidth); } } diff --git a/Numerics/Sampling/Bootstrap/Bootstrap.cs b/Numerics/Sampling/Bootstrap/Bootstrap.cs index 51c6402c..05e28dd7 100644 --- a/Numerics/Sampling/Bootstrap/Bootstrap.cs +++ b/Numerics/Sampling/Bootstrap/Bootstrap.cs @@ -113,6 +113,15 @@ private enum BootstrapRunType private int _numStats; private int _numParams; private int _failedCount; + private int _failedJackknifeReplicates; + + /// + /// The number of accumulation chunks used by the jackknife reduction. Fixed, not derived from + /// the processor count, so the summation order — and therefore the acceleration constant — + /// does not vary with the machine or the thread count. + /// + private const int JackknifeChunks = 64; + private bool[] _validFlags = null!; private double[,]? _studentizedValues; private double[,]? _transformedStatistics; @@ -282,6 +291,19 @@ public Matrix? OriginalCovariance /// public int FailedReplicates => _failedCount; + /// + /// Gets the number of leave-one-out jackknife replicates that failed while computing the BCa + /// acceleration constants on the most recent + /// call that requested . Zero for every other interval method. + /// + /// + /// Failed jackknife replicates are excluded from the acceleration sums, so a non-zero count means + /// the acceleration constants rest on fewer leave-one-out samples than the sample size implies. + /// This is reported separately from , which counts bootstrap + /// resampling failures rather than jackknife failures. + /// + public int FailedJackknifeReplicates => _failedJackknifeReplicates; + #endregion #region Run Methods @@ -1171,6 +1193,15 @@ private BootstrapStatisticResult ComputeBootstrapTCI(int statisticIndex, double /// /// The original statistic estimates. /// The acceleration constant for each statistic. + /// + /// Thrown when reports a non-positive sample size, or when every + /// leave-one-out replicate produced by fails. + /// + /// + /// The jackknife second and third moments are accumulated over a fixed number of chunks and merged + /// serially in chunk order, so the acceleration constants are reproducible run to run rather than + /// dependent on the thread scheduler. + /// private double[] ComputeAccelerationConstants(double[] populationEstimates) { var sampleSize = SampleSizeFunction!; @@ -1179,33 +1210,99 @@ private double[] ComputeAccelerationConstants(double[] populationEstimates) var statistic = StatisticFunction!; int N = sampleSize(_originalData); - var I2 = new double[_numStats]; - var I3 = new double[_numStats]; var a = new double[_numStats]; + _failedJackknifeReplicates = 0; + if (N <= 0) + throw new InvalidOperationException("The BCa acceleration constants require at least one leave-one-out jackknife replicate, but SampleSizeFunction reported a non-positive sample size."); + + int chunks = Math.Min(JackknifeChunks, N); + var chunkSecondMoments = new double[chunks][]; + var chunkThirdMoments = new double[chunks][]; + var chunkValid = new int[chunks]; + var chunkFailed = new int[chunks]; + var chunkFirstFailure = new Exception?[chunks]; + for (int c = 0; c < chunks; c++) + { + chunkSecondMoments[c] = new double[_numStats]; + chunkThirdMoments[c] = new double[_numStats]; + } - Parallel.For(0, N, idx => + Parallel.For(0, chunks, c => { - try + var secondMoments = chunkSecondMoments[c]; + var thirdMoments = chunkThirdMoments[c]; + int start = (int)((long)c * N / chunks); + int end = (int)((long)(c + 1) * N / chunks); + int valid = 0; + int failed = 0; + Exception? firstFailure = null; + for (int idx = start; idx < end; idx++) { - var jackData = jackknife(_originalData, idx); - var jackFit = fitFunc(jackData); - var jackStats = statistic(jackFit); + try + { + var jackData = jackknife(_originalData, idx); + var jackFit = fitFunc(jackData); + var jackStats = statistic(jackFit); - for (int i = 0; i < _numStats; i++) + for (int i = 0; i < _numStats; i++) + { + double diff = populationEstimates[i] - jackStats[i]; + secondMoments[i] += diff * diff; + thirdMoments[i] += diff * diff * diff; + } + valid++; + } + catch (Exception exception) { - double diff = populationEstimates[i] - jackStats[i]; - Tools.ParallelAdd(ref I2[i], diff * diff); - Tools.ParallelAdd(ref I3[i], diff * diff * diff); + // Count the failed jackknife replicate and keep the first exception so the cause + // survives instead of being discarded silently. + failed++; + if (firstFailure == null) firstFailure = exception; } } - catch (Exception) - { - // Skip failed jackknife samples. - } + chunkValid[c] = valid; + chunkFailed[c] = failed; + chunkFirstFailure[c] = firstFailure; }); + int validCount = 0; + int failedCount = 0; + Exception? firstJackknifeFailure = null; + for (int c = 0; c < chunks; c++) + { + validCount += chunkValid[c]; + failedCount += chunkFailed[c]; + if (firstJackknifeFailure == null) firstJackknifeFailure = chunkFirstFailure[c]; + } + _failedJackknifeReplicates = failedCount; + + if (validCount == 0) + { + string cause = firstJackknifeFailure != null + ? " The first failure was: " + firstJackknifeFailure.Message + : string.Empty; + throw new InvalidOperationException( + "Every leave-one-out replicate produced by JackknifeFunction failed, so the BCa acceleration constants are undefined." + cause, + firstJackknifeFailure); + } + for (int i = 0; i < _numStats; i++) - a[i] = I3[i] / (Math.Pow(I2[i], 1.5) * 6d); + { + double secondMoment = 0d; + double thirdMoment = 0d; + for (int c = 0; c < chunks; c++) + { + secondMoment += chunkSecondMoments[c][i]; + thirdMoment += chunkThirdMoments[c][i]; + } + + // A zero second moment means every jackknife replicate returned the same statistic value, + // so the statistic has no jackknife variation. Zero is the correct limit of the + // acceleration there, and BCa degenerates to the bias-corrected interval. + a[i] = secondMoment > 0d && Tools.IsFinite(secondMoment) + ? thirdMoment / (Math.Pow(secondMoment, 1.5) * 6d) + : 0d; + } return a; } diff --git a/Test_Numerics/Data/Statistics/Test_Statistics.cs b/Test_Numerics/Data/Statistics/Test_Statistics.cs index c2c5f415..99b77e01 100644 --- a/Test_Numerics/Data/Statistics/Test_Statistics.cs +++ b/Test_Numerics/Data/Statistics/Test_Statistics.cs @@ -411,6 +411,28 @@ public void Test_Percentiles() Assert.AreEqual(trueVal8, val8, 1E-2); } + /// + /// Test that the Percentile methods reject a null sample and a non-finite percentile. + /// + /// + /// Every comparison against NaN is false, so a NaN percentile used to pass the range check and + /// reach the interpolation index. On .NET Core the float-to-int conversion saturates and the + /// method silently returned NaN; on .NET Framework the conversion is undefined and the indexer + /// threw. Both infinities were already rejected by the range check. + /// + [TestMethod] + public void Test_Percentile_InvalidArguments() + { + Assert.Throws(() => Numerics.Data.Statistics.Statistics.Percentile(_sample1, double.NaN)); + Assert.Throws(() => Numerics.Data.Statistics.Statistics.Percentile(_sample1, double.PositiveInfinity)); + Assert.Throws(() => Numerics.Data.Statistics.Statistics.Percentile(_sample1, double.NegativeInfinity)); + Assert.Throws(() => Numerics.Data.Statistics.Statistics.Percentile(_sample1, new double[] { 0.5d, double.NaN })); + + Assert.Throws(() => Numerics.Data.Statistics.Statistics.Percentile(null, 0.5d)); + Assert.Throws(() => Numerics.Data.Statistics.Statistics.Percentile(null, new double[] { 0.5d })); + Assert.Throws(() => Numerics.Data.Statistics.Statistics.Percentile(_sample1, (IList)null)); + } + /// /// Test the FiveNumberSummary methods that returns {min, 25th-percentile, 50th-percentile, 75th-percentile, max} against the validation values /// attained in the previous tests of each individual statistic. diff --git a/Test_Numerics/Distributions/Univariate/Test_KernelDensity.cs b/Test_Numerics/Distributions/Univariate/Test_KernelDensity.cs index 132da67d..f10f5b6f 100644 --- a/Test_Numerics/Distributions/Univariate/Test_KernelDensity.cs +++ b/Test_Numerics/Distributions/Univariate/Test_KernelDensity.cs @@ -151,6 +151,76 @@ public void ExplicitZeroBandwidth_RemainsInvalid() new KernelDensity(new[] { -1d, 0d, 1d }, KernelDensity.KernelType.Gaussian, 0d)); } + /// + /// Creates a deterministic sample large enough for the density reduction to be partitioned + /// across several threads. + /// + /// A fixed Normal sample. + private static double[] CreateReductionSample() + { + return new Normal(10d, 3d).GenerateRandomValues(2000, 20250824); + } + + /// + /// Verifies that repeated density evaluations at the same ordinate return bit-identical values. + /// + /// + /// The density is a sum over every sample point, so a scheduler-dependent accumulation order lets + /// the last bits of a public distribution function change between otherwise identical calls. + /// + [TestMethod] + public void PDF_IsBitReproducibleAcrossRepeatedCalls() + { + var reductionSample = CreateReductionSample(); + var weights = new double[reductionSample.Length]; + for (int i = 0; i < weights.Length; i++) weights[i] = 0.5d + i % 7; + + var unweighted = new KernelDensity(reductionSample); + var weighted = new KernelDensity(reductionSample, weights); + + foreach (double x in new[] { 2.5d, 7d, 10d, 13.25d, 19d }) + { + long expectedUnweighted = BitConverter.DoubleToInt64Bits(unweighted.PDF(x)); + long expectedWeighted = BitConverter.DoubleToInt64Bits(weighted.PDF(x)); + for (int trial = 0; trial < 25; trial++) + { + Assert.AreEqual(expectedUnweighted, BitConverter.DoubleToInt64Bits(unweighted.PDF(x)), + $"The unweighted density is not bit-identical at x = {x}."); + Assert.AreEqual(expectedWeighted, BitConverter.DoubleToInt64Bits(weighted.PDF(x)), + $"The weighted density is not bit-identical at x = {x}."); + } + } + } + + /// + /// Verifies that the mode and the cached CDF are bit-identical across separate instances built + /// from the same sample. Both are derived from the density, so a drifting density moves them too. + /// + [TestMethod] + public void ModeAndCDF_AreBitReproducibleAcrossInstances() + { + var reductionSample = CreateReductionSample(); + long expectedMode = 0L; + long expectedCDF = 0L; + + for (int trial = 0; trial < 3; trial++) + { + var distribution = new KernelDensity(reductionSample); + long mode = BitConverter.DoubleToInt64Bits(distribution.Mode); + long cdf = BitConverter.DoubleToInt64Bits(distribution.CDF(11.5d)); + + if (trial == 0) + { + expectedMode = mode; + expectedCDF = cdf; + continue; + } + + Assert.AreEqual(expectedMode, mode, "The mode is not bit-identical across instances."); + Assert.AreEqual(expectedCDF, cdf, "The CDF is not bit-identical across instances."); + } + } + diff --git a/Test_Numerics/Sampling/Test_Bootstrap.cs b/Test_Numerics/Sampling/Test_Bootstrap.cs index f711a16d..6c37a1d0 100644 --- a/Test_Numerics/Sampling/Test_Bootstrap.cs +++ b/Test_Numerics/Sampling/Test_Bootstrap.cs @@ -413,5 +413,180 @@ public void Test_NoFailures() } } + /// + /// Creates a deterministic sample used by the BCa jackknife tests. + /// + /// A fixed Normal sample. + private static double[] CreateJackknifeSample() + { + return new Normal(3.1d, 0.55d).GenerateRandomValues(40, 8675309); + } + + /// + /// Creates a small BCa-ready bootstrap over the supplied sample. The jackknife delegate is left + /// unset so each test can supply its own leave-one-out behavior. + /// + /// The observed sample. + /// The number of bootstrap replicates. + /// The configured bootstrap. + private static Bootstrap CreateJackknifeBootstrap(double[] sampleData, int replicates) + { + var dist = new Normal(); + ((IEstimation)dist).Estimate(sampleData, ParameterEstimationMethod.MethodOfMoments); + + var boot = new Bootstrap(sampleData, new ParameterSet(dist.GetParameters, double.NaN)) + { + Replicates = replicates, + PRNGSeed = 12345 + }; + + boot.ResampleFunction = (data, ps, rng) => + { + var d = new Normal(ps.Values[0], ps.Values[1]); + return d.GenerateRandomValues(sampleData.Length, rng.Next()); + }; + + boot.FitFunction = (sample) => + { + var d = new Normal(); + ((IEstimation)d).Estimate(sample, ParameterEstimationMethod.MethodOfMoments); + if (!d.ParametersValid) throw new Exception("Invalid parameters."); + return new ParameterSet(d.GetParameters, double.NaN); + }; + + boot.StatisticFunction = (ps) => + { + var d = new Normal(ps.Values[0], ps.Values[1]); + return new double[] { d.InverseCDF(0.5d), d.InverseCDF(0.99d) }; + }; + + boot.SampleSizeFunction = (data) => data.Length; + return boot; + } + + /// + /// Test that a wholly failed jackknife reports the cause instead of producing NaN acceleration. + /// + /// + /// A NaN acceleration constant reaches Statistics.Percentile as the percentile argument, where it + /// silently returned NaN bounds on .NET Core and raised an index error on .NET Framework. + /// + [TestMethod] + public void Test_BCa_AllJackknifeReplicatesFail() + { + var sampleData = CreateJackknifeSample(); + var boot = CreateJackknifeBootstrap(sampleData, 500); + boot.JackknifeFunction = (data, idx) => throw new InvalidOperationException("Jackknife replicate rejected."); + + boot.Run(); + + var exception = Assert.Throws(() => boot.GetConfidenceIntervals(BootstrapCIMethod.BCa)); + StringAssert.Contains(exception.Message, nameof(Bootstrap.JackknifeFunction)); + StringAssert.Contains(exception.Message, "Jackknife replicate rejected."); + Assert.AreEqual(sampleData.Length, boot.FailedJackknifeReplicates); + } + + /// + /// Test that a partially failing jackknife still produces finite BCa bounds and reports the failures. + /// + [TestMethod] + public void Test_BCa_PartialJackknifeFailuresAreReported() + { + var sampleData = CreateJackknifeSample(); + var boot = CreateJackknifeBootstrap(sampleData, 500); + boot.JackknifeFunction = (data, idx) => + { + if (idx % 5 == 0) throw new InvalidOperationException("Jackknife replicate rejected."); + var list = new List(data); + list.RemoveAt(idx); + return list.ToArray(); + }; + + boot.Run(); + var results = boot.GetConfidenceIntervals(BootstrapCIMethod.BCa); + + Assert.AreEqual(sampleData.Length / 5, boot.FailedJackknifeReplicates); + for (int i = 0; i < results.StatisticResults.Length; i++) + { + Assert.IsTrue(Tools.IsFinite(results.StatisticResults[i].LowerCI), $"Lower CI is not finite for statistic {i}."); + Assert.IsTrue(Tools.IsFinite(results.StatisticResults[i].UpperCI), $"Upper CI is not finite for statistic {i}."); + Assert.IsLessThan(results.StatisticResults[i].UpperCI, results.StatisticResults[i].LowerCI); + } + } + + /// + /// Test that a statistic with no jackknife variation yields a zero acceleration constant, so BCa + /// degenerates to the bias-corrected interval instead of dividing zero by zero. + /// + [TestMethod] + public void Test_BCa_DegenerateJackknifeYieldsBiasCorrectedInterval() + { + var sampleData = CreateJackknifeSample(); + var boot = CreateJackknifeBootstrap(sampleData, 2000); + + // Returning the whole sample for every index makes each jackknife statistic identical to the + // population estimate, so the jackknife second moment is exactly zero. + boot.JackknifeFunction = (data, idx) => (double[])data.Clone(); + + boot.Run(); + var bca = boot.GetConfidenceIntervals(BootstrapCIMethod.BCa); + var biasCorrected = boot.GetConfidenceIntervals(BootstrapCIMethod.BiasCorrected); + + Assert.AreEqual(0, boot.FailedJackknifeReplicates); + for (int i = 0; i < bca.StatisticResults.Length; i++) + { + Assert.IsTrue(Tools.IsFinite(bca.StatisticResults[i].LowerCI), $"Lower CI is not finite for statistic {i}."); + Assert.IsTrue(Tools.IsFinite(bca.StatisticResults[i].UpperCI), $"Upper CI is not finite for statistic {i}."); + + // With a zero acceleration the BCa limits reduce to the bias-corrected limits; the two + // differ only by the plotting-position offset in the bias proportion, which is O(1/B). + Assert.AreEqual(biasCorrected.StatisticResults[i].LowerCI, bca.StatisticResults[i].LowerCI, + 0.01 * Math.Abs(biasCorrected.StatisticResults[i].LowerCI)); + Assert.AreEqual(biasCorrected.StatisticResults[i].UpperCI, bca.StatisticResults[i].UpperCI, + 0.01 * Math.Abs(biasCorrected.StatisticResults[i].UpperCI)); + } + } + + /// + /// Test that two BCa runs over the same fixture produce bit-identical bounds. The jackknife + /// reduction must not depend on the thread scheduler. + /// + [TestMethod] + public void Test_BCa_BoundsAreBitReproducible() + { + var sampleData = CreateJackknifeSample(); + + var first = RunReproducibleBCa(sampleData); + var second = RunReproducibleBCa(sampleData); + + Assert.HasCount(first.Length, second); + for (int i = 0; i < first.Length; i++) + { + Assert.AreEqual(BitConverter.DoubleToInt64Bits(first[i].LowerCI), BitConverter.DoubleToInt64Bits(second[i].LowerCI), + $"Lower CI is not bit-identical for statistic {i}."); + Assert.AreEqual(BitConverter.DoubleToInt64Bits(first[i].UpperCI), BitConverter.DoubleToInt64Bits(second[i].UpperCI), + $"Upper CI is not bit-identical for statistic {i}."); + } + } + + /// + /// Runs a complete BCa analysis over a fresh bootstrap instance. + /// + /// The observed sample. + /// The BCa statistic results. + private static BootstrapStatisticResult[] RunReproducibleBCa(double[] sampleData) + { + var boot = CreateJackknifeBootstrap(sampleData, 1000); + boot.JackknifeFunction = (data, idx) => + { + var list = new List(data); + list.RemoveAt(idx); + return list.ToArray(); + }; + + boot.Run(); + return boot.GetConfidenceIntervals(BootstrapCIMethod.BCa).StatisticResults; + } + } } From e9a3f277ae17f92e2b83608c70f1da6e4d444423 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 24 Aug 2026 15:05:05 -0600 Subject: [PATCH 060/222] Make the tie-sensitive k-NN, MLSL and MCMC initialization sorts deterministic List.Sort and Array.Sort are unstable introspective sorts, so the relative order of equal keys is implementation-defined and can differ between target frameworks. Three sites take the front of such a sort and feed it into a numeric result: the two k-NN distance sorts, the MLSL reduced sample, and the MCMC chain starting states. Give the k-NN sorts the explicit training-index secondary key already used in ShuffledComplexEvolution, and switch the MLSL and MCMC sorts to the stable OrderBy and OrderByDescending over the same keys. Distinct keys order identically either way, so no reference result moves. The MAP initialization branch takes its draws in proposal order and has no sort to fix. Add three tests that force exact ties larger than the framework insertion-sort threshold and assert the tied elements keep their generation order. --- .../Supervised/KNearestNeighbors.cs | 16 +++-- .../Mathematics/Optimization/Global/MLSL.cs | 6 +- Numerics/Sampling/MCMC/Base/MCMCSampler.cs | 10 +++- .../Machine Learning/Supervised/Test_kNN.cs | 58 +++++++++++++++++++ .../Optimization/Global/Test_MLSL.cs | 49 ++++++++++++++++ .../Sampling/MCMC/Test_MCMCInitialization.cs | 45 ++++++++++++++ 6 files changed, 177 insertions(+), 7 deletions(-) diff --git a/Numerics/Machine Learning/Supervised/KNearestNeighbors.cs b/Numerics/Machine Learning/Supervised/KNearestNeighbors.cs index 6899a595..b271aa2c 100644 --- a/Numerics/Machine Learning/Supervised/KNearestNeighbors.cs +++ b/Numerics/Machine Learning/Supervised/KNearestNeighbors.cs @@ -257,8 +257,12 @@ public KNearestNeighbors(Matrix x, Vector y, int k) items[idx].Distance = Tools.Distance(point, xTrain.Row(idx)); }); - // Sort items and find the k-nearest neighbors - Array.Sort(items, (a, b) => a.Distance.CompareTo(b.Distance)); + // Sort items and find the k-nearest neighbors. + // Array.Sort is an unstable introspective sort, so the training index is used as an + // explicit secondary key. Duplicated rows and coded features produce exact distance + // ties, and without the secondary key the selected neighbors would be + // implementation-defined and could differ between target frameworks. + Array.Sort(items, (a, b) => { int c = a.Distance.CompareTo(b.Distance); return c != 0 ? c : a.Index.CompareTo(b.Index); }); for (int j = 0; j < K; j++) { result[i * K + j] = items[j].Index; @@ -291,8 +295,12 @@ public KNearestNeighbors(Matrix x, Vector y, int k) items[idx].Distance = Tools.Distance(point, xTrain.Row(idx)); }); - // Sort items and find the k-nearest neighbors - Array.Sort(items, (a, b) => a.Distance.CompareTo(b.Distance)); + // Sort items and find the k-nearest neighbors. + // Array.Sort is an unstable introspective sort, so the training index is used as an + // explicit secondary key. Duplicated rows and coded features produce exact distance + // ties, and without the secondary key the neighbors feeding the regression average or + // the classification vote would be implementation-defined. + Array.Sort(items, (a, b) => { int c = a.Distance.CompareTo(b.Distance); return c != 0 ? c : a.Index.CompareTo(b.Index); }); var knn = new double[K]; // Record results diff --git a/Numerics/Mathematics/Optimization/Global/MLSL.cs b/Numerics/Mathematics/Optimization/Global/MLSL.cs index b4e8c7e0..a99b002d 100644 --- a/Numerics/Mathematics/Optimization/Global/MLSL.cs +++ b/Numerics/Mathematics/Optimization/Global/MLSL.cs @@ -233,7 +233,11 @@ protected override void Optimize() // Select the γkN points with the lowest objective function values. // This resultant set, Rk, is called the reduced sample. - SampledPoints.Sort((x, y) => x.ParameterSet.Fitness.CompareTo(y.ParameterSet.Fitness)); + // List.Sort is an unstable introspective sort, and the reduced sample is truncated + // exactly at the sort boundary, so ties would decide which points start local searches + // and therefore which optimum is returned. OrderBy is a stable sort, so equally fit + // points keep the order in which they were sampled. Do not replace it with Sort. + SampledPoints = SampledPoints.OrderBy(x => x.ParameterSet.Fitness).ToList(); int gkN = (int)Math.Ceiling(Gamma * (Iterations + 1) * N); var Rk = SampledPoints.Take(gkN).ToList(); diff --git a/Numerics/Sampling/MCMC/Base/MCMCSampler.cs b/Numerics/Sampling/MCMC/Base/MCMCSampler.cs index 7be26fc0..c801aa71 100644 --- a/Numerics/Sampling/MCMC/Base/MCMCSampler.cs +++ b/Numerics/Sampling/MCMC/Base/MCMCSampler.cs @@ -460,8 +460,14 @@ protected virtual ParameterSet[] InitializeChains() tempPopulation.Add(new ParameterSet((double[])parameters.Clone(), logLH)); } - // Sort temp population by log-likelihood in descending order - tempPopulation.Sort((x, y) => -1 * x.Fitness.CompareTo(y.Fitness)); + // Sort temp population by log-likelihood in descending order. + // List.Sort is an unstable introspective sort, and the chain starting states are taken from + // the front of this list, so ties would decide the starting states and with them the entire + // trajectory of every chain. A wide prior can leave many draws at exactly negative infinity. + // OrderByDescending is a stable sort using the same default double comparison, so the order + // of distinct log-likelihoods is unchanged and ties keep their draw order. Do not replace it + // with Sort. + tempPopulation = tempPopulation.OrderByDescending(x => x.Fitness).ToList(); // Set the initial vectors to the best performing parameter sets for (int i = 0; i < NumberOfChains; i++) diff --git a/Test_Numerics/Machine Learning/Supervised/Test_kNN.cs b/Test_Numerics/Machine Learning/Supervised/Test_kNN.cs index 6dc8f883..6c6e18a0 100644 --- a/Test_Numerics/Machine Learning/Supervised/Test_kNN.cs +++ b/Test_Numerics/Machine Learning/Supervised/Test_kNN.cs @@ -168,5 +168,63 @@ public void Test_GetNeighbors_MultiRow() Assert.IsGreaterThanOrEqualTo(6, neighbors[3], $"Second query's 2nd nearest should be in cluster B, got index {neighbors[3]}"); } + /// + /// Verify that exact distance ties are resolved by the lowest training-row index. + /// + /// + /// Duplicated training rows produce bit-identical distances, and the distance sort is an + /// unstable introspective sort, so without an explicit index tie-break the chosen neighbors + /// are implementation-defined and can differ between target frameworks. + /// + [TestMethod] + public void Test_kNN_TiedDistances_UseLowestIndex() + { + // 24 training rows, more than the insertion-sort threshold of the framework sort. + // Rows 0-19 are duplicates at (1, 0), all exactly distance 1 from the query (0, 0). + // Row 20 at (0.5, 0) is the unique nearest row, and rows 21-23 are far away. + int n = 24; + var x1 = new double[n]; + var x2 = new double[n]; + var y = new double[n]; + for (int i = 0; i < 20; i++) + { + x1[i] = 1d; + x2[i] = 0d; + y[i] = 1d; + } + x1[20] = 0.5d; + x2[20] = 0d; + y[20] = 2d; + for (int i = 21; i < n; i++) + { + x1[i] = 10d; + x2[i] = 0d; + y[i] = 3d; + } + + var X_training = new Matrix(new List { x1, x2 }); + var Y_training = new Vector(y); + var knn = new KNearestNeighbors(X_training, Y_training, 3); + + // Confirm the tie is exact before relying on it. + var distances = new double[20]; + for (int i = 0; i < 20; i++) + distances[i] = Tools.Distance(new double[] { 0d, 0d }, X_training.Row(i)); + for (int i = 1; i < 20; i++) + Assert.AreEqual(distances[0], distances[i], 0d, "The duplicated rows must tie exactly."); + + var query = new double[,] { { 0d, 0d } }; + var neighbors = knn.GetNeighbors(query); + Assert.IsNotNull(neighbors); + + // Row 20 is the unique nearest; the other two must be the lowest indices of the tied group. + CollectionAssert.AreEqual(new int[] { 20, 0, 1 }, neighbors); + + // Repeated calls must return the same neighbors. + var repeated = knn.GetNeighbors(query); + Assert.IsNotNull(repeated); + CollectionAssert.AreEqual(neighbors, repeated); + } + } } diff --git a/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs b/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs index 4fcd361e..11000686 100644 --- a/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs +++ b/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs @@ -334,5 +334,54 @@ public void Test_TP2() bool match2 = Math.Abs(x - validY) < 1E-4 && Math.Abs(y - validX) < 1E-4; Assert.IsTrue(match1 || match2); } + + /// + /// Test that the sample reduction sort keeps equally fit sample points in the order they were generated. + /// + /// + /// A constant objective function makes every sampled point tie exactly. The reduced sample is + /// truncated at the sort boundary, so under an unstable sort the ties would decide which points + /// start local searches, and therefore which optimum is returned. + /// + [TestMethod] + public void Test_TiedFitnessPreservesSampleOrder() + { + var initial = new double[] { 0.5d, 0.5d }; + var lower = new double[] { 0d, 0d }; + var upper = new double[] { 1d, 1d }; + + // Record the evaluated parameter arrays by reference. A sample point stores the same array + // instance that was handed to the objective function, so the first occurrence of an array + // in this list is the position at which that sample point was added. + var evaluated = new List(); + var sync = new object(); + var solver = new MLSL(x => { lock (sync) { evaluated.Add(x); } return 1d; }, 2, initial, lower, upper) + { + // A small reduction parameter keeps the number of local searches down. + Gamma = 0.02, + ReportFailure = false + }; + solver.Minimize(); + + // Each iteration appends exactly one generation of sample points. + Assert.AreEqual(0, solver.SampledPoints.Count % solver.SampleSize); + + // Every point ties, and the tied group is larger than the insertion-sort threshold of the + // framework sort, so an unstable sort is free to permute it. + foreach (var point in solver.SampledPoints) + Assert.AreEqual(1d, point.ParameterSet.Fitness); + Assert.IsGreaterThan(16, solver.SampledPoints.Count); + + // The sorted sample must still be in generation order. + int previous = -1; + for (int i = 0; i < solver.SampledPoints.Count; i++) + { + var values = solver.SampledPoints[i].ParameterSet.Values; + int index = evaluated.FindIndex(v => ReferenceEquals(v, values)); + Assert.IsGreaterThanOrEqualTo(0, index, "Every sample point must have been evaluated."); + Assert.IsGreaterThan(previous, index, $"Sample point {i} is out of generation order."); + previous = index; + } + } } } diff --git a/Test_Numerics/Sampling/MCMC/Test_MCMCInitialization.cs b/Test_Numerics/Sampling/MCMC/Test_MCMCInitialization.cs index 802d8204..6e2a29d1 100644 --- a/Test_Numerics/Sampling/MCMC/Test_MCMCInitialization.cs +++ b/Test_Numerics/Sampling/MCMC/Test_MCMCInitialization.cs @@ -97,6 +97,51 @@ public void NutsInitializationUsesConfiguredGradientAndReducesLikelihoodWork() Assert.IsGreaterThan(analyticalLikelihoodEvaluations, numericalLikelihoodEvaluations); } + /// + /// Verifies that randomized initialization resolves log-likelihood ties by draw order. + /// + /// + /// A wide prior can leave most of the initial population at exactly negative infinity. The chain + /// starting states are taken from the front of the sorted population, so under an unstable sort + /// the tied draws that seed the chains would be implementation-defined, and with them the entire + /// trajectory of every chain. + /// + [TestMethod] + public void RandomizedInitializationBreaksLogLikelihoodTiesByDrawOrder() + { + var evaluationOrder = new List(); + var sampler = new InitializableRwmh( + new List { new Uniform(-5d, 5d) }, + parameters => + { + evaluationOrder.Add((double[])parameters.Clone()); + + // Only the twenty-first draw has finite support. The remaining thirty-nine draws tie + // at exactly negative infinity, which is more than the insertion-sort threshold of + // the framework sort, so an unstable sort is free to permute them. + return evaluationOrder.Count == 21 ? -1d : double.NegativeInfinity; + }) + { + NumberOfChains = 4, + InitialIterations = 40, + PRNGSeed = 12345, + Initialize = MCMCSampler.InitializationType.Randomize + }; + + var initials = sampler.InitializeOnly(); + + Assert.HasCount(40, evaluationOrder); + Assert.HasCount(4, initials); + + // The single finite draw sorts to the front. + CollectionAssert.AreEqual(evaluationOrder[20], initials[0].Values); + + // The tied draws follow in the order they were generated. + CollectionAssert.AreEqual(evaluationOrder[0], initials[1].Values); + CollectionAssert.AreEqual(evaluationOrder[1], initials[2].Values); + CollectionAssert.AreEqual(evaluationOrder[2], initials[3].Values); + } + /// /// Evaluates the quadratic log-likelihood used to verify MAP fitness and evaluation counts. /// From 5a4bc3eb60ccae834a567fd0f241a081252c78ee Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 24 Aug 2026 15:18:34 -0600 Subject: [PATCH 061/222] Validate leave-one-out statistics and correct the bootstrap exception documentation Route each jackknife statistic through ValidateStatistics inside the replicate try block, so a non-finite or wrong-length statistic becomes a counted failed replicate with its exception preserved instead of poisoning the acceleration moments. Document the BCa throw path on the public GetConfidenceIntervals, name the whole leave-one-out pipeline in the all-failed message, and correct the failed-jackknife and empty-sample documentation to match behavior. --- Numerics/Data/Statistics/Statistics.cs | 2 +- Numerics/Sampling/Bootstrap/Bootstrap.cs | 33 +++++-- Test_Numerics/Sampling/Test_Bootstrap.cs | 117 ++++++++++++++++++++++- 3 files changed, 139 insertions(+), 13 deletions(-) diff --git a/Numerics/Data/Statistics/Statistics.cs b/Numerics/Data/Statistics/Statistics.cs index 06c7ee53..46ac1d5d 100644 --- a/Numerics/Data/Statistics/Statistics.cs +++ b/Numerics/Data/Statistics/Statistics.cs @@ -617,7 +617,7 @@ public static double Percentile(IList data, double k, bool dataIsSorted /// Boolean value indicating if the data is sorted or not. Assumed false, not sorted, by default. /// The k-th percentile. /// Thrown when or is null. - /// Thrown when is empty. + /// Thrown when has entries and is empty. An empty returns an empty array without inspecting . /// Thrown when any entry of is not a finite value in [0,1]. public static double[] Percentile(IList data, IList k, bool dataIsSorted = false) { diff --git a/Numerics/Sampling/Bootstrap/Bootstrap.cs b/Numerics/Sampling/Bootstrap/Bootstrap.cs index 05e28dd7..fd6561dd 100644 --- a/Numerics/Sampling/Bootstrap/Bootstrap.cs +++ b/Numerics/Sampling/Bootstrap/Bootstrap.cs @@ -294,13 +294,16 @@ public Matrix? OriginalCovariance /// /// Gets the number of leave-one-out jackknife replicates that failed while computing the BCa /// acceleration constants on the most recent - /// call that requested . Zero for every other interval method. + /// call that requested . /// /// /// Failed jackknife replicates are excluded from the acceleration sums, so a non-zero count means /// the acceleration constants rest on fewer leave-one-out samples than the sample size implies. /// This is reported separately from , which counts bootstrap - /// resampling failures rather than jackknife failures. + /// resampling failures rather than jackknife failures. The count is zero until BCa intervals have + /// been requested, and a request for any other interval method leaves it unchanged rather than + /// clearing it, so it always describes the most recent BCa computation rather than the most + /// recent call. /// public int FailedJackknifeReplicates => _failedJackknifeReplicates; @@ -898,7 +901,13 @@ private Matrix LinkCovariance(BootstrapFit fit, LinkController linkController) /// The confidence interval method. /// The two-sided alpha level. Default = 0.1, resulting in 90% confidence intervals. /// A object containing confidence intervals for parameters and statistics. - /// Thrown when the requested interval method is incompatible with the last run mode. + /// + /// Thrown when the requested interval method is incompatible with the last run mode, when + /// has not been set, or when is + /// and the acceleration constants cannot be computed because + /// reports a non-positive sample size or every leave-one-out + /// replicate fails. The first leave-one-out failure is preserved as the inner exception. + /// /// Thrown when is not between zero and one. public BootstrapResults GetConfidenceIntervals(BootstrapCIMethod method, double alpha = 0.1) { @@ -1242,7 +1251,11 @@ private double[] ComputeAccelerationConstants(double[] populationEstimates) { var jackData = jackknife(_originalData, idx); var jackFit = fitFunc(jackData); - var jackStats = statistic(jackFit); + + // Validate the leave-one-out statistic the same way the original estimates are + // validated. Inside this try a bad statistic becomes a counted failed replicate + // with its exception preserved, rather than poisoning the moment accumulators. + var jackStats = ValidateStatistics(statistic(jackFit), _numStats); for (int i = 0; i < _numStats; i++) { @@ -1282,7 +1295,7 @@ private double[] ComputeAccelerationConstants(double[] populationEstimates) ? " The first failure was: " + firstJackknifeFailure.Message : string.Empty; throw new InvalidOperationException( - "Every leave-one-out replicate produced by JackknifeFunction failed, so the BCa acceleration constants are undefined." + cause, + "Every leave-one-out replicate failed, so the BCa acceleration constants are undefined. Each replicate applies JackknifeFunction, then FitFunction, then StatisticFunction, and any of the three can be the cause." + cause, firstJackknifeFailure); } @@ -1296,9 +1309,13 @@ private double[] ComputeAccelerationConstants(double[] populationEstimates) thirdMoment += chunkThirdMoments[c][i]; } - // A zero second moment means every jackknife replicate returned the same statistic value, - // so the statistic has no jackknife variation. Zero is the correct limit of the - // acceleration there, and BCa degenerates to the bias-corrected interval. + // Two distinct cases fall to the zero fallback. + // 1. A zero second moment means every leave-one-out replicate returned the same statistic + // value, so the statistic has no jackknife variation. Zero is the correct limit of the + // acceleration there, and BCa degenerates to the bias-corrected interval. + // 2. A non-finite second moment. Each leave-one-out statistic is validated as finite + // above, so this can now only arise from overflow while summing finite squared + // differences. Zero is a defensive fallback, not a modelling statement. a[i] = secondMoment > 0d && Tools.IsFinite(secondMoment) ? thirdMoment / (Math.Pow(secondMoment, 1.5) * 6d) : 0d; diff --git a/Test_Numerics/Sampling/Test_Bootstrap.cs b/Test_Numerics/Sampling/Test_Bootstrap.cs index 6c37a1d0..41c9d6fe 100644 --- a/Test_Numerics/Sampling/Test_Bootstrap.cs +++ b/Test_Numerics/Sampling/Test_Bootstrap.cs @@ -464,6 +464,37 @@ private static Bootstrap CreateJackknifeBootstrap(double[] sampleData, return boot; } + /// + /// Reproduces the BCa limit that a zero acceleration constant would produce for one statistic, + /// from the same bootstrap ensemble the analysis used. + /// + /// The bootstrap values for one statistic. + /// The original statistic estimate. + /// The two-sided alpha level. + /// True for the upper limit, false for the lower limit. + /// The limit implied by an acceleration constant of exactly zero. + /// + /// With a = 0 the BCa denominator 1 - a(Z₀ + z) is exactly one, so the division leaves its + /// numerator bit-unchanged and the adjusted probability is Φ(Z₀ + (Z₀ + z)). Every step below + /// therefore reproduces the library's arithmetic exactly, which lets the caller compare bit + /// patterns instead of choosing a tolerance. + /// + private static double ZeroAccelerationBCaLimit(double[] values, double populationEstimate, double alpha, bool upper) + { + var validValues = values.Where(Tools.IsFinite).ToArray(); + int countLeq = 0; + for (int i = 0; i < validValues.Length; i++) + if (validValues[i] <= populationEstimate) countLeq++; + + double p0 = (double)(countLeq + 1) / (validValues.Length + 1); + Array.Sort(validValues); + + double z0 = Normal.StandardZ(p0); + double z = Normal.StandardZ(upper ? 1d - alpha / 2d : alpha / 2d); + double adjusted = Normal.StandardCDF(z0 + (z0 + z) / 1d); + return Statistics.Percentile(validValues, adjusted, true); + } + /// /// Test that a wholly failed jackknife reports the cause instead of producing NaN acceleration. /// @@ -538,12 +569,90 @@ public void Test_BCa_DegenerateJackknifeYieldsBiasCorrectedInterval() Assert.IsTrue(Tools.IsFinite(bca.StatisticResults[i].LowerCI), $"Lower CI is not finite for statistic {i}."); Assert.IsTrue(Tools.IsFinite(bca.StatisticResults[i].UpperCI), $"Upper CI is not finite for statistic {i}."); - // With a zero acceleration the BCa limits reduce to the bias-corrected limits; the two - // differ only by the plotting-position offset in the bias proportion, which is O(1/B). + // Pin the acceleration to exactly zero. Reproducing the a = 0 limit from the bootstrap + // ensemble is bit-exact, so any non-zero acceleration moves the limit and fails here. + var values = boot.BootstrapStatistics.GetColumn(i); + double populationEstimate = bca.StatisticResults[i].PopulationEstimate; + Assert.AreEqual( + BitConverter.DoubleToInt64Bits(ZeroAccelerationBCaLimit(values, populationEstimate, 0.1d, false)), + BitConverter.DoubleToInt64Bits(bca.StatisticResults[i].LowerCI), + $"The lower CI does not match a zero acceleration for statistic {i}."); + Assert.AreEqual( + BitConverter.DoubleToInt64Bits(ZeroAccelerationBCaLimit(values, populationEstimate, 0.1d, true)), + BitConverter.DoubleToInt64Bits(bca.StatisticResults[i].UpperCI), + $"The upper CI does not match a zero acceleration for statistic {i}."); + + // With a zero acceleration the BCa limits also reduce to the bias-corrected limits. The + // residual gap is not zero because the two methods use different plotting positions for + // the bias proportion — countLeq / (B + 1) versus (countLeq + 1) / (B + 1) — an offset of + // 1/(B+1) in P0 that propagates to roughly 2E-4 in the limit here. The tolerance bounds + // that offset and nothing wider. Assert.AreEqual(biasCorrected.StatisticResults[i].LowerCI, bca.StatisticResults[i].LowerCI, - 0.01 * Math.Abs(biasCorrected.StatisticResults[i].LowerCI)); + 1E-3 * Math.Abs(biasCorrected.StatisticResults[i].LowerCI)); Assert.AreEqual(biasCorrected.StatisticResults[i].UpperCI, bca.StatisticResults[i].UpperCI, - 0.01 * Math.Abs(biasCorrected.StatisticResults[i].UpperCI)); + 1E-3 * Math.Abs(biasCorrected.StatisticResults[i].UpperCI)); + } + } + + /// + /// Test that a leave-one-out replicate whose statistic is non-finite is counted as a failure + /// rather than silently poisoning the jackknife moments. + /// + /// + /// A non-finite statistic drives the second moment to NaN. Because NaN fails every ordered + /// comparison, it would otherwise fall through the zero-acceleration fallback and quietly + /// degenerate BCa to the bias-corrected interval while reporting no failures at all. + /// + [TestMethod] + public void Test_BCa_NonFiniteJackknifeStatisticIsCountedAsFailure() + { + var sampleData = CreateJackknifeSample(); + var boot = CreateJackknifeBootstrap(sampleData, 1000); + + // One leave-one-out index returns a sample two elements short. Bootstrap resamples have the + // full length and every other leave-one-out sample has length n-1, so carrying the length + // through the parameter set's fitness field marks exactly one replicate. + int markerLength = sampleData.Length - 2; + boot.JackknifeFunction = (data, idx) => + { + var list = new List(data); + list.RemoveAt(idx); + if (idx == 7) list.RemoveAt(0); + return list.ToArray(); + }; + + boot.FitFunction = (sample) => + { + var d = new Normal(); + ((IEstimation)d).Estimate(sample, ParameterEstimationMethod.MethodOfMoments); + if (!d.ParametersValid) throw new Exception("Invalid parameters."); + return new ParameterSet(d.GetParameters, sample.Length); + }; + + boot.StatisticFunction = (ps) => + { + if (ps.Fitness == markerLength) return new double[] { double.NaN, double.NaN }; + var d = new Normal(ps.Values[0], ps.Values[1]); + return new double[] { d.InverseCDF(0.5d), d.InverseCDF(0.99d) }; + }; + + boot.Run(); + var results = boot.GetConfidenceIntervals(BootstrapCIMethod.BCa); + + Assert.AreEqual(1, boot.FailedJackknifeReplicates); + for (int i = 0; i < results.StatisticResults.Length; i++) + { + Assert.IsTrue(Tools.IsFinite(results.StatisticResults[i].LowerCI), $"Lower CI is not finite for statistic {i}."); + Assert.IsTrue(Tools.IsFinite(results.StatisticResults[i].UpperCI), $"Upper CI is not finite for statistic {i}."); + + // The surviving 39 replicates still carry jackknife variation, so the acceleration must + // not have collapsed to the zero fallback. + var values = boot.BootstrapStatistics.GetColumn(i); + double populationEstimate = results.StatisticResults[i].PopulationEstimate; + Assert.AreNotEqual( + BitConverter.DoubleToInt64Bits(ZeroAccelerationBCaLimit(values, populationEstimate, 0.1d, false)), + BitConverter.DoubleToInt64Bits(results.StatisticResults[i].LowerCI), + $"The lower CI collapsed to a zero acceleration for statistic {i}."); } } From def4eb908a11b8b343d6e390a4baf7b62bb6207c Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 24 Aug 2026 15:30:22 -0600 Subject: [PATCH 062/222] Cover the k-NN prediction tie-break and keep the MLSL sample list instance The k-NN prediction sort had no tie-forcing test, even though it is the sort that feeds the regression average, the classification vote, and the bootstrap prediction intervals, where bootstrap resampling duplicates rows and guarantees exact distance ties. Add a regression test and a classification test that stack training rows at an identical distance from the query, confirm the tie is exact, and pin the prediction implied by the lowest tied indices. The MLSL tie test used a constant objective, so the whole sample was one tie group and the assertion held whether or not the reduced sample was sorted at all. Give the objective a narrow strictly better strip so the sorted sample has a distinct group ahead of the tied group, and assert both that the better points sort to the front from later generation positions and that each group keeps its sample order. Copy the ordered reduced sample back into the existing SampledPoints list rather than assigning a new list. SampledPoints is public, and replacing it on every iteration leaves a caller that captured the list mid-run holding a detached copy that stops growing. OrderBy still produces the ordering, so no result moves. --- .../Mathematics/Optimization/Global/MLSL.cs | 8 +- .../Machine Learning/Supervised/Test_kNN.cs | 117 ++++++++++++++++++ .../Optimization/Global/Test_MLSL.cs | 102 ++++++++++++--- 3 files changed, 210 insertions(+), 17 deletions(-) diff --git a/Numerics/Mathematics/Optimization/Global/MLSL.cs b/Numerics/Mathematics/Optimization/Global/MLSL.cs index a99b002d..578610dd 100644 --- a/Numerics/Mathematics/Optimization/Global/MLSL.cs +++ b/Numerics/Mathematics/Optimization/Global/MLSL.cs @@ -237,7 +237,13 @@ protected override void Optimize() // exactly at the sort boundary, so ties would decide which points start local searches // and therefore which optimum is returned. OrderBy is a stable sort, so equally fit // points keep the order in which they were sampled. Do not replace it with Sort. - SampledPoints = SampledPoints.OrderBy(x => x.ParameterSet.Fitness).ToList(); + // The ordered result is copied back into the existing list rather than assigned as a + // new list, because SampledPoints is public and a caller holding a reference during a + // run would otherwise be left with a detached list that stops growing. Do not replace + // this with an assignment. + var sorted = SampledPoints.OrderBy(x => x.ParameterSet.Fitness).ToList(); + SampledPoints.Clear(); + SampledPoints.AddRange(sorted); int gkN = (int)Math.Ceiling(Gamma * (Iterations + 1) * N); var Rk = SampledPoints.Take(gkN).ToList(); diff --git a/Test_Numerics/Machine Learning/Supervised/Test_kNN.cs b/Test_Numerics/Machine Learning/Supervised/Test_kNN.cs index 6c6e18a0..f6164a75 100644 --- a/Test_Numerics/Machine Learning/Supervised/Test_kNN.cs +++ b/Test_Numerics/Machine Learning/Supervised/Test_kNN.cs @@ -226,5 +226,122 @@ public void Test_kNN_TiedDistances_UseLowestIndex() CollectionAssert.AreEqual(neighbors, repeated); } + /// + /// Verify that the regression prediction resolves exact distance ties by the lowest training-row index. + /// + /// + /// The prediction path sorts distances independently of the neighbor lookup, and its result feeds + /// the inverse distance weighted average, so an unstable sort would make the predicted value + /// implementation-defined whenever tied rows carry different response values. Bootstrap resampling + /// duplicates rows, so exact ties are guaranteed by construction in the prediction intervals. + /// + [TestMethod] + public void Test_kNNPredict_Regression_TiedDistances_UseLowestIndex() + { + // 24 training rows, more than the insertion-sort threshold of the framework sort. + // Rows 0-19 sit on top of each other at (1, 0), all exactly distance 1 from the query + // (0, 0), but each carries a different response value. Row 20 at (0.5, 0) is the unique + // nearest row, and rows 21-23 are far away. + int n = 24; + var x1 = new double[n]; + var x2 = new double[n]; + var y = new double[n]; + for (int i = 0; i < 20; i++) + { + x1[i] = 1d; + x2[i] = 0d; + y[i] = i; + } + x1[20] = 0.5d; + x2[20] = 0d; + y[20] = 100d; + for (int i = 21; i < n; i++) + { + x1[i] = 10d; + x2[i] = 0d; + y[i] = 999d; + } + + var X_training = new Matrix(new List { x1, x2 }); + var Y_training = new Vector(y); + var knn = new KNearestNeighbors(X_training, Y_training, 3) { IsRegression = true }; + + // Confirm the tie is exact before relying on it. + var query = new double[] { 0d, 0d }; + for (int i = 1; i < 20; i++) + Assert.AreEqual(Tools.Distance(query, X_training.Row(0)), Tools.Distance(query, X_training.Row(i)), 0d, "The stacked rows must tie exactly."); + Assert.AreEqual(0.5d, Tools.Distance(query, X_training.Row(20)), 0d); + + var prediction = knn.Predict(new double[,] { { 0d, 0d } }); + Assert.IsNotNull(prediction); + Assert.HasCount(1, prediction); + + // The three neighbors are row 20 and the two lowest indices of the tied group, rows 0 and 1. + // The inverse distance weights are 1 / 0.5^2 = 4 for row 20 and 1 / 1^2 = 1 for each tied row. + // Any other resolution of the tie changes the result by at least 1 / 6. + double expected = (100d * 4d) / 6d + (0d * 1d) / 6d + (1d * 1d) / 6d; + Assert.AreEqual(expected, prediction[0], 1E-12); + + // Repeated calls must return the same prediction. + var repeated = knn.Predict(new double[,] { { 0d, 0d } }); + Assert.IsNotNull(repeated); + Assert.AreEqual(prediction[0], repeated[0], 0d); + } + + /// + /// Verify that the classification prediction resolves exact distance ties by the lowest training-row index. + /// + /// + /// The classification branch of the prediction path takes the most common response among the + /// selected neighbors, so under an unstable distance sort the predicted class would be + /// implementation-defined whenever more rows tie at the neighbor boundary than there are + /// neighbors to select. + /// + [TestMethod] + public void Test_kNNPredict_Classification_TiedDistances_UseLowestIndex() + { + // Rows 0-19 sit on top of each other at (1, 0), all exactly distance 1 from the query + // (0, 0). Rows 0-2 are class 1 and rows 3-19 are class 2. Rows 20-23 are far away. + int n = 24; + var x1 = new double[n]; + var x2 = new double[n]; + var y = new double[n]; + for (int i = 0; i < 20; i++) + { + x1[i] = 1d; + x2[i] = 0d; + y[i] = i < 3 ? 1d : 2d; + } + for (int i = 20; i < n; i++) + { + x1[i] = 10d; + x2[i] = 0d; + y[i] = 3d; + } + + var X_training = new Matrix(new List { x1, x2 }); + var Y_training = new Vector(y); + var knn = new KNearestNeighbors(X_training, Y_training, 5) { IsRegression = false }; + + // Confirm the tie is exact before relying on it. + var query = new double[] { 0d, 0d }; + for (int i = 1; i < 20; i++) + Assert.AreEqual(Tools.Distance(query, X_training.Row(0)), Tools.Distance(query, X_training.Row(i)), 0d, "The stacked rows must tie exactly."); + + var prediction = knn.Predict(new double[,] { { 0d, 0d } }); + Assert.IsNotNull(prediction); + Assert.HasCount(1, prediction); + + // The five neighbors are the five lowest indices of the tied group, rows 0-4, which vote + // three to two for class 1. Any selection holding fewer than three of rows 0-2 votes for + // class 2 instead. + Assert.AreEqual(1d, prediction[0]); + + // Repeated calls must return the same prediction. + var repeated = knn.Predict(new double[,] { { 0d, 0d } }); + Assert.IsNotNull(repeated); + Assert.AreEqual(prediction[0], repeated[0]); + } + } } diff --git a/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs b/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs index 11000686..31a59faa 100644 --- a/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs +++ b/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs @@ -336,12 +336,15 @@ public void Test_TP2() } /// - /// Test that the sample reduction sort keeps equally fit sample points in the order they were generated. + /// Test that the sample reduction sort orders by fitness and keeps equally fit sample points in the order they were generated. /// /// - /// A constant objective function makes every sampled point tie exactly. The reduced sample is - /// truncated at the sort boundary, so under an unstable sort the ties would decide which points - /// start local searches, and therefore which optimum is returned. + /// The objective function returns the same value everywhere except in a narrow strip of the + /// search space, where it is strictly lower. Most sampled points therefore tie exactly, and a + /// few are distinctly better. The reduced sample is truncated at the sort boundary, so under an + /// unstable sort the ties would decide which points start local searches, and therefore which + /// optimum is returned. The strictly better points are not the first points generated, so the + /// test pins the primary fitness key as well as the tie-break and fails if the sort is removed. /// [TestMethod] public void Test_TiedFitnessPreservesSampleOrder() @@ -355,7 +358,10 @@ public void Test_TiedFitnessPreservesSampleOrder() // in this list is the position at which that sample point was added. var evaluated = new List(); var sync = new object(); - var solver = new MLSL(x => { lock (sync) { evaluated.Add(x); } return 1d; }, 2, initial, lower, upper) + + // The objective is constant except in a narrow strip near the upper bound of the first + // parameter. The initial point is outside the strip, so it belongs to the tied group. + var solver = new MLSL(x => { lock (sync) { evaluated.Add(x); } return x[0] > 0.9d ? 0d : 1d; }, 2, initial, lower, upper) { // A small reduction parameter keeps the number of local searches down. Gamma = 0.02, @@ -365,23 +371,87 @@ public void Test_TiedFitnessPreservesSampleOrder() // Each iteration appends exactly one generation of sample points. Assert.AreEqual(0, solver.SampledPoints.Count % solver.SampleSize); - - // Every point ties, and the tied group is larger than the insertion-sort threshold of the - // framework sort, so an unstable sort is free to permute it. - foreach (var point in solver.SampledPoints) - Assert.AreEqual(1d, point.ParameterSet.Fitness); Assert.IsGreaterThan(16, solver.SampledPoints.Count); - // The sorted sample must still be in generation order. - int previous = -1; + // Resolve the generation order of every sample point. + var generation = new int[solver.SampledPoints.Count]; for (int i = 0; i < solver.SampledPoints.Count; i++) { var values = solver.SampledPoints[i].ParameterSet.Values; - int index = evaluated.FindIndex(v => ReferenceEquals(v, values)); - Assert.IsGreaterThanOrEqualTo(0, index, "Every sample point must have been evaluated."); - Assert.IsGreaterThan(previous, index, $"Sample point {i} is out of generation order."); - previous = index; + generation[i] = evaluated.FindIndex(v => ReferenceEquals(v, values)); + Assert.IsGreaterThanOrEqualTo(0, generation[i], "Every sample point must have been evaluated."); } + + // The objective takes exactly two values, and both groups must be populated for the test to + // pin the primary sort key as well as the tie-break. + int better = solver.SampledPoints.Count(p => p.ParameterSet.Fitness == 0d); + int tied = solver.SampledPoints.Count(p => p.ParameterSet.Fitness == 1d); + Assert.AreEqual(solver.SampledPoints.Count, better + tied); + Assert.IsGreaterThan(0, better, "At least one point must fall in the strictly better strip."); + + // The tied group is larger than the insertion-sort threshold of the framework sort, so an + // unstable sort is free to permute it. + Assert.IsGreaterThan(16, tied); + + // The strictly better points must sort ahead of the tied points. + for (int i = 0; i < better; i++) + Assert.AreEqual(0d, solver.SampledPoints[i].ParameterSet.Fitness, $"Sample point {i} should be in the better group."); + for (int i = better; i < solver.SampledPoints.Count; i++) + Assert.AreEqual(1d, solver.SampledPoints[i].ParameterSet.Fitness, $"Sample point {i} should be in the tied group."); + + // No point of the better group was generated first, so leaving the sample in generation + // order, or sorting on anything other than fitness, fails here. + Assert.IsGreaterThan(0, generation[0], "The best sample point must have moved to the front of the sample."); + + // Within each fitness group the sample must still be in generation order. + for (int i = 1; i < better; i++) + Assert.IsGreaterThan(generation[i - 1], generation[i], $"Better sample point {i} is out of generation order."); + for (int i = better + 1; i < solver.SampledPoints.Count; i++) + Assert.IsGreaterThan(generation[i - 1], generation[i], $"Tied sample point {i} is out of generation order."); + } + + /// + /// Test that the sampled point list keeps the same instance for the whole optimization run. + /// + /// + /// The sample reduction sort runs on every iteration, and is a + /// public property. Sorting into a new list instance would leave a caller that captured the list + /// during a run holding a detached copy that stops growing, so the ordered result must be copied + /// back into the existing list. + /// + [TestMethod] + public void Test_SampledPointsKeepSameListInstance() + { + var initial = new double[] { 0.5d, 0.5d }; + var lower = new double[] { 0d, 0d }; + var upper = new double[] { 1d, 1d }; + + // Record every distinct list instance observed from inside the run. A run that replaces the + // list on each iteration records one instance per iteration. + var instances = new List>(); + var sync = new object(); + MLSL solver = null; + solver = new MLSL(x => + { + lock (sync) + { + var points = solver?.SampledPoints; + if (points != null && (instances.Count == 0 || !ReferenceEquals(instances[instances.Count - 1], points))) + instances.Add(points); + } + return Math.Pow(x[0] - 0.3d, 2d) + Math.Pow(x[1] - 0.7d, 2d); + }, 2, initial, lower, upper) + { + ReportFailure = false + }; + solver.Minimize(); + + // More than one generation of sample points was drawn, so the sort ran at least once. + Assert.IsGreaterThan(solver.SampleSize, solver.SampledPoints.Count); + + // The list instance never changed, and it is the one the solver still exposes. + Assert.HasCount(1, instances); + Assert.IsTrue(ReferenceEquals(instances[0], solver.SampledPoints), "The sampled point list instance must not be replaced during a run."); } } } From f2fcf751e9afdd563b1bf08be08fac7771acf3b8 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 24 Aug 2026 15:45:23 -0600 Subject: [PATCH 063/222] Add the method of moments fit to the Joe and Frank copulas Clayton, AMH and Gumbel each expose SetThetaFromTau, but the other two Archimedean families do not. Add it to Joe and Frank, together with the public theta-to-tau relation each one inverts. IArchimedeanCopula is unchanged. Joe has no closed-form tau relation. The series terms decay like 1/(theta^2 k^3), so a plain truncation still carries about 1E-13 after two million terms. The first 1,000 terms are summed from the smallest upward and the remainder is added in closed form: the term is expanded in inverse powers of the index and each power is summed over the tail with the Euler-Maclaurin form of the Hurwitz zeta function, which leaves a residual below 1E-25. Frank needs the order-1 Debye function. Debye.Function is the order-3 function, so a private order-1 evaluation is used: the Maclaurin series through the twenty-fourth power below one, the exponential form against the pi^2/6 limit above it, and the reflection D1(-y) = D1(y) + y/2 for the negative dependency branch, without which tau diverges instead of approaching -1. Both relations are pinned against pyvinecopulib 0.7.6, the engine behind R's rvinecopulib, and agree to 4.5E-16 for Joe and 3.4E-16 for Frank. A tau the fitting bracket cannot reach throws with the attainable range in the message, and the independence limit is assigned directly rather than handed to a bracket that straddles no root. --- .../Bivariate Copulas/FrankCopula.cs | 152 +++++++++++++++++ .../Bivariate Copulas/JoeCopula.cs | 140 +++++++++++++++ .../Bivariate Copulas/Test_FrankCopula.cs | 160 ++++++++++++++++++ .../Bivariate Copulas/Test_JoeCopula.cs | 146 ++++++++++++++++ 4 files changed, 598 insertions(+) diff --git a/Numerics/Distributions/Bivariate Copulas/FrankCopula.cs b/Numerics/Distributions/Bivariate Copulas/FrankCopula.cs index 670862d0..ec88eb38 100644 --- a/Numerics/Distributions/Bivariate Copulas/FrankCopula.cs +++ b/Numerics/Distributions/Bivariate Copulas/FrankCopula.cs @@ -1,4 +1,5 @@ using Numerics.Data.Statistics; +using Numerics.Mathematics.RootFinding; using System; using System.Collections.Generic; @@ -193,6 +194,157 @@ public override BivariateCopula Clone() return new FrankCopula(Theta, CloneMarginal(MarginalDistributionX), CloneMarginal(MarginalDistributionY)); } + /// + /// The Maclaurin coefficients of the order-1 Debye function for the even powers x², x⁴, ... x²⁴. + /// + /// + /// The k-th entry is B(2k) / ((2k + 1) * (2k)!), where B(2k) is the 2k-th Bernoulli number. The entries + /// were evaluated as exact rationals and rounded once to double. + /// + private static readonly double[] DebyeSeriesCoefficients = + { + 2.7777777777777776E-02, -2.7777777777777778E-04, 4.7241118669690098E-06, + -9.1857730746619641E-08, 1.8978869988971000E-09, -4.0647616451442256E-11, + 8.9216910204564523E-13, -1.9939295860721074E-14, 4.5189800296199183E-16, + -1.0356517612181247E-17, 2.3952186210261870E-19, -5.5817858743250090E-21 + }; + + /// + /// The largest number of exponential terms summed in the large-argument branch of the order-1 Debye function. + /// + private const int DebyeMaximumTerms = 1000; + + /// + /// The size at which an exponential term is small enough to end the large-argument Debye summation. + /// + private const double DebyeTermTolerance = 1E-20; + + /// + /// Returns the order-1 Debye function D₁(x) for any real argument. + /// + /// The argument to evaluate. + /// The order-1 Debye function evaluated at the given argument. + /// + /// + /// The order-1 Debye function is D₁(x) = (1/x) ∫[0 to x] t / (e^t - 1) dt, with D₁(0) = 1. Note that + /// is the order-3 Debye + /// function, not this one, so it cannot be used here. + /// + /// + /// Three branches are used. A negative argument is reduced by the reflection D₁(-y) = D₁(y) + y/2 for + /// y > 0, which is what keeps the negative dependency branch of the Frank copula finite. For + /// 0 < x ≤ 1 the Maclaurin series D₁(x) = 1 - x/4 + Σ[k ≥ 1] B(2k) x^(2k) / ((2k + 1) (2k)!) is used, + /// truncated after x²⁴, where the next term is below 1E-22. For x > 1 the integral is written against + /// its limit π²/6, giving D₁(x) = π²/(6x) - Σ[k ≥ 1] e^(-k x) (1/k + 1/(k² x)), which converges + /// geometrically and is summed until the term falls below 1E-20. + /// + /// + private static double DebyeOrderOne(double x) + { + if (x == 0d) return 1d; + + // D₁(-y) = D₁(y) + y/2 for y > 0. + if (x < 0d) return DebyeOrderOne(-x) - 0.5d * x; + + if (x <= 1d) + { + double squared = x * x; + double power = 1d; + double sum = 0d; + for (int k = 0; k < DebyeSeriesCoefficients.Length; k++) + { + power *= squared; + sum += DebyeSeriesCoefficients[k] * power; + } + return 1d - 0.25d * x + sum; + } + + double remainder = 0d; + for (int k = 1; k <= DebyeMaximumTerms; k++) + { + double term = Math.Exp(-k * x) * (1d / k + 1d / ((double)k * k * x)); + remainder += term; + if (term < DebyeTermTolerance) break; + } + return Math.PI * Math.PI / (6d * x) - remainder; + } + + /// + /// Returns Kendall's τ (tau) implied by the Frank copula dependency parameter θ (theta). + /// + /// The dependency parameter, θ. Must be non-zero. + /// Kendall's τ for the given θ. The value is negative for θ < 0 and positive for θ > 0. + /// + /// + /// The Frank copula relates θ to Kendall's τ through the order-1 Debye function D₁: + /// + /// + /// τ(θ) = 1 - (4 / θ) * (1 - D₁(θ)) + /// + /// + /// The relation is odd in θ, spans τ in (-1, 1) as θ ranges over the real line, and has the removable + /// independence limit τ = 0 at θ = 0, where this expression is indeterminate. The negative branch + /// depends on the reflection D₁(-y) = D₁(y) + y/2; omitting it makes τ diverge instead of approaching + /// -1. The implementation agrees with pyvinecopulib 0.7.6 to 5E-16 or better for |θ| in [0.1, 100]. + /// For |θ| below about 0.01 the leading terms of the expression cancel and the absolute accuracy + /// degrades to about 2E-14, which is far smaller than the τ values involved there. + /// + /// + /// References: + /// + /// + /// + /// Genest, C. (1987). Frank's family of bivariate distributions. Biometrika, 74(3), 549-555. + /// + /// + /// Nelsen, R. B. (2006). An Introduction to Copulas, 2nd ed., Table 4.1. Springer, New York. + /// + /// + /// + public static double KendallsTauFromTheta(double theta) + { + return 1d - 4d / theta * (1d - DebyeOrderOne(theta)); + } + + /// + /// Estimates the dependency parameter using the method of moments. + /// + /// The sample data for the X variable. + /// The sample data for the Y variable. + /// + /// Thrown when Kendall's τ for the sample data lies outside the range the fitting bracket can reach. + /// + /// + /// Kendall's τ is estimated from the sample data and is + /// inverted with Brent's method over the bracket returned by + /// , which is [0.001, 100] for a + /// positive τ and [-100, -0.001] for a negative one. That bracket reaches |τ| in [0.000111, 0.9607], so + /// a |τ| above the upper end means the dependence is too strong to fit within the bracket and throws. + /// The independence limit θ = 0 leaves the Frank generator and distribution functions indeterminate and + /// is excluded from the bracket, so a τ of 0, or any τ too small for the bracket to straddle a root, is + /// assigned the bracket endpoint of matching sign rather than handed to the solver. + /// + public void SetThetaFromTau(IList sampleDataX, IList sampleDataY) + { + var tau = Correlation.KendallsTau(sampleDataX, sampleDataY); + + if (Math.Abs(tau) > KendallsTauFromTheta(100d)) + throw new ArgumentException("For the Frank copula, tau must be in [-0.9607, 0.9607], the range attainable over the fitting bracket θ (theta) of [-100, 100]. The dependency in the data is too strong to use the Frank copula."); + + // θ = 0 is the independence limit and is excluded from the bracket, so a τ smaller than the bracket + // can reach is assigned the endpoint nearest independence. + double nearIndependence = tau < 0d ? -0.001d : 0.001d; + if (Math.Abs(tau) <= Math.Abs(KendallsTauFromTheta(nearIndependence))) + { + Theta = nearIndependence; + return; + } + + double L = tau > 0 ? 0.001d : -100d; + double U = tau > 0 ? 100d : -0.001d; + Theta = Brent.Solve(t => KendallsTauFromTheta(t) - tau, L, U); + } + /// public override double[,] ParameterConstraints(IList sampleDataX, IList sampleDataY) { diff --git a/Numerics/Distributions/Bivariate Copulas/JoeCopula.cs b/Numerics/Distributions/Bivariate Copulas/JoeCopula.cs index 35180a83..c3dbba7f 100644 --- a/Numerics/Distributions/Bivariate Copulas/JoeCopula.cs +++ b/Numerics/Distributions/Bivariate Copulas/JoeCopula.cs @@ -1,4 +1,5 @@ using System; +using Numerics.Data.Statistics; using Numerics.Mathematics.RootFinding; using System.Collections.Generic; @@ -170,6 +171,145 @@ public override BivariateCopula Clone() return new JoeCopula(Theta, CloneMarginal(MarginalDistributionX), CloneMarginal(MarginalDistributionY)); } + /// + /// The number of terms of the Kendall's τ series that are summed directly before the closed-form tail is added. + /// + private const int TauSeriesTerms = 1000; + + /// + /// The highest inverse power of the summation index retained in the closed-form tail of the Kendall's τ series. + /// + private const int TauSeriesTailOrder = 6; + + /// + /// Returns Kendall's τ (tau) implied by the Joe copula dependency parameter θ (theta). + /// + /// The dependency parameter, θ. Must be greater than or equal to 1. + /// Kendall's τ for the given θ. The value is 0 at θ = 1 and increases to 1 as θ → ∞. + /// + /// + /// The Joe copula has no closed-form relation between θ and Kendall's τ. The relation is the series + /// + /// + /// τ(θ) = 1 - 4 * Σ[k = 1 to ∞] 1 / ( k * (θ*k + 2) * (θ*(k - 1) + 2) ) + /// + /// + /// The factor θ*(k - 1) + 2 equals 2 at k = 1, so no admissible θ divides by zero. The Joe copula + /// models positive dependence only, so τ is confined to [0, 1). + /// + /// + /// Truncation. The terms decay like 1 / (θ² k³), so simply truncating the series after K terms + /// leaves an error of about 1 / (2 θ² K²) in the sum, which is 5E-7 at θ = 1 and K = 1,000 and would + /// still be near 1E-13 after two million terms. Truncating on the size of the last term is therefore not + /// sufficient. The first K = 1,000 terms are instead summed directly, from the smallest term to the + /// largest to limit accumulated rounding, and the remainder is added as a closed form: the term is + /// expanded in inverse powers of k, giving 1 / (θ² k³) * Σ[j ≥ 0] (-1)^j h_j / k^j with + /// h_j = Σ[i = 0 to j] p^i q^(j - i), p = 2/θ and q = (2 - θ)/θ, and each power is summed over k > K + /// with the Euler-Maclaurin form of the Hurwitz zeta function. Retaining terms through j = 6 leaves a + /// residual below 1E-25, so the accuracy of the result is limited only by double rounding. The + /// implementation agrees with pyvinecopulib 0.7.6 to 5E-16 or better over θ in [1, 100]. + /// + /// + /// References: + /// + /// + /// + /// Joe, H. (1997). Multivariate Models and Dependence Concepts. Chapman and Hall, London. + /// + /// + /// Nelsen, R. B. (2006). An Introduction to Copulas, 2nd ed. Springer, New York. + /// + /// + /// + public static double KendallsTauFromTheta(double theta) + { + // The terms are summed from the smallest to the largest to limit accumulated rounding error. + double sum = 0d; + for (int k = TauSeriesTerms; k >= 1; k--) + sum += 1d / (k * (theta * k + 2d) * (theta * (k - 1d) + 2d)); + + return 1d - 4d * (sum + TauSeriesTail(theta)); + } + + /// + /// Returns the closed-form remainder of the Kendall's τ series beyond the directly summed terms. + /// + /// The dependency parameter, θ. + /// The sum of the series terms for k greater than . + /// + /// The term 1 / (k * (θ*k + 2) * (θ*(k - 1) + 2)) is written as 1 / (θ² k³ (1 + p/k)(1 + q/k)) with + /// p = 2/θ and q = (2 - θ)/θ, and the product is expanded as Σ[j ≥ 0] (-1)^j h_j / k^j, where the + /// complete homogeneous symmetric polynomials h_j satisfy h_(j+1) = p * h_j + q^(j+1). Both |p| and |q| + /// are far smaller than the truncation point, so the expansion converges geometrically. Each inverse + /// power is summed over the tail with the Euler-Maclaurin expansion of the Hurwitz zeta function + /// ζ(m, a) = a^(1-m)/(m-1) + a^(-m)/2 + m/12 * a^(-m-1) - m(m+1)(m+2)/720 * a^(-m-3) + ..., which is + /// exact to about 1E-25 at a = 1,001. + /// + private static double TauSeriesTail(double theta) + { + double a = TauSeriesTerms + 1d; + double inverse = 1d / a; + double p = 2d / theta; + double q = (2d - theta) / theta; + double h = 1d; + double qPower = 1d; + double aPower = inverse * inverse * inverse; + double sign = 1d; + double tail = 0d; + + for (int j = 0; j <= TauSeriesTailOrder; j++) + { + double m = 3d + j; + double zeta = aPower * a / (m - 1d) + 0.5d * aPower + m / 12d * aPower * inverse + - m * (m + 1d) * (m + 2d) / 720d * aPower * inverse * inverse * inverse; + tail += sign * h * zeta; + + sign = -sign; + qPower *= q; + h = p * h + qPower; + aPower *= inverse; + } + + return tail / (theta * theta); + } + + /// + /// Estimates the dependency parameter using the method of moments. + /// + /// The sample data for the X variable. + /// The sample data for the Y variable. + /// + /// Thrown when Kendall's τ for the sample data lies outside the range the fitting bracket can reach. + /// + /// + /// Kendall's τ is estimated from the sample data and is + /// inverted with Brent's method over the bracket returned by + /// , θ in [1, 100]. That bracket reaches + /// τ in [0, 0.9803]. The Joe copula models positive dependence only, so a negative τ is not attainable + /// at any θ, and a τ above the upper end means the dependence is too strong to fit within the bracket; + /// both throw. A τ of 0 is the independence limit, reached at θ = 1, and is assigned directly rather + /// than handed to a bracket that does not straddle a root. + /// + public void SetThetaFromTau(IList sampleDataX, IList sampleDataY) + { + var tau = Correlation.KendallsTau(sampleDataX, sampleDataY); + + double L = 1d; + double U = 100d; + + if (tau < 0d || tau > KendallsTauFromTheta(U)) + throw new ArgumentException("For the Joe copula, tau must be in [0, 0.9803], the range attainable over the fitting bracket θ (theta) of [1, 100]. The Joe copula models positive dependence only, and the dependency in the data is too strong to use the Joe copula."); + + // τ(1) is 0 up to rounding, so any τ at or below it is the independence limit at θ = 1. + if (tau <= KendallsTauFromTheta(L)) + { + Theta = L; + return; + } + + Theta = Brent.Solve(t => KendallsTauFromTheta(t) - tau, L, U); + } + /// public override double[,] ParameterConstraints(IList sampleDataX, IList sampleDataY) { diff --git a/Test_Numerics/Distributions/Bivariate Copulas/Test_FrankCopula.cs b/Test_Numerics/Distributions/Bivariate Copulas/Test_FrankCopula.cs index 47d814c5..e286a727 100644 --- a/Test_Numerics/Distributions/Bivariate Copulas/Test_FrankCopula.cs +++ b/Test_Numerics/Distributions/Bivariate Copulas/Test_FrankCopula.cs @@ -267,5 +267,165 @@ public void Test_InverseConditionalCDF() } } } + + /// + /// The length of the rank fixtures used by the method of moments tests. + /// + private const int RankFixtureLength = 101; + + /// + /// The number of pairs in a rank fixture, which is the denominator of Kendall's tau without ties. + /// + private const double RankFixturePairs = RankFixtureLength * (RankFixtureLength - 1) / 2d; + + /// + /// Returns a permutation of 0 to n - 1 carrying exactly the requested number of inversions. + /// + /// The length of the permutation. + /// The number of inverted pairs, between 0 and n * (n - 1) / 2. + /// The permutation, as a double array. + /// + /// Paired against the ascending sequence 0 to n - 1, a permutation carrying d inversions has exactly d + /// discordant pairs and no ties, so Kendall's tau of the pair is exactly 1 - 2 d / (n (n - 1) / 2). This + /// gives a fixture whose tau is a chosen rational value, which is what lets the method of moments fit be + /// exercised at a prescribed dependence. The permutation is built from its Lehmer code, taking as many + /// inversions as the remaining positions allow at each step. + /// + private static double[] PermutationWithInversions(int n, int inversions) + { + var available = new List(); + for (int i = 0; i < n; i++) available.Add(i); + + var result = new double[n]; + int remaining = inversions; + for (int i = 0; i < n; i++) + { + int take = Math.Min(remaining, n - 1 - i); + result[i] = available[take]; + available.RemoveAt(take); + remaining -= take; + } + return result; + } + + /// + /// Returns the ascending sequence 0 to n - 1. + /// + /// The length of the sequence. + /// The ascending sequence, as a double array. + private static double[] AscendingRanks(int n) + { + var result = new double[n]; + for (int i = 0; i < n; i++) result[i] = i; + return result; + } + + /// + /// Test Kendall's tau as a function of theta against an external oracle. + /// + /// + /// The reference values are from pyvinecopulib 0.7.6, the Python bindings of vinecopulib, which is the + /// C++ engine behind the R package rvinecopulib. They are an independent implementation of the Frank tau + /// relation and are not derived from the Debye evaluation used here. The negative rows are the ones that + /// pin the Debye reflection: statsmodels 0.14.6 omits it and returns 2.7E6 at theta = -30, while it + /// reproduces the positive rows below to about 3.5E-12. + /// + [TestMethod] + public void Test_KendallsTauFromTheta() + { + Assert.AreEqual(-0.8739774847415348, FrankCopula.KendallsTauFromTheta(-30.0), 1E-12); + Assert.AreEqual(-0.6657773862719784, FrankCopula.KendallsTauFromTheta(-10.0), 1E-12); + Assert.AreEqual(-0.4567009581601168, FrankCopula.KendallsTauFromTheta(-5.0), 1E-12); + Assert.AreEqual(-0.11001853644899295, FrankCopula.KendallsTauFromTheta(-1.0), 1E-12); + Assert.AreEqual(0.11001853644899295, FrankCopula.KendallsTauFromTheta(1.0), 1E-12); + Assert.AreEqual(0.4567009581601168, FrankCopula.KendallsTauFromTheta(5.0), 1E-12); + Assert.AreEqual(0.6657773862719784, FrankCopula.KendallsTauFromTheta(10.0), 1E-12); + Assert.AreEqual(0.8739774847415348, FrankCopula.KendallsTauFromTheta(30.0), 1E-12); + } + + /// + /// Estimate using the method of moments. + /// + [TestMethod] + public void Test_MOM_Fit() + { + var copula = new FrankCopula(); + copula.SetThetaFromTau(data1, data2); + Assert.AreEqual(7.7385956, copula.Theta, 1E-4); + } + + /// + /// Test that the method of moments fit recovers the theta implied by the sample tau, on both dependency branches. + /// + /// + /// Each fixture has a Kendall's tau that is an exact rational, so the fitted theta must reproduce that + /// tau when it is pushed back through the tau relation. The tolerance reflects the root-finding + /// tolerance of Brent's method, not the accuracy of the tau relation. + /// + [TestMethod] + public void Test_SetThetaFromTau_RoundTrip() + { + var ranks = AscendingRanks(RankFixtureLength); + foreach (int inversions in new[] { 200, 1200, 2400, 3800, 4800 }) + { + var permuted = PermutationWithInversions(RankFixtureLength, inversions); + double expected = 1d - 2d * inversions / RankFixturePairs; + Assert.AreEqual(expected, Correlation.KendallsTau(ranks, permuted), 1E-12, $"The fixture with {inversions} inversions must carry the expected tau."); + + var copula = new FrankCopula(); + copula.SetThetaFromTau(ranks, permuted); + Assert.AreEqual(Math.Sign(expected), Math.Sign(copula.Theta), "The fitted theta must take the sign of the sample tau."); + Assert.IsLessThanOrEqualTo(100d, Math.Abs(copula.Theta)); + Assert.AreEqual(expected, FrankCopula.KendallsTauFromTheta(copula.Theta), 1E-7, $"The fit with {inversions} inversions did not recover the sample tau."); + } + } + + /// + /// Test that a tau the fitting bracket cannot reach is rejected. + /// + [TestMethod] + public void Test_SetThetaFromTau_UnattainableTau() + { + var ranks = AscendingRanks(RankFixtureLength); + var copula = new FrankCopula(); + + // Perfect concordance and perfect discordance give tau = 1 and tau = -1, both outside the range the + // bracket theta in [-100, 100] reaches. + var concordant = PermutationWithInversions(RankFixtureLength, 0); + var positive = Assert.ThrowsExactly(() => copula.SetThetaFromTau(ranks, concordant)); + StringAssert.Contains(positive.Message, "[-0.9607, 0.9607]"); + + var discordant = PermutationWithInversions(RankFixtureLength, (int)RankFixturePairs); + var negative = Assert.ThrowsExactly(() => copula.SetThetaFromTau(ranks, discordant)); + StringAssert.Contains(negative.Message, "[-0.9607, 0.9607]"); + } + + /// + /// Test that an independent sample is fitted at the smallest dependency the bracket admits. + /// + /// + /// The Frank independence limit is theta = 0, at which the generator and the distribution functions are + /// indeterminate, so the fit returns the bracket endpoint nearest independence instead. The resulting + /// copula must be indistinguishable from independence over the unit square. + /// + [TestMethod] + public void Test_SetThetaFromTau_Independence() + { + var ranks = AscendingRanks(RankFixtureLength); + var permuted = PermutationWithInversions(RankFixtureLength, (int)RankFixturePairs / 2); + Assert.AreEqual(0d, Correlation.KendallsTau(ranks, permuted), 0d, "Half of the pairs discordant gives a tau of exactly zero."); + + var copula = new FrankCopula(); + copula.SetThetaFromTau(ranks, permuted); + Assert.AreEqual(0.001d, copula.Theta, 0d); + Assert.AreEqual(0d, FrankCopula.KendallsTauFromTheta(copula.Theta), 1E-3); + foreach (double u in new[] { 0.1, 0.3, 0.5, 0.7, 0.9 }) + { + foreach (double v in new[] { 0.1, 0.3, 0.5, 0.7, 0.9 }) + { + Assert.AreEqual(u * v, copula.CDF(u, v), 1E-4, $"The near-independent fit must reproduce the independence copula at ({u}, {v})."); + } + } + } } } diff --git a/Test_Numerics/Distributions/Bivariate Copulas/Test_JoeCopula.cs b/Test_Numerics/Distributions/Bivariate Copulas/Test_JoeCopula.cs index f02e00f1..9a1fa9c6 100644 --- a/Test_Numerics/Distributions/Bivariate Copulas/Test_JoeCopula.cs +++ b/Test_Numerics/Distributions/Bivariate Copulas/Test_JoeCopula.cs @@ -314,5 +314,151 @@ public void Test_InverseConditionalCDF_BoundaryConditionals() } } + /// + /// The length of the rank fixtures used by the method of moments tests. + /// + private const int RankFixtureLength = 101; + + /// + /// The number of pairs in a rank fixture, which is the denominator of Kendall's tau without ties. + /// + private const double RankFixturePairs = RankFixtureLength * (RankFixtureLength - 1) / 2d; + + /// + /// Returns a permutation of 0 to n - 1 carrying exactly the requested number of inversions. + /// + /// The length of the permutation. + /// The number of inverted pairs, between 0 and n * (n - 1) / 2. + /// The permutation, as a double array. + /// + /// Paired against the ascending sequence 0 to n - 1, a permutation carrying d inversions has exactly d + /// discordant pairs and no ties, so Kendall's tau of the pair is exactly 1 - 2 d / (n (n - 1) / 2). This + /// gives a fixture whose tau is a chosen rational value, which is what lets the method of moments fit be + /// exercised at a prescribed dependence. The permutation is built from its Lehmer code, taking as many + /// inversions as the remaining positions allow at each step. + /// + private static double[] PermutationWithInversions(int n, int inversions) + { + var available = new List(); + for (int i = 0; i < n; i++) available.Add(i); + + var result = new double[n]; + int remaining = inversions; + for (int i = 0; i < n; i++) + { + int take = Math.Min(remaining, n - 1 - i); + result[i] = available[take]; + available.RemoveAt(take); + remaining -= take; + } + return result; + } + + /// + /// Returns the ascending sequence 0 to n - 1. + /// + /// The length of the sequence. + /// The ascending sequence, as a double array. + private static double[] AscendingRanks(int n) + { + var result = new double[n]; + for (int i = 0; i < n; i++) result[i] = i; + return result; + } + + /// + /// Test Kendall's tau as a function of theta against an external oracle. + /// + /// + /// The reference values are from pyvinecopulib 0.7.6, the Python bindings of vinecopulib, which is the + /// C++ engine behind the R package rvinecopulib. They are an independent implementation of the Joe tau + /// relation and are not derived from the series evaluated here. vinecopulib rejects a Joe theta above 30 + /// and returns nan at exactly theta = 2, so the table stops at 30 and skips 2. + /// + [TestMethod] + public void Test_KendallsTauFromTheta() + { + Assert.AreEqual(0.0, JoeCopula.KendallsTauFromTheta(1.0), 1E-12); + Assert.AreEqual(0.21927246047709437, JoeCopula.KendallsTauFromTheta(1.5), 1E-12); + Assert.AreEqual(0.5179624982298885, JoeCopula.KendallsTauFromTheta(3.0), 1E-12); + Assert.AreEqual(0.677220746877611, JoeCopula.KendallsTauFromTheta(5.0), 1E-12); + Assert.AreEqual(0.8220439420773361, JoeCopula.KendallsTauFromTheta(10.0), 1E-12); + Assert.AreEqual(0.9059400804989396, JoeCopula.KendallsTauFromTheta(20.0), 1E-12); + Assert.AreEqual(0.9360443756097613, JoeCopula.KendallsTauFromTheta(30.0), 1E-12); + } + + /// + /// Estimate using the method of moments. + /// + [TestMethod] + public void Test_MOM_Fit() + { + var copula = new JoeCopula(); + copula.SetThetaFromTau(data1, data2); + Assert.AreEqual(2.6052516, copula.Theta, 1E-4); + } + + /// + /// Test that the method of moments fit recovers the theta implied by the sample tau. + /// + /// + /// Each fixture has a Kendall's tau that is an exact rational, so the fitted theta must reproduce that + /// tau when it is pushed back through the tau relation. The tolerance reflects the root-finding + /// tolerance of Brent's method, not the accuracy of the tau relation. + /// + [TestMethod] + public void Test_SetThetaFromTau_RoundTrip() + { + var ranks = AscendingRanks(RankFixtureLength); + foreach (int inversions in new[] { 100, 400, 900, 1600, 2400 }) + { + var permuted = PermutationWithInversions(RankFixtureLength, inversions); + double expected = 1d - 2d * inversions / RankFixturePairs; + Assert.AreEqual(expected, Correlation.KendallsTau(ranks, permuted), 1E-12, $"The fixture with {inversions} inversions must carry the expected tau."); + + var copula = new JoeCopula(); + copula.SetThetaFromTau(ranks, permuted); + Assert.IsGreaterThanOrEqualTo(1d, copula.Theta); + Assert.IsLessThanOrEqualTo(100d, copula.Theta); + Assert.AreEqual(expected, JoeCopula.KendallsTauFromTheta(copula.Theta), 1E-7, $"The fit with {inversions} inversions did not recover the sample tau."); + } + } + + /// + /// Test that a tau the fitting bracket cannot reach is rejected. + /// + [TestMethod] + public void Test_SetThetaFromTau_UnattainableTau() + { + var ranks = AscendingRanks(RankFixtureLength); + var copula = new JoeCopula(); + + // Perfect concordance gives tau = 1, above the largest tau the bracket theta in [1, 100] reaches. + var concordant = PermutationWithInversions(RankFixtureLength, 0); + var tooStrong = Assert.ThrowsExactly(() => copula.SetThetaFromTau(ranks, concordant)); + StringAssert.Contains(tooStrong.Message, "[0, 0.9803]"); + + // Perfect discordance gives tau = -1, and the Joe copula models positive dependence only. + var discordant = PermutationWithInversions(RankFixtureLength, (int)RankFixturePairs); + var negative = Assert.ThrowsExactly(() => copula.SetThetaFromTau(ranks, discordant)); + StringAssert.Contains(negative.Message, "[0, 0.9803]"); + } + + /// + /// Test that an independent sample is fitted at the independence limit theta = 1. + /// + [TestMethod] + public void Test_SetThetaFromTau_Independence() + { + var ranks = AscendingRanks(RankFixtureLength); + var permuted = PermutationWithInversions(RankFixtureLength, (int)RankFixturePairs / 2); + Assert.AreEqual(0d, Correlation.KendallsTau(ranks, permuted), 0d, "Half of the pairs discordant gives a tau of exactly zero."); + + var copula = new JoeCopula(); + copula.SetThetaFromTau(ranks, permuted); + Assert.AreEqual(1d, copula.Theta, 0d); + Assert.AreEqual(0d, JoeCopula.KendallsTauFromTheta(copula.Theta), 1E-12); + } + } } From 116fbc2fe7ece6bccbfc2ddc96d685d6dd11e53a Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 24 Aug 2026 16:39:05 -0600 Subject: [PATCH 064/222] Add a thresholded log pseudo-determinant to the singular value decomposition The parameterless LogPseudoDeterminant zeroes a singular value only on an exact comparison against zero, so a rank-deficient matrix whose smallest singular value comes out at roughly 1E-16 folds a large negative term into the sum. The new overload zeroes singular values at the same threshold used by Rank, Nullspace and Solve, keeping the rank, the pseudo-determinant and the pseudo-inverse in agreement. The existing overload keeps its behaviour. --- .../SingularValueDecomposition.cs | 32 ++++++++++++ .../Test_SingularValueDecomp.cs | 51 ++++++++++++++++++- 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/Numerics/Mathematics/Linear Algebra/SingularValueDecomposition.cs b/Numerics/Mathematics/Linear Algebra/SingularValueDecomposition.cs index 6956ffe9..eb15d5e0 100644 --- a/Numerics/Mathematics/Linear Algebra/SingularValueDecomposition.cs +++ b/Numerics/Mathematics/Linear Algebra/SingularValueDecomposition.cs @@ -509,6 +509,14 @@ public double LogDeterminant() /// /// Takes Log determinant of the Matrix W /// + /// + /// This overload treats a singular value as zero only when it compares exactly equal to zero. + /// A singular value that is numerically zero but not exactly zero — a rank-deficient matrix + /// commonly produces one of order 1E-16 rather than 0 — is therefore included in the sum and + /// contributes a large negative term. Use to zero + /// singular values at the same threshold used by and + /// . + /// public double LogPseudoDeterminant() { double det = 0; @@ -517,5 +525,29 @@ public double LogPseudoDeterminant() return det; } + /// + /// Takes the log pseudo-determinant of the Matrix W, after zeroing any singular values smaller + /// than the threshold. + /// + /// The threshold to evaluate. + /// If the threshold is negative, a default value based on estimated roundoff is used. + /// The sum of the logs of the singular values above the threshold. + /// + /// The pseudo-determinant is the product of the nonzero singular values, so this is the + /// log-determinant restricted to the range of A. Sharing the threshold with + /// , and + /// keeps the rank, the pseudo-determinant and the + /// pseudo-inverse in agreement about which singular values are zero, which is required when + /// they are combined — for example in a degenerate multivariate normal density. + /// + public double LogPseudoDeterminant(double threshold) + { + Threshold = (threshold >= 0d ? threshold : 0.5 * Math.Sqrt(m + n + 1d) * W[0] * eps); + double det = 0; + for (int i = 0; i < W.Length; i++) + if (W[i] > Threshold) det += Math.Log((double)W[i]); + return det; + } + } } \ No newline at end of file diff --git a/Test_Numerics/Mathematics/Linear Algebra/Test_SingularValueDecomp.cs b/Test_Numerics/Mathematics/Linear Algebra/Test_SingularValueDecomp.cs index 46d675e7..84080938 100644 --- a/Test_Numerics/Mathematics/Linear Algebra/Test_SingularValueDecomp.cs +++ b/Test_Numerics/Mathematics/Linear Algebra/Test_SingularValueDecomp.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Mathematics.LinearAlgebra; @@ -318,5 +318,54 @@ public void Test_LogPseudoDeterminant() Assert.AreEqual(det, true_det, 0.0001d); } + /// + /// Tests the thresholded log pseudo-determinant against the parameterless overload on a + /// rank-deficient matrix. + /// + /// + /// The matrix below has its third row equal to its first, so it has rank two and its smallest + /// singular value is zero in exact arithmetic. In floating point that singular value comes out at + /// roughly 3E-16 rather than exactly zero, so the parameterless + /// — which zeroes a singular value + /// only on an exact comparison against zero — includes it and returns a large negative number. The + /// thresholded overload zeroes it at the same threshold used by + /// and returns the pseudo-determinant of + /// 4.158312395177701 * 0.8416876048222999, whose log is 1.2527629684953678 (verified against + /// scipy 1.17.1, which reports the same log pseudo-determinant for this matrix). + /// + [TestMethod()] + public void Test_LogPseudoDeterminant_ThresholdedIgnoresNumericallyZeroSingularValues() + { + var A = new Matrix(new double[,] { { 2d, 0.5d, 2d }, { 0.5d, 1d, 0.5d }, { 2d, 0.5d, 2d } }); + var svd = new SingularValueDecomposition(A); + double threshold = svd.Threshold; + + Assert.AreEqual(2, svd.Rank(threshold)); + Assert.AreEqual(1.2527629684953678d, svd.LogPseudoDeterminant(threshold), 1E-12d); + + // The parameterless overload keeps its exact-zero convention, so it still folds in the + // numerically zero singular value and lands far from the pseudo-determinant. + Assert.IsLessThan(-30d, svd.LogPseudoDeterminant()); + + // A negative threshold falls back to the class default, which is the same threshold here. + Assert.AreEqual(1.2527629684953678d, svd.LogPseudoDeterminant(-1d), 1E-12d); + } + + /// + /// Tests the thresholded log pseudo-determinant on a full-rank matrix, where it must agree with the + /// ordinary log determinant. + /// + [TestMethod()] + public void Test_LogPseudoDeterminant_ThresholdedMatchesLogDeterminantAtFullRank() + { + var A = new Matrix(new double[,] { { 4d, 1d, 0.5d }, { 1d, 3d, 0.25d }, { 0.5d, 0.25d, 2d } }); + var svd = new SingularValueDecomposition(A); + double threshold = svd.Threshold; + + Assert.AreEqual(3, svd.Rank(threshold)); + Assert.AreEqual(svd.LogDeterminant(), svd.LogPseudoDeterminant(threshold), 1E-12d); + Assert.AreEqual(3.056356895370426d, svd.LogPseudoDeterminant(threshold), 1E-12d); + } + } } From 3ca7432af10c8c897499b3de011dcc2543210940 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 24 Aug 2026 16:39:12 -0600 Subject: [PATCH 065/222] Support singular value decomposition in the multivariate normal distribution Adds a DecompositionMethod selector with Cholesky and SingularValue members, exposed as an optional constructor argument on new overloads and a get-only property. Cholesky remains the default and its results are unchanged. Under SingularValue the covariance is factorized with a singular value decomposition, so a singular (collinear) covariance is supported. The density follows the scipy convention for a degenerate Gaussian: the normalizing constant uses the rank rather than the dimension, the log pseudo-determinant rather than the log determinant, and the pseudo-inverse in the quadratic form, with zero density off the affine support. Sampling uses A = U*sqrt(W), computed once at factorization. IsPositiveDefinite, ValidateParameters and Clone honour the selector; the Genz MVNDST integrator and the conditional and marginal helpers factorize independently and are unaffected. Resolves #145. --- .../Multivariate/MultivariateNormal.cs | 349 +++++++++++- .../Linear Algebra/DecompositionMethod.cs | 55 ++ .../Multivariate/Test_MultivariateNormal.cs | 511 ++++++++++++++++++ 3 files changed, 901 insertions(+), 14 deletions(-) create mode 100644 Numerics/Mathematics/Linear Algebra/DecompositionMethod.cs diff --git a/Numerics/Distributions/Multivariate/MultivariateNormal.cs b/Numerics/Distributions/Multivariate/MultivariateNormal.cs index 69d5c4e9..175bef11 100644 --- a/Numerics/Distributions/Multivariate/MultivariateNormal.cs +++ b/Numerics/Distributions/Multivariate/MultivariateNormal.cs @@ -60,16 +60,90 @@ public MultivariateNormal(double[] mean, double[,] covariance) SetParameters(mean, covariance); } + /// + /// Constructs a multivariate Gaussian distribution with zero mean vector and identity covariance + /// matrix, factorized with the given decomposition method. + /// + /// The number of dimensions in the distribution. + /// The decomposition method used to factorize the covariance matrix. + /// + /// See for what the selector governs. Added for + /// . + /// + public MultivariateNormal(int dimension, DecompositionMethod decomposition) + { + _decomposition = decomposition; + var mean = new double[dimension]; + SetParameters(mean, Matrix.Identity(dimension).ToArray()); + } + + /// + /// Constructs a new Multivariate Normal distribution with an identity covariance matrix, + /// factorized with the given decomposition method. + /// + /// The mean vector μ (mu) for the distribution. + /// The decomposition method used to factorize the covariance matrix. + /// + /// See for what the selector governs. Added for + /// . + /// + public MultivariateNormal(double[] mean, DecompositionMethod decomposition) + { + _decomposition = decomposition; + SetParameters(mean, Matrix.Identity(mean.Length).ToArray()); + } + + /// + /// Constructs a new Multivariate Normal distribution, factorized with the given decomposition method. + /// + /// The mean vector μ (mu) for the distribution. + /// The covariance matrix Σ (sigma) for the distribution. + /// The decomposition method used to factorize the covariance matrix. + /// + /// accepts a singular (collinear) covariance matrix + /// that rejects. See for what + /// the selector governs and for the degenerate-density convention. Added for + /// . + /// + public MultivariateNormal(double[] mean, double[,] covariance, DecompositionMethod decomposition) + { + _decomposition = decomposition; + SetParameters(mean, covariance); + } + private bool _parametersValid = true; private int _dimension = 0; private double[] _mean = null!; private Matrix _covariance = null!; + private DecompositionMethod _decomposition = DecompositionMethod.Cholesky; private CholeskyDecomposition _cholesky = null!; + private SingularValueDecomposition _svd = null!; + private Matrix _factor = null!; + private Matrix _nullspace = null!; + private double _svdThreshold; + private int _rank; private double _lnconstant; private double[]? _variance; private double[]? _standardDeviation; + /// + /// The multiple of the machine epsilon used when comparing a covariance matrix against its own + /// symmetric reconstruction and when testing whether a point lies on the support of a degenerate + /// distribution. + /// + /// + /// Both comparisons separate quantities that are zero up to accumulated roundoff from quantities + /// that are genuinely nonzero. The multiplier follows the convention used by + /// scipy.stats.multivariate_normal, which scales the double-precision epsilon by 1E6 before + /// deciding that an eigenvalue or a null-space residual is zero. It was chosen with several orders + /// of margin on both sides: the measured roundoff on the reference covariance matrices of + /// is of order 1E-15 relative to the + /// matrix scale, while a genuinely indefinite matrix or a genuinely off-support point misses by an + /// amount of order one. + /// + private const double ZeroToleranceFactor = 1E6; + // variables required for the multivariate CDF private Matrix _correlation = null!; private double[] _correl = null!; @@ -241,27 +315,86 @@ public double[] StandardDeviation get { return _covariance.ToArray(); } } + /// + /// The decomposition method used to factorize the covariance matrix. Default = + /// . + /// + /// + /// + /// The selector is fixed at construction and is honoured by every re-factorization performed by + /// , and . + /// It is deliberately get-only: a setter would have to re-factorize, and between the assignment and + /// the next call to every cached quantity — the normalizing constant and + /// the sampling factor — would be stale. + /// + /// + /// The selector governs the density (, , + /// ), the log-determinant carried in the normalizing constant, and the + /// factor A with A·Aᵀ = Σ used by , + /// , and + /// . It does not govern or + /// : the Genz MVNDST numerical integrator factorizes the correlation matrix + /// internally and is unaffected. Nor does it govern and + /// , which factorize the observed sub-covariance with its own Cholesky + /// decomposition and return distributions that use the default + /// selector. + /// + /// + /// Under the distribution follows the degenerate + /// convention of scipy.stats.multivariate_normal(..., allow_singular=True): the density is + /// taken with respect to Lebesgue measure on the affine support μ + range(Σ), so the normalizing + /// constant uses the rank of Σ rather than , the log pseudo-determinant + /// rather than the log determinant, and the pseudo-inverse rather than the inverse in the quadratic + /// form. A point off that support has density exactly zero, so returns negative + /// infinity and returns zero there. + /// + /// + /// Added for . + /// + /// + public DecompositionMethod Decomposition => _decomposition; + /// /// Determines if the covariance matrix is positive definite. /// - public bool IsPositiveDefinite => _cholesky.IsPositiveDefinite; + /// + /// Under the covariance is only required to be + /// positive semi-definite, so this reports whether the singular value decomposition found the + /// covariance to have full rank. + /// + public bool IsPositiveDefinite => _decomposition == DecompositionMethod.Cholesky + ? _cholesky.IsPositiveDefinite + : _rank == _dimension; /// /// Set the distribution parameters. /// /// The mean vector μ (mu) for the distribution. /// The covariance matrix Σ (sigma) for the distribution. + /// + /// The covariance is factorized with the decomposition method chosen at construction; see + /// . + /// public void SetParameters(double[] mean, double[,] covariance) { // Validate parameters ValidateParameters(mean, covariance, true); - _dimension = mean.Length; + _dimension = mean.Length; _mean = mean; _covariance = new Matrix(covariance); - _cholesky = new CholeskyDecomposition(_covariance); - double lndet = _cholesky.LogDeterminant(); - _lnconstant = -(Math.Log(2d * Math.PI) * _mean.Length + lndet) * 0.5d; + if (_decomposition == DecompositionMethod.Cholesky) + { + _cholesky = new CholeskyDecomposition(_covariance); + double lndet = _cholesky.LogDeterminant(); + _lnconstant = -(Math.Log(2d * Math.PI) * _mean.Length + lndet) * 0.5d; + // The sampling factor A with A*A^T = Sigma is the lower triangular Cholesky factor itself. + _factor = _cholesky.L; + } + else + { + FactorizeWithSingularValues(); + } // Set up parameters for MVN CDF _correlationMatrixCreated = false; @@ -282,6 +415,89 @@ public void SetParameters(double[] mean, double[,] covariance) } + /// + /// Factorizes the covariance matrix with a singular value decomposition and caches every quantity + /// the degenerate density and the sampler need: the rank, the null space, the normalizing constant + /// built on the log pseudo-determinant, and the sampling factor A = U·sqrt(W). + /// + /// + /// + /// The threshold below which a singular value counts as zero is captured once, immediately after the + /// decomposition, and is then passed explicitly to every query. + /// recomputes its on each call that takes a + /// threshold argument, so passing the captured value keeps the rank, the null space, the + /// pseudo-determinant and the pseudo-inverse solve in agreement about which singular values are zero. + /// + /// + /// Because Σ is symmetric positive semi-definite — enforced by — the + /// left and right singular vectors coincide for every singular value above the threshold, so + /// Σ = U·W·Uᵀ and A = U·sqrt(W) satisfies A·Aᵀ = Σ. This is the factor NumPy builds for + /// multivariate_normal(..., method='svd'). Null directions get a zero column and therefore + /// carry no noise, which places every draw on the support of the distribution. + /// + /// + private void FactorizeWithSingularValues() + { + _cholesky = null!; + _svd = new SingularValueDecomposition(_covariance); + _svdThreshold = _svd.Threshold; + _rank = _svd.Rank(_svdThreshold); + _nullspace = _svd.Nullspace(_svdThreshold); + double lndet = _svd.LogPseudoDeterminant(_svdThreshold); + // The normalizing constant uses the rank, not the dimension: the density lives on the + // rank-dimensional affine support mu + range(Sigma). + _lnconstant = -(Math.Log(2d * Math.PI) * _rank + lndet) * 0.5d; + var factor = new Matrix(_dimension, _dimension); + for (int j = 0; j < _dimension; j++) + { + double scale = _svd.W[j] > _svdThreshold ? Math.Sqrt(_svd.W[j]) : 0d; + for (int i = 0; i < _dimension; i++) + factor[i, j] = _svd.U[i, j] * scale; + } + _factor = factor; + } + + /// + /// Determines whether a point lies on the affine support μ + range(Σ) of the distribution. + /// + /// A point in the distribution space. + /// + /// True when the centred point has a negligible component in the null space of Σ. Always true under + /// , where the covariance is positive-definite and the + /// support is the whole space. + /// + /// + /// The centred point is projected onto the orthonormal null-space basis returned by + /// . The projection is compared against + /// times the machine epsilon, scaled by the magnitude of the + /// centred point so that the test stays meaningful for points far from the mean, where the roundoff + /// in the projection grows in proportion. On the reference cases of + /// an on-support point projects to at + /// most 6E-16 while an off-support point projects to more than 0.7, so the test has several orders + /// of margin on both sides. + /// + private bool IsOnSupport(double[] x) + { + // A dimension mismatch is reported by Mahalanobis, which validates the point. + if (_nullspace == null || _nullspace.NumberOfColumns == 0 || x.Length != Dimension) return true; + double norm = 0d; + var z = new double[Dimension]; + for (int i = 0; i < Dimension; i++) + { + z[i] = x[i] - _mean[i]; + norm += z[i] * z[i]; + } + double tolerance = ZeroToleranceFactor * Tools.DoubleMachineEpsilon * Math.Max(1d, Math.Sqrt(norm)); + for (int j = 0; j < _nullspace.NumberOfColumns; j++) + { + double projection = 0d; + for (int i = 0; i < Dimension; i++) + projection += _nullspace[i, j] * z[i]; + if (!(Math.Abs(projection) <= tolerance)) return false; + } + return true; + } + /// /// Create the correlation arrays required for computing the CDF. /// @@ -356,15 +572,73 @@ private void CreateCorrelationMatrix() } } - var chol = new CholeskyDecomposition(m); - if (!chol.IsPositiveDefinite) + if (_decomposition == DecompositionMethod.Cholesky) { - var ex = new ArgumentOutOfRangeException(nameof(Covariance), "Covariance matrix is not positive-definite."); + var chol = new CholeskyDecomposition(m); + if (!chol.IsPositiveDefinite) + { + var ex = new ArgumentOutOfRangeException(nameof(Covariance), "Covariance matrix is not positive-definite."); + if (throwException) throw ex; else return ex; + } + } + else if (!IsSymmetricPositiveSemiDefinite(m)) + { + var ex = new ArgumentOutOfRangeException(nameof(Covariance), "Covariance matrix is not symmetric positive-semi-definite."); if (throwException) throw ex; else return ex; } return null; } + /// + /// Determines whether a matrix is symmetric and positive semi-definite, the requirement of the + /// singular value decomposition path. + /// + /// The candidate covariance matrix. + /// True when the matrix is symmetric with no negative eigenvalue. + /// + /// + /// Singular values are unsigned, so they cannot by themselves distinguish a negative eigenvalue from + /// a positive one. The test instead compares the matrix against its own symmetric reconstruction + /// U·W·Uᵀ. For a symmetric matrix the singular value decomposition returns Wⱼ = |λⱼ| with left and + /// right singular vectors that agree up to the sign of λⱼ, so the reconstruction reproduces the + /// matrix exactly when every eigenvalue is non-negative and misses by about 2·|λ| for each negative + /// eigenvalue. An asymmetric matrix likewise fails to reconstruct. The test therefore checks exactly + /// the property the sampling factor A = U·sqrt(W) depends on, and unlike a sign test on the singular + /// vectors it stays correct when eigenvalues of equal magnitude and opposite sign make the singular + /// subspace ambiguous. + /// + /// + /// The comparison uses times the machine epsilon, scaled by the + /// magnitude of the largest entry so that the test is invariant to the units of the covariance. This + /// tolerates eigenvalues that are negative only by roundoff, matching the convention of + /// scipy.stats.multivariate_normal. + /// + /// + private static bool IsSymmetricPositiveSemiDefinite(Matrix covariance) + { + int n = covariance.NumberOfRows; + double scale = 0d; + for (int i = 0; i < n; i++) + { + for (int j = 0; j < n; j++) + scale = Math.Max(scale, Math.Abs(covariance[i, j])); + } + double tolerance = ZeroToleranceFactor * Tools.DoubleMachineEpsilon * Math.Max(1d, scale); + var svd = new SingularValueDecomposition(covariance); + for (int i = 0; i < n; i++) + { + for (int j = i; j < n; j++) + { + double reconstructed = 0d; + for (int k = 0; k < n; k++) + reconstructed += svd.U[i, k] * svd.W[k] * svd.U[j, k]; + if (!(Math.Abs(reconstructed - covariance[i, j]) <= tolerance)) return false; + if (!(Math.Abs(reconstructed - covariance[j, i]) <= tolerance)) return false; + } + } + return true; + } + /// /// Attempts to set the distribution parameters without throwing: the covariance /// is factorized eagerly, and when it is not positive-definite the density is @@ -555,9 +829,14 @@ private static void ValidateIndices(int[] indices, int dimension) /// The Probability Density Function (PDF) of the distribution evaluated at a point X. /// /// A point in the distribution space. + /// + /// Under a point off the affine support + /// μ + range(Σ) has density exactly zero; see . + /// public override double PDF(double[] x) { if (!_densityValid) return 0d; + if (_decomposition == DecompositionMethod.SingularValue && !IsOnSupport(x)) return 0d; return Math.Exp(-0.5d * Mahalanobis(x) + _lnconstant); } @@ -565,9 +844,14 @@ public override double PDF(double[] x) /// Returns the natural log of the PDF. /// /// The vector of x values. + /// + /// Under a point off the affine support + /// μ + range(Σ) returns negative infinity; see . + /// public override double LogPDF(double[] x) { if (!_densityValid) return double.NegativeInfinity; + if (_decomposition == DecompositionMethod.SingularValue && !IsOnSupport(x)) return double.NegativeInfinity; double f = -0.5d * Mahalanobis(x) + _lnconstant; if (double.IsNaN(f) || double.IsInfinity(f)) return double.NegativeInfinity; return f; @@ -577,15 +861,23 @@ public override double LogPDF(double[] x) /// Gets the Mahalanobis distance between a sample and this distribution. /// /// A point in the distribution space. + /// Thrown when the point is not the dimension of the distribution. + /// + /// Under the quadratic form uses the pseudo-inverse + /// of Σ, which ignores the null directions; a point off the support therefore still returns a finite + /// distance, and it is and that apply the support test. + /// public double Mahalanobis(double[] x) { if (x.Length != Dimension) throw new ArgumentOutOfRangeException(nameof(x), "The vector must be the same dimension as the distribution."); - // + // var z = new double[_mean.Length]; for (int i = 0; i < x.Length; i++) z[i] = x[i] - _mean[i]; - var a = _cholesky.Solve(new Vector(z)); + var a = _decomposition == DecompositionMethod.Cholesky + ? _cholesky.Solve(new Vector(z)) + : _svd.Solve(new Vector(z), _svdThreshold); double b = 0d; for (int i = 0; i < z.Length; i++) b += a[i] * z[i]; @@ -669,6 +961,14 @@ public double Interval(double[] lower, double[] upper) /// The inverse cumulative distribution function (InverseCDF). /// /// Array of probabilities. + /// + /// The probabilities are mapped to standard normal variates z and correlated as x = A·z + μ, where A + /// is the factor with A·Aᵀ = Σ produced by the decomposition method chosen at construction: the + /// Cholesky factor L, or U·sqrt(W) from the singular value decomposition. Under + /// the null directions of a singular covariance carry + /// a zero column, so the result lies on the support of the distribution. See + /// . + /// public double[] InverseCDF(double[] probabilities) { var sample = new double[Dimension]; @@ -677,7 +977,7 @@ public double[] InverseCDF(double[] probabilities) for (int j = 0; j < Dimension; j++) z[j] = Normal.StandardZ(probabilities[j]); // x = A*z + mu - var Az = _cholesky.L * z; + var Az = _factor * z; for (int j = 0; j < Dimension; j++) sample[j] = Az[j] + _mean[j]; return sample; @@ -726,6 +1026,11 @@ public static MultivariateNormal Bivariate(double mu1, double mu2, double sigma1 /// Array of random values. The number of rows are equal to the sample size. /// The number of columns are equal to the dimensions of this distribution. /// + /// + /// The standard normal variates z are correlated as x = A·z + μ, where A is the factor with + /// A·Aᵀ = Σ produced by the decomposition method chosen at construction; see + /// . + /// public double[,] GenerateRandomValues(int sampleSize, int seed = -1) { // Create PRNG for generating random numbers @@ -739,7 +1044,7 @@ public static MultivariateNormal Bivariate(double mu1, double mu2, double sigma1 for (int j = 0; j < Dimension; j++) z[j] = Normal.StandardZ(rnd.NextDouble()); // x = A*z + mu - var Az = _cholesky.L * z; + var Az = _factor * z; for (int j = 0; j < Dimension; j++) sample[i, j] = Az[j] + _mean[j]; } @@ -755,6 +1060,11 @@ public static MultivariateNormal Bivariate(double mu1, double mu2, double sigma1 /// /// Array of random values. /// + /// + /// The standard normal variates z are correlated as x = A·z + μ, where A is the factor with + /// A·Aᵀ = Σ produced by the decomposition method chosen at construction; see + /// . + /// public double[,] LatinHypercubeRandomValues(int sampleSize, int seed) { var r = LatinHypercube.Random(sampleSize, Dimension, seed); @@ -767,7 +1077,7 @@ public static MultivariateNormal Bivariate(double mu1, double mu2, double sigma1 for (int j = 0; j < Dimension; j++) z[j] = Normal.StandardZ(r[i, j]); // x = A*z + mu - var Az = _cholesky.L * z; + var Az = _factor * z; for (int j = 0; j < Dimension; j++) sample[i, j] = Az[j] + _mean[j]; } @@ -780,6 +1090,11 @@ public static MultivariateNormal Bivariate(double mu1, double mu2, double sigma1 /// /// A list of stratification bins. /// Seed for random number generator. + /// + /// The standard normal variates z are correlated as x = A·z + μ, where A is the factor with + /// A·Aᵀ = Σ produced by the decomposition method chosen at construction; see + /// . + /// public double[,] StratifiedRandomValues(List stratificationBins, int seed) { int samplesize = stratificationBins.Count; @@ -802,7 +1117,7 @@ public static MultivariateNormal Bivariate(double mu1, double mu2, double sigma1 } } // x = A*z + mu - var Az = _cholesky.L * z; + var Az = _factor * z; for (int j = 0; j < Dimension; j++) sample[i, j] = Az[j] + _mean[j]; } @@ -2366,7 +2681,13 @@ public override MultivariateDistribution Clone() _dimension = this.Dimension, _mean = this.Mean.ToArray(), _covariance = this._covariance.Clone(), + _decomposition = this._decomposition, _cholesky = this._cholesky, + _svd = this._svd, + _factor = this._factor, + _nullspace = this._nullspace, + _svdThreshold = this._svdThreshold, + _rank = this._rank, _lnconstant = this._lnconstant, _variance = this.Variance.ToArray(), _standardDeviation = this.StandardDeviation.ToArray(), diff --git a/Numerics/Mathematics/Linear Algebra/DecompositionMethod.cs b/Numerics/Mathematics/Linear Algebra/DecompositionMethod.cs new file mode 100644 index 00000000..840e413d --- /dev/null +++ b/Numerics/Mathematics/Linear Algebra/DecompositionMethod.cs @@ -0,0 +1,55 @@ +using System; + +namespace Numerics.Mathematics.LinearAlgebra +{ + + /// + /// The matrix decomposition method used to factorize a symmetric covariance matrix. + /// + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + /// Description: + /// + /// + /// A covariance matrix Σ is factorized to obtain a matrix A satisfying A·Aᵀ = Σ, which correlates + /// independent standard normal variates, and to obtain the log-determinant and the quadratic form + /// required by a Gaussian density. The two members below trade computational speed against the range + /// of supported covariance structures: is the faster factorization but requires + /// a strictly positive-definite matrix, while also handles the singular + /// (perfectly or near-perfectly collinear) covariance matrices that arise, for example, in gridded + /// data where adjacent pixels are perfectly correlated. + /// + /// + /// References: + /// + /// "Numerical Recipes: The Art of Scientific Computing, Third Edition." Press et al., 2017. + /// + /// + /// + /// + /// + /// + [Serializable] + public enum DecompositionMethod + { + /// + /// Cholesky decomposition Σ = L·Lᵀ. This is the faster factorization — roughly twice as efficient + /// as an LU decomposition and considerably cheaper than a singular value decomposition — and is the + /// default. It requires a strictly positive-definite matrix and fails when the matrix is singular, + /// so choose it when the covariance is known to be well conditioned. + /// + Cholesky, + /// + /// Singular value decomposition Σ = U·W·Vᵀ. This is the slower factorization but supports a wider + /// range of covariance structures: it tolerates singular (collinear) matrices by working on the + /// affine support of the distribution, using the rank, the pseudo-determinant and the pseudo-inverse + /// in place of the dimension, the determinant and the inverse. Choose it when perfect or + /// near-perfect correlation can occur, such as gridded data with adjacent, highly correlated cells. + /// + SingularValue + } +} diff --git a/Test_Numerics/Distributions/Multivariate/Test_MultivariateNormal.cs b/Test_Numerics/Distributions/Multivariate/Test_MultivariateNormal.cs index 4057c045..ea799fef 100644 --- a/Test_Numerics/Distributions/Multivariate/Test_MultivariateNormal.cs +++ b/Test_Numerics/Distributions/Multivariate/Test_MultivariateNormal.cs @@ -1,6 +1,7 @@ using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Distributions; +using Numerics.Mathematics.LinearAlgebra; using Numerics.Sampling; namespace Distributions.Multivariate @@ -623,5 +624,515 @@ public void Test_MVNUNI_RejectsNull() var multivariate = new MultivariateNormal(2); Assert.Throws(() => multivariate.MVNUNI = null!); } + + #region Decomposition method selector (issue #145) + + /// + /// Case 1 of the reference set: the mean vector paired with . + /// + private static readonly double[] Case1Mean = { 1d, 2d, 3d }; + + /// + /// Case 1 of the reference set: a non-singular 3-D covariance. + /// + private static readonly double[,] Case1Covariance = + { + { 4d, 1d, 0.5d }, + { 1d, 3d, 0.25d }, + { 0.5d, 0.25d, 2d } + }; + + /// + /// Case 2 of the reference set: a singular 3-D covariance of rank two, where the third component + /// is exactly the first. This is the gridded-data structure described in issue #145. + /// + private static readonly double[,] Case2Covariance = + { + { 2d, 0.5d, 2d }, + { 0.5d, 1d, 0.5d }, + { 2d, 0.5d, 2d } + }; + + /// + /// Case 3 of the reference set: a singular 2-D covariance of rank one — two perfectly correlated + /// components. + /// + private static readonly double[,] Case3Covariance = + { + { 1d, 1d }, + { 1d, 1d } + }; + + /// + /// The tolerance applied to log densities compared against the scipy oracle. The measured + /// disagreement across every reference point is at most 3.6E-15, so this is that measurement + /// rounded out. + /// + private const double OracleTolerance = 1E-14; + + /// + /// Asserts the action throws, and returns the exception, without requiring a specific type + /// (net481-compatible). + /// + /// The action expected to throw. + /// The exception that was thrown. + private static Exception AssertThrowsAny(Action action) + { + try + { + action(); + } + catch (Exception ex) + { + return ex; + } + Assert.Fail("Expected an exception."); + throw new InvalidOperationException("Unreachable."); + } + + /// + /// Builds the singular value decomposition sampling factor A = U*sqrt(W) for a covariance matrix, + /// which is the factor the distribution uses under + /// . + /// + /// The covariance matrix. + /// The factor A, which satisfies A*Aᵀ = covariance. + private static double[,] SingularValueFactor(double[,] covariance) + { + int n = covariance.GetLength(0); + var svd = new SingularValueDecomposition(new Matrix(covariance)); + double threshold = svd.Threshold; + var factor = new double[n, n]; + for (int j = 0; j < n; j++) + { + double scale = svd.W[j] > threshold ? Math.Sqrt(svd.W[j]) : 0d; + for (int i = 0; i < n; i++) + factor[i, j] = svd.U[i, j] * scale; + } + return factor; + } + + /// + /// Verifies that the decomposition selector defaults to Cholesky on every pre-existing constructor + /// and is carried by the new overloads. + /// + [TestMethod] + public void Test_Decomposition_DefaultsToCholesky() + { + Assert.AreEqual(DecompositionMethod.Cholesky, new MultivariateNormal(2).Decomposition); + Assert.AreEqual(DecompositionMethod.Cholesky, new MultivariateNormal(new[] { 0d, 0d }).Decomposition); + Assert.AreEqual(DecompositionMethod.Cholesky, new MultivariateNormal(Case1Mean, Case1Covariance).Decomposition); + + Assert.AreEqual(DecompositionMethod.SingularValue, new MultivariateNormal(2, DecompositionMethod.SingularValue).Decomposition); + Assert.AreEqual(DecompositionMethod.SingularValue, new MultivariateNormal(new[] { 0d, 0d }, DecompositionMethod.SingularValue).Decomposition); + Assert.AreEqual(DecompositionMethod.SingularValue, new MultivariateNormal(Case1Mean, Case1Covariance, DecompositionMethod.SingularValue).Decomposition); + } + + /// + /// Case 1: a non-singular covariance, where the Cholesky and singular value paths are + /// mathematically identical. Both must reproduce the scipy oracle and must agree with each other + /// to near machine precision. + /// + /// + /// Oracle: scipy.stats.multivariate_normal(mean, cov).logpdf(x), scipy 1.17.1. This case is + /// the backward-compatibility anchor for issue #145: the singular value path must not change any + /// answer that the Cholesky path already gets right. + /// + [TestMethod] + public void Test_SVD_Case1_NonSingularMatchesScipyAndCholesky() + { + var points = new[] + { + new[] { 1d, 2d, 3d }, + new[] { 0d, 0d, 0d }, + new[] { 2.5d, 1d, 4d }, + new[] { -1d, 5d, 2d } + }; + var oracle = new[] + { + -4.2849940472992305d, + -6.989405812005113d, + -5.108155812005113d, + -7.226170517887469d + }; + + var cholesky = new MultivariateNormal(Case1Mean, Case1Covariance); + var singular = new MultivariateNormal(Case1Mean, Case1Covariance, DecompositionMethod.SingularValue); + + Assert.IsTrue(cholesky.IsPositiveDefinite); + Assert.IsTrue(singular.IsPositiveDefinite); + + for (int i = 0; i < points.Length; i++) + { + Assert.AreEqual(oracle[i], cholesky.LogPDF(points[i]), OracleTolerance); + Assert.AreEqual(oracle[i], singular.LogPDF(points[i]), OracleTolerance); + Assert.AreEqual(cholesky.LogPDF(points[i]), singular.LogPDF(points[i]), OracleTolerance); + Assert.AreEqual(Math.Exp(oracle[i]), singular.PDF(points[i]), 1E-15d); + } + } + + /// + /// Case 2: a singular covariance of rank two — the gridded-data structure of issue #145, where the + /// third component repeats the first. The singular value path must reproduce the scipy oracle, + /// report the covariance as not positive-definite, and return zero density off the support. + /// + /// + /// Oracle: scipy.stats.multivariate_normal(mean, cov, allow_singular=True).logpdf(x), + /// scipy 1.17.1. The log pseudo-determinant is 1.2527629684953678. + /// + [TestMethod] + public void Test_SVD_Case2_SingularRankTwoMatchesScipy() + { + var mean = new[] { 0d, 0d, 0d }; + var singular = new MultivariateNormal(mean, Case2Covariance, DecompositionMethod.SingularValue); + + Assert.IsFalse(singular.IsPositiveDefinite); + Assert.AreEqual(-2.4642585506570285d, singular.LogPDF(new[] { 0d, 0d, 0d }), OracleTolerance); + Assert.AreEqual(-2.7499728363713145d, singular.LogPDF(new[] { 1d, 0.5d, 1d }), OracleTolerance); + Assert.AreEqual(-3.607115693514173d, singular.LogPDF(new[] { -1d, 1d, -1d }), OracleTolerance); + Assert.AreEqual(Math.Exp(-2.4642585506570285d), singular.PDF(new[] { 0d, 0d, 0d }), 1E-15d); + + // The support is the plane x3 == x1; a point off it has density exactly zero. + Assert.AreEqual(double.NegativeInfinity, singular.LogPDF(new[] { 1d, 0.5d, 1.5d })); + Assert.AreEqual(0d, singular.PDF(new[] { 1d, 0.5d, 1.5d }), 0d); + } + + /// + /// Case 3: a singular covariance of rank one — two perfectly correlated components. The singular + /// value path must reproduce the scipy oracle on the support and return negative infinity off it. + /// + /// + /// Oracle: scipy.stats.multivariate_normal([0, 0], [[1, 1], [1, 1]], allow_singular=True), + /// scipy 1.17.1. The log pseudo-determinant is 0.6931471805599453 = log(2). + /// + [TestMethod] + public void Test_SVD_Case3_SingularRankOneMatchesScipy() + { + var mean = new[] { 0d, 0d }; + var singular = new MultivariateNormal(mean, Case3Covariance, DecompositionMethod.SingularValue); + + Assert.IsFalse(singular.IsPositiveDefinite); + Assert.AreEqual(-1.2655121234846454d, singular.LogPDF(new[] { 0d, 0d }), OracleTolerance); + Assert.AreEqual(-1.7655121234846454d, singular.LogPDF(new[] { 1d, 1d }), OracleTolerance); + Assert.AreEqual(-1.3905121234846454d, singular.LogPDF(new[] { -0.5d, -0.5d }), OracleTolerance); + Assert.AreEqual(Math.Exp(-1.2655121234846454d), singular.PDF(new[] { 0d, 0d }), 1E-15d); + + foreach (var offSupport in new[] { new[] { 1d, -1d }, new[] { 0d, 1d }, new[] { 2d, 1d } }) + { + Assert.AreEqual(double.NegativeInfinity, singular.LogPDF(offSupport)); + Assert.AreEqual(0d, singular.PDF(offSupport), 0d); + } + } + + /// + /// Guards the single most important line of the singular value path: the normalizing constant must + /// use the rank of the covariance matrix, not its dimension. + /// + /// + /// An SVD fallback carried by this class in the repository's first commit (46d5487, removed in + /// ad0b293) computed the constant as + /// -(Math.Log(2d * Math.PI) * _mean.Length + lndet) * 0.5d — the dimension where the rank + /// belongs. Using the dimension shifts the log density by exactly 0.5 * (d - rank) * log(2π), which + /// is 0.9189385332046727 per deficient dimension, a factor of 2.5066282746310002 on the density. + /// Cases 2 and 3 are each rank-deficient by exactly one, so that bug would put every one of their + /// oracle values off by that fixed amount. This test asserts both that the oracle value is + /// reproduced and that the value the old constant would have produced is exactly that far away, so + /// it fails loudly if the constant ever reverts. + /// + [TestMethod] + public void Test_SVD_NormalizingConstantUsesRankNotDimension() + { + double shiftPerDeficientDimension = 0.5d * Math.Log(2d * Math.PI); + Assert.AreEqual(0.9189385332046727d, shiftPerDeficientDimension, 1E-15d); + + // Case 2: dimension 3, rank 2 — deficient by one. + var case2 = new MultivariateNormal(new[] { 0d, 0d, 0d }, Case2Covariance, DecompositionMethod.SingularValue); + double case2Oracle = -2.4642585506570285d; + double case2Actual = case2.LogPDF(new[] { 0d, 0d, 0d }); + Assert.AreEqual(case2Oracle, case2Actual, OracleTolerance); + Assert.IsGreaterThan(0.9d, Math.Abs(case2Actual - (case2Oracle - shiftPerDeficientDimension)), + "The log density must not carry the dimension-based constant."); + + // Case 3: dimension 2, rank 1 — deficient by one. + var case3 = new MultivariateNormal(new[] { 0d, 0d }, Case3Covariance, DecompositionMethod.SingularValue); + double case3Oracle = -1.2655121234846454d; + double case3Actual = case3.LogPDF(new[] { 0d, 0d }); + Assert.AreEqual(case3Oracle, case3Actual, OracleTolerance); + Assert.IsGreaterThan(0.9d, Math.Abs(case3Actual - (case3Oracle - shiftPerDeficientDimension)), + "The log density must not carry the dimension-based constant."); + + // Case 1 is full rank, so rank and dimension agree and the constant is unchanged there. + var case1 = new MultivariateNormal(Case1Mean, Case1Covariance, DecompositionMethod.SingularValue); + Assert.AreEqual(-4.2849940472992305d, case1.LogPDF(new[] { 1d, 2d, 3d }), OracleTolerance); + } + + /// + /// Verifies the sampling factor built by the singular value path reproduces the covariance matrix, + /// A*Aᵀ = Σ, for all three reference cases, and that the distribution actually samples with it. + /// + /// + /// A = U*sqrt(W) is the factor NumPy builds for multivariate_normal(..., method='svd'). + /// Because Σ is symmetric positive semi-definite the left and right singular vectors coincide for + /// every singular value above the threshold, so U*W*Uᵀ = Σ. The measured reconstruction error is at + /// most 2.3E-15 across the three cases. The second half of the test recovers the factor the + /// distribution itself holds by driving with a known + /// vector of standard normal variates. + /// + [TestMethod] + public void Test_SVD_SamplingFactorReproducesCovariance() + { + var covariances = new[] { Case1Covariance, Case2Covariance, Case3Covariance }; + foreach (var covariance in covariances) + { + int n = covariance.GetLength(0); + var factor = SingularValueFactor(covariance); + for (int i = 0; i < n; i++) + { + for (int j = 0; j < n; j++) + { + double sum = 0d; + for (int k = 0; k < n; k++) + sum += factor[i, k] * factor[j, k]; + Assert.AreEqual(covariance[i, j], sum, 1E-14d); + } + } + } + + // The distribution's own factor: InverseCDF maps probabilities to x = A*z + mu. + var probabilities = new[] { 0.15d, 0.62d, 0.93d }; + var z = new double[3]; + for (int j = 0; j < 3; j++) + z[j] = Normal.StandardZ(probabilities[j]); + + var expectedFactor = SingularValueFactor(Case1Covariance); + var singular = new MultivariateNormal(Case1Mean, Case1Covariance, DecompositionMethod.SingularValue); + var actual = singular.InverseCDF(probabilities); + for (int i = 0; i < 3; i++) + { + double expected = Case1Mean[i]; + for (int j = 0; j < 3; j++) + expected += expectedFactor[i, j] * z[j]; + Assert.AreEqual(expected, actual[i], 1E-12d); + } + } + + /// + /// Verifies that seeded draws from a rank-one covariance under the singular value path satisfy the + /// collinearity exactly, which is the property the reporter of issue #145 currently obtains by + /// performing the decomposition by hand. + /// + /// + /// The null direction of Σ receives a zero column in A = U*sqrt(W), so it carries no noise and every + /// draw lands on the support x1 = x2. The measured departure across the seeded sample is at most + /// 8.9E-16, which is roundoff in the matrix-vector product rather than sampling noise. + /// + [TestMethod] + public void Test_SVD_SeededDrawsOnRankOneCovarianceAreCollinear() + { + var singular = new MultivariateNormal(new[] { 0d, 0d }, Case3Covariance, DecompositionMethod.SingularValue); + + var sample = singular.GenerateRandomValues(200, 4321); + for (int i = 0; i < 200; i++) + { + Assert.AreEqual(sample[i, 0], sample[i, 1], 1E-14d); + // Every draw is on the support, so the density there is finite. + Assert.IsGreaterThan(double.NegativeInfinity, singular.LogPDF(new[] { sample[i, 0], sample[i, 1] })); + } + + var latin = singular.LatinHypercubeRandomValues(50, 777); + for (int i = 0; i < 50; i++) + Assert.AreEqual(latin[i, 0], latin[i, 1], 1E-14d); + + var inverse = singular.InverseCDF(new[] { 0.2d, 0.8d }); + Assert.AreEqual(inverse[0], inverse[1], 1E-14d); + } + + /// + /// Pins the seeded output of the default Cholesky path so that any change to the decomposition + /// plumbing that perturbed it would fail here. + /// + /// + /// The expected values were captured from the library immediately before the decomposition selector + /// of issue #145 was added, and are asserted exactly — not within a tolerance — so the Cholesky path + /// is held bit-identical. + /// + [TestMethod] + public void Test_Cholesky_SeededOutputIsUnchanged() + { + var cholesky = new MultivariateNormal(Case1Mean, Case1Covariance); + + var expected = new[,] + { + { 3.9458754639257694d, 4.771800776088886d, 2.7965750395390128d }, + { -1.246107872880183d, -0.05488903400275058d, 0.21419166312288151d }, + { -0.6508844921537047d, 3.146382948321917d, 3.101603919764241d }, + { 1.1609885052730824d, 2.441279589126226d, 5.414221213964924d }, + { 4.611326489815959d, 2.74432691956876d, 3.9917091805407323d } + }; + var sample = cholesky.GenerateRandomValues(5, 12345); + for (int i = 0; i < 5; i++) + { + for (int j = 0; j < 3; j++) + Assert.AreEqual(expected[i, j], sample[i, j], 0d); + } + + var latin = cholesky.LatinHypercubeRandomValues(3, 987); + var expectedLatin = new[,] + { + { 2.742364437119263d, 1.795955104586471d, 2.0437569460738882d }, + { -3.2097827841193878d, 2.29997314661673d, 2.4025926343175597d }, + { 1.359253584263394d, 0.1502253290672808d, 4.165190525051644d } + }; + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + Assert.AreEqual(expectedLatin[i, j], latin[i, j], 0d); + } + + var inverse = cholesky.InverseCDF(new[] { 0.1d, 0.5d, 0.9d }); + Assert.AreEqual(-1.5631031310892016d, inverse[0], 0d); + Assert.AreEqual(1.3592242172276996d, inverse[1], 0d); + Assert.AreEqual(4.460838864683783d, inverse[2], 0d); + + Assert.AreEqual(-4.2849940472992305d, cholesky.LogPDF(new[] { 1d, 2d, 3d }), 0d); + Assert.AreEqual(-6.9894058120051135d, cholesky.LogPDF(new[] { 0d, 0d, 0d }), 0d); + Assert.AreEqual(-5.108155812005113d, cholesky.LogPDF(new[] { 2.5d, 1d, 4d }), 0d); + Assert.AreEqual(-7.226170517887466d, cholesky.LogPDF(new[] { -1d, 5d, 2d }), 0d); + } + + /// + /// Verifies the validation contract of the singular value path: a positive semi-definite covariance + /// is accepted, while an indefinite, an asymmetric, or a non-finite one is still rejected. + /// + /// + /// Singular values are unsigned, so a negative eigenvalue is detected by comparing the matrix + /// against its own symmetric reconstruction U*W*Uᵀ rather than by inspecting the spectrum. The + /// matrix {{0, 2}, {2, 0}} is included because its eigenvalues are +2 and -2: a sign test on + /// the singular vectors is ambiguous there, while the reconstruction test is not. + /// + [TestMethod] + public void Test_SVD_ValidationAcceptsSemiDefiniteAndRejectsIndefinite() + { + // Accepted: singular but positive semi-definite. + var accepted = new MultivariateNormal(new[] { 0d, 0d }, Case3Covariance, DecompositionMethod.SingularValue); + Assert.IsNull(accepted.ValidateParameters(new[] { 0d, 0d }, Case3Covariance, false)); + + // Rejected: a negative eigenvalue. + AssertThrowsOutOfRange(() => new MultivariateNormal(new[] { 0d, 0d }, new[,] { { 1d, 2d }, { 2d, 1d } }, DecompositionMethod.SingularValue)); + AssertThrowsOutOfRange(() => new MultivariateNormal(new[] { 0d, 0d }, new[,] { { -1d, 0d }, { 0d, 2d } }, DecompositionMethod.SingularValue)); + AssertThrowsOutOfRange(() => new MultivariateNormal(new[] { 0d, 0d }, new[,] { { 0d, 2d }, { 2d, 0d } }, DecompositionMethod.SingularValue)); + + // Rejected: asymmetric. + AssertThrowsOutOfRange(() => new MultivariateNormal(new[] { 0d, 0d }, new[,] { { 1d, 0.5d }, { 0.2d, 1d } }, DecompositionMethod.SingularValue)); + + // Rejected: non-finite entries. + AssertThrowsOutOfRange(() => new MultivariateNormal(new[] { 0d, 0d }, new[,] { { 1d, double.NaN }, { double.NaN, 1d } }, DecompositionMethod.SingularValue)); + AssertThrowsOutOfRange(() => new MultivariateNormal(new[] { 0d, 0d }, new[,] { { double.PositiveInfinity, 0d }, { 0d, 1d } }, DecompositionMethod.SingularValue)); + + // Accepted: a well-conditioned covariance at a large scale, where the reconstruction residual + // grows with the magnitude of the entries and the tolerance must scale with it. + var large = new MultivariateNormal(new[] { 0d, 0d }, new[,] { { 4e8d, 1e8d }, { 1e8d, 3e8d } }, DecompositionMethod.SingularValue); + Assert.IsTrue(large.IsPositiveDefinite); + + // The non-throwing path reports the same decision without raising. + var mutable = new MultivariateNormal(new[] { 0d, 0d }, new[,] { { 1d, 0d }, { 0d, 1d } }, DecompositionMethod.SingularValue); + Assert.IsTrue(mutable.TrySetCovariance(Case3Covariance)); + Assert.IsTrue(mutable.IsDensityValid); + Assert.AreEqual(-1.7655121234846454d, mutable.LogPDF(new[] { 1d, 1d }), OracleTolerance); + Assert.IsFalse(mutable.TrySetCovariance(new[,] { { 1d, 2d }, { 2d, 1d } })); + Assert.IsFalse(mutable.IsDensityValid); + } + + /// + /// Documents what the Cholesky path does with the two singular reference covariances, which is the + /// behaviour issue #145 was filed about. + /// + /// + /// Case 3, the rank-one covariance, fails cleanly: the Cholesky factorization reaches a + /// non-positive pivot and throws. Case 2, the rank-two covariance, does not throw — its final + /// pivot evaluates to about 4.4E-16 rather than exactly zero, so the factorization completes with a + /// pivot of order 1E-8 and the resulting log density is wrong by about 17 nats. This test + /// pins both behaviours as they stand today and shows the singular value path getting the right + /// answer where the Cholesky path does not. Neither Cholesky behaviour is changed here. + /// + [TestMethod] + public void Test_Cholesky_BehaviourOnSingularCovariancesIsUnchanged() + { + // Case 3: a clean failure. + var exception = AssertThrowsAny(() => new MultivariateNormal(new[] { 0d, 0d }, Case3Covariance)); + Assert.IsGreaterThanOrEqualTo(0, exception.Message.IndexOf("positive-definite", StringComparison.OrdinalIgnoreCase)); + + // Case 2: no failure, but an unusable density — the silent failure mode the issue describes. + var cholesky = new MultivariateNormal(new[] { 0d, 0d, 0d }, Case2Covariance); + double choleskyLogPdf = cholesky.LogPDF(new[] { 0d, 0d, 0d }); + double oracle = -2.4642585506570285d; + Assert.IsGreaterThan(1d, Math.Abs(choleskyLogPdf - oracle), + "The Cholesky path is expected to be far from the correct density on a rank-deficient covariance."); + + var singular = new MultivariateNormal(new[] { 0d, 0d, 0d }, Case2Covariance, DecompositionMethod.SingularValue); + Assert.AreEqual(oracle, singular.LogPDF(new[] { 0d, 0d, 0d }), OracleTolerance); + } + + /// + /// Verifies that carries the decomposition selector and the + /// factorization that goes with it. + /// + [TestMethod] + public void Test_Decomposition_ClonePreservesTheSelector() + { + var singular = new MultivariateNormal(new[] { 0d, 0d }, Case3Covariance, DecompositionMethod.SingularValue); + var singularClone = (MultivariateNormal)singular.Clone(); + Assert.AreEqual(DecompositionMethod.SingularValue, singularClone.Decomposition); + Assert.IsFalse(singularClone.IsPositiveDefinite); + Assert.AreEqual(-1.7655121234846454d, singularClone.LogPDF(new[] { 1d, 1d }), OracleTolerance); + Assert.AreEqual(double.NegativeInfinity, singularClone.LogPDF(new[] { 1d, -1d })); + var clonedSample = singularClone.GenerateRandomValues(10, 4321); + for (int i = 0; i < 10; i++) + Assert.AreEqual(clonedSample[i, 0], clonedSample[i, 1], 1E-14d); + + var cholesky = new MultivariateNormal(Case1Mean, Case1Covariance); + var choleskyClone = (MultivariateNormal)cholesky.Clone(); + Assert.AreEqual(DecompositionMethod.Cholesky, choleskyClone.Decomposition); + Assert.IsTrue(choleskyClone.IsPositiveDefinite); + var original = cholesky.GenerateRandomValues(5, 12345); + var copied = choleskyClone.GenerateRandomValues(5, 12345); + for (int i = 0; i < 5; i++) + { + for (int j = 0; j < 3; j++) + Assert.AreEqual(original[i, j], copied[i, j], 0d); + } + } + + /// + /// Verifies that the paths the selector deliberately does not govern behave as they do today: the + /// Genz MVNDST integrator behind factorizes the correlation + /// matrix internally, and and + /// factorize the sub-covariance with their own Cholesky + /// decomposition and return Cholesky-based distributions. + /// + [TestMethod] + public void Test_Decomposition_CdfAndConditionalHelpersAreUnaffected() + { + var cholesky = new MultivariateNormal(Case1Mean, Case1Covariance); + var singular = new MultivariateNormal(Case1Mean, Case1Covariance, DecompositionMethod.SingularValue); + + // MVNDST is a randomized lattice rule that advances its generator, so compare first + // evaluations on freshly constructed instances. + Assert.AreEqual(cholesky.CDF(new[] { 2d, 3d, 4d }), singular.CDF(new[] { 2d, 3d, 4d }), 0d); + + // The conditional and marginal helpers return distributions on the default selector. + var conditional = singular.Conditional(new[] { 2 }, new[] { 3d }); + Assert.AreEqual(DecompositionMethod.Cholesky, conditional.Decomposition); + var marginal = singular.Marginal(0, 1); + Assert.AreEqual(DecompositionMethod.Cholesky, marginal.Decomposition); + + // They agree with the Cholesky parent, which is the behaviour they have today. + var choleskyConditional = cholesky.Conditional(new[] { 2 }, new[] { 3d }); + Assert.AreEqual(choleskyConditional.LogPDF(new[] { 1d, 2d }), conditional.LogPDF(new[] { 1d, 2d }), 0d); + var choleskyMarginal = cholesky.Marginal(0, 1); + Assert.AreEqual(choleskyMarginal.LogPDF(new[] { 1d, 2d }), marginal.LogPDF(new[] { 1d, 2d }), 0d); + } + + #endregion } } From 1151c1be90391577d7a4c9525ecabae9c775738d Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 24 Aug 2026 16:53:05 -0600 Subject: [PATCH 066/222] Move the order-1 Debye function into the Debye class and narrow the tau helpers The order-1 Debye evaluation was a private helper inside FrankCopula. A special function does not belong in a copula, so it moves to the Debye class as public static FunctionOrderOne, carried across verbatim: the method body and the twelve Maclaurin coefficients are byte-identical to the version already verified, and the pinned copula oracles are unmoved at 4.441E-16 for Joe and 3.331E-16 for Frank. Add an oracle test for it against mpmath at 60 digits, computed by two independent routes, plus the removable limit at zero and the reflection. Debye's class documentation showed a malformed definition and never stated which order Function implements, which is what sent the Frank work down the wrong path. State plainly that Function is order 3 and FunctionOrderOne is order 1, with the evidence, and cross-reference them. Make both KendallsTauFromTheta methods internal rather than public. Numerics already grants InternalsVisibleTo to Test_Numerics and the assembly is unsigned, so the oracle tests reach them with no new public surface on a published package. Give each one a domain guard, since an inadmissible theta previously returned a silent NaN for Frank and a plausible-looking number for Joe. Correct the accuracy claims in the Frank remarks to the measured values, 1.8E-15 over 0.1 <= |theta| <= 100 and 2.0E-13 at the bracket floor, and separate the oracle-point agreement from the grid bound in the Joe remarks. Quote the guard ranges so they no longer round outward past the bound they describe. Use one sign convention for a zero tau, matching ParameterConstraints. Record that the MLSL sort now leaves the shared list transiently empty. --- .../Bivariate Copulas/FrankCopula.cs | 122 +++++------------- .../Bivariate Copulas/JoeCopula.cs | 21 ++- .../Mathematics/Optimization/Global/MLSL.cs | 6 +- .../Mathematics/Special Functions/Debye.cs | 105 ++++++++++++++- .../Bivariate Copulas/Test_FrankCopula.cs | 11 +- .../Bivariate Copulas/Test_JoeCopula.cs | 4 +- .../Test_SpecialFunctions.cs | 49 ++++++- 7 files changed, 208 insertions(+), 110 deletions(-) diff --git a/Numerics/Distributions/Bivariate Copulas/FrankCopula.cs b/Numerics/Distributions/Bivariate Copulas/FrankCopula.cs index ec88eb38..74c5fcaf 100644 --- a/Numerics/Distributions/Bivariate Copulas/FrankCopula.cs +++ b/Numerics/Distributions/Bivariate Copulas/FrankCopula.cs @@ -1,5 +1,6 @@ using Numerics.Data.Statistics; using Numerics.Mathematics.RootFinding; +using Numerics.Mathematics.SpecialFunctions; using System; using System.Collections.Generic; @@ -194,86 +195,15 @@ public override BivariateCopula Clone() return new FrankCopula(Theta, CloneMarginal(MarginalDistributionX), CloneMarginal(MarginalDistributionY)); } - /// - /// The Maclaurin coefficients of the order-1 Debye function for the even powers x², x⁴, ... x²⁴. - /// - /// - /// The k-th entry is B(2k) / ((2k + 1) * (2k)!), where B(2k) is the 2k-th Bernoulli number. The entries - /// were evaluated as exact rationals and rounded once to double. - /// - private static readonly double[] DebyeSeriesCoefficients = - { - 2.7777777777777776E-02, -2.7777777777777778E-04, 4.7241118669690098E-06, - -9.1857730746619641E-08, 1.8978869988971000E-09, -4.0647616451442256E-11, - 8.9216910204564523E-13, -1.9939295860721074E-14, 4.5189800296199183E-16, - -1.0356517612181247E-17, 2.3952186210261870E-19, -5.5817858743250090E-21 - }; - - /// - /// The largest number of exponential terms summed in the large-argument branch of the order-1 Debye function. - /// - private const int DebyeMaximumTerms = 1000; - - /// - /// The size at which an exponential term is small enough to end the large-argument Debye summation. - /// - private const double DebyeTermTolerance = 1E-20; - - /// - /// Returns the order-1 Debye function D₁(x) for any real argument. - /// - /// The argument to evaluate. - /// The order-1 Debye function evaluated at the given argument. - /// - /// - /// The order-1 Debye function is D₁(x) = (1/x) ∫[0 to x] t / (e^t - 1) dt, with D₁(0) = 1. Note that - /// is the order-3 Debye - /// function, not this one, so it cannot be used here. - /// - /// - /// Three branches are used. A negative argument is reduced by the reflection D₁(-y) = D₁(y) + y/2 for - /// y > 0, which is what keeps the negative dependency branch of the Frank copula finite. For - /// 0 < x ≤ 1 the Maclaurin series D₁(x) = 1 - x/4 + Σ[k ≥ 1] B(2k) x^(2k) / ((2k + 1) (2k)!) is used, - /// truncated after x²⁴, where the next term is below 1E-22. For x > 1 the integral is written against - /// its limit π²/6, giving D₁(x) = π²/(6x) - Σ[k ≥ 1] e^(-k x) (1/k + 1/(k² x)), which converges - /// geometrically and is summed until the term falls below 1E-20. - /// - /// - private static double DebyeOrderOne(double x) - { - if (x == 0d) return 1d; - - // D₁(-y) = D₁(y) + y/2 for y > 0. - if (x < 0d) return DebyeOrderOne(-x) - 0.5d * x; - - if (x <= 1d) - { - double squared = x * x; - double power = 1d; - double sum = 0d; - for (int k = 0; k < DebyeSeriesCoefficients.Length; k++) - { - power *= squared; - sum += DebyeSeriesCoefficients[k] * power; - } - return 1d - 0.25d * x + sum; - } - - double remainder = 0d; - for (int k = 1; k <= DebyeMaximumTerms; k++) - { - double term = Math.Exp(-k * x) * (1d / k + 1d / ((double)k * k * x)); - remainder += term; - if (term < DebyeTermTolerance) break; - } - return Math.PI * Math.PI / (6d * x) - remainder; - } - /// /// Returns Kendall's τ (tau) implied by the Frank copula dependency parameter θ (theta). /// - /// The dependency parameter, θ. Must be non-zero. + /// The dependency parameter, θ. Must be finite and non-zero. /// Kendall's τ for the given θ. The value is negative for θ < 0 and positive for θ > 0. + /// + /// Thrown when θ is zero or is not finite. θ = 0 is the independence limit, at which the relation is + /// indeterminate; it is rejected rather than returned as a silent NaN. + /// /// /// /// The Frank copula relates θ to Kendall's τ through the order-1 Debye function D₁: @@ -285,9 +215,15 @@ private static double DebyeOrderOne(double x) /// The relation is odd in θ, spans τ in (-1, 1) as θ ranges over the real line, and has the removable /// independence limit τ = 0 at θ = 0, where this expression is indeterminate. The negative branch /// depends on the reflection D₁(-y) = D₁(y) + y/2; omitting it makes τ diverge instead of approaching - /// -1. The implementation agrees with pyvinecopulib 0.7.6 to 5E-16 or better for |θ| in [0.1, 100]. - /// For |θ| below about 0.01 the leading terms of the expression cancel and the absolute accuracy - /// degrades to about 2E-14, which is far smaller than the τ values involved there. + /// -1. + /// + /// + /// Accuracy. The eight pinned pyvinecopulib 0.7.6 oracle points are matched to 3.4E-16. + /// Measured against mpmath at 60 decimal digits on a grid over 0.1 ≤ |θ| ≤ 100, the worst + /// absolute error is 1.8E-15, at θ = 0.2. Below |θ| of about 0.01 the leading terms of the expression + /// cancel against each other and the absolute error grows, reaching 2.0E-13 at θ = 0.001, which is the + /// floor of the fitting bracket and where τ itself is only 1.1E-4. The cancellation is a property of + /// this form of the relation rather than of the Debye evaluation. /// /// /// References: @@ -301,9 +237,12 @@ private static double DebyeOrderOne(double x) /// /// /// - public static double KendallsTauFromTheta(double theta) + internal static double KendallsTauFromTheta(double theta) { - return 1d - 4d / theta * (1d - DebyeOrderOne(theta)); + if (theta == 0d || double.IsNaN(theta) || double.IsInfinity(theta)) + throw new ArgumentOutOfRangeException(nameof(theta), "The dependency parameter θ (theta) must be finite and non-zero. θ = 0 is the independence limit, at which the Kendall's tau relation is indeterminate."); + + return 1d - 4d / theta * (1d - Debye.FunctionOrderOne(theta)); } /// @@ -318,22 +257,27 @@ public static double KendallsTauFromTheta(double theta) /// Kendall's τ is estimated from the sample data and is /// inverted with Brent's method over the bracket returned by /// , which is [0.001, 100] for a - /// positive τ and [-100, -0.001] for a negative one. That bracket reaches |τ| in [0.000111, 0.9607], so - /// a |τ| above the upper end means the dependence is too strong to fit within the bracket and throws. - /// The independence limit θ = 0 leaves the Frank generator and distribution functions indeterminate and - /// is excluded from the bracket, so a τ of 0, or any τ too small for the bracket to straddle a root, is - /// assigned the bracket endpoint of matching sign rather than handed to the solver. + /// positive τ and [-100, -0.001] for a non-positive one. That bracket reaches |τ| in + /// about [1.1E-4, 0.96065797], so a |τ| above the upper end means the dependence is too strong to fit + /// within the bracket and throws. The independence limit θ = 0 leaves the Frank generator and + /// distribution functions indeterminate and is excluded from the bracket, so a τ of 0, or any τ too + /// small for the bracket to straddle a root, is assigned the endpoint of its own bracket nearest + /// independence rather than handed to the solver. A τ of exactly 0 takes the negative bracket, matching + /// , and so returns θ = -0.001; at that + /// magnitude the copula is indistinguishable from independence on either branch. /// public void SetThetaFromTau(IList sampleDataX, IList sampleDataY) { var tau = Correlation.KendallsTau(sampleDataX, sampleDataY); if (Math.Abs(tau) > KendallsTauFromTheta(100d)) - throw new ArgumentException("For the Frank copula, tau must be in [-0.9607, 0.9607], the range attainable over the fitting bracket θ (theta) of [-100, 100]. The dependency in the data is too strong to use the Frank copula."); + throw new ArgumentException("For the Frank copula, tau must be in ~= [-0.96065, 0.96065], the range attainable over the fitting bracket θ (theta) of [-100, 100]. The dependency in the data is too strong to use the Frank copula."); // θ = 0 is the independence limit and is excluded from the bracket, so a τ smaller than the bracket - // can reach is assigned the endpoint nearest independence. - double nearIndependence = tau < 0d ? -0.001d : 0.001d; + // can reach is assigned the endpoint nearest independence. The sign test matches the one used by + // ParameterConstraints and by the bracket below, so a τ of exactly 0 resolves the same way in all + // three places. + double nearIndependence = tau > 0d ? 0.001d : -0.001d; if (Math.Abs(tau) <= Math.Abs(KendallsTauFromTheta(nearIndependence))) { Theta = nearIndependence; diff --git a/Numerics/Distributions/Bivariate Copulas/JoeCopula.cs b/Numerics/Distributions/Bivariate Copulas/JoeCopula.cs index c3dbba7f..d0cd377e 100644 --- a/Numerics/Distributions/Bivariate Copulas/JoeCopula.cs +++ b/Numerics/Distributions/Bivariate Copulas/JoeCopula.cs @@ -184,8 +184,13 @@ public override BivariateCopula Clone() /// /// Returns Kendall's τ (tau) implied by the Joe copula dependency parameter θ (theta). /// - /// The dependency parameter, θ. Must be greater than or equal to 1. + /// The dependency parameter, θ. Must be finite and greater than or equal to 1. /// Kendall's τ for the given θ. The value is 0 at θ = 1 and increases to 1 as θ → ∞. + /// + /// Thrown when θ is below or is not finite. The series is defined outside + /// that domain but the value it returns has no meaning for this copula, so an inadmissible θ is + /// rejected rather than returned as a plausible-looking number. + /// /// /// /// The Joe copula has no closed-form relation between θ and Kendall's τ. The relation is the series @@ -206,8 +211,9 @@ public override BivariateCopula Clone() /// expanded in inverse powers of k, giving 1 / (θ² k³) * Σ[j ≥ 0] (-1)^j h_j / k^j with /// h_j = Σ[i = 0 to j] p^i q^(j - i), p = 2/θ and q = (2 - θ)/θ, and each power is summed over k > K /// with the Euler-Maclaurin form of the Hurwitz zeta function. Retaining terms through j = 6 leaves a - /// residual below 1E-25, so the accuracy of the result is limited only by double rounding. The - /// implementation agrees with pyvinecopulib 0.7.6 to 5E-16 or better over θ in [1, 100]. + /// residual below 1E-25, so the accuracy of the result is limited only by double rounding. The seven + /// pinned pyvinecopulib 0.7.6 oracle points are matched to 4.5E-16, and against mpmath at 60 decimal + /// digits on a grid over θ in [1, 100] the worst absolute error is 1.7E-16. /// /// /// References: @@ -221,8 +227,11 @@ public override BivariateCopula Clone() /// /// /// - public static double KendallsTauFromTheta(double theta) + internal static double KendallsTauFromTheta(double theta) { + if (theta < 1d || double.IsNaN(theta) || double.IsInfinity(theta)) + throw new ArgumentOutOfRangeException(nameof(theta), "The dependency parameter θ (theta) must be finite and greater than or equal to 1."); + // The terms are summed from the smallest to the largest to limit accumulated rounding error. double sum = 0d; for (int k = TauSeriesTerms; k >= 1; k--) @@ -285,7 +294,7 @@ private static double TauSeriesTail(double theta) /// Kendall's τ is estimated from the sample data and is /// inverted with Brent's method over the bracket returned by /// , θ in [1, 100]. That bracket reaches - /// τ in [0, 0.9803]. The Joe copula models positive dependence only, so a negative τ is not attainable + /// τ in [0, 0.98025359]. The Joe copula models positive dependence only, so a negative τ is not attainable /// at any θ, and a τ above the upper end means the dependence is too strong to fit within the bracket; /// both throw. A τ of 0 is the independence limit, reached at θ = 1, and is assigned directly rather /// than handed to a bracket that does not straddle a root. @@ -298,7 +307,7 @@ public void SetThetaFromTau(IList sampleDataX, IList sampleDataY double U = 100d; if (tau < 0d || tau > KendallsTauFromTheta(U)) - throw new ArgumentException("For the Joe copula, tau must be in [0, 0.9803], the range attainable over the fitting bracket θ (theta) of [1, 100]. The Joe copula models positive dependence only, and the dependency in the data is too strong to use the Joe copula."); + throw new ArgumentException("For the Joe copula, tau must be in ~= [0, 0.98025], the range attainable over the fitting bracket θ (theta) of [1, 100]. The Joe copula models positive dependence only, and the dependency in the data is too strong to use the Joe copula."); // τ(1) is 0 up to rounding, so any τ at or below it is the independence limit at θ = 1. if (tau <= KendallsTauFromTheta(L)) diff --git a/Numerics/Mathematics/Optimization/Global/MLSL.cs b/Numerics/Mathematics/Optimization/Global/MLSL.cs index 578610dd..eb8216c9 100644 --- a/Numerics/Mathematics/Optimization/Global/MLSL.cs +++ b/Numerics/Mathematics/Optimization/Global/MLSL.cs @@ -240,7 +240,11 @@ protected override void Optimize() // The ordered result is copied back into the existing list rather than assigned as a // new list, because SampledPoints is public and a caller holding a reference during a // run would otherwise be left with a detached list that stops growing. Do not replace - // this with an assignment. + // this with an assignment. The trade-off is that the shared list is transiently empty + // here, so a caller enumerating SampledPoints from another thread during a run could + // see a partial list or an invalidated enumerator where the previous code handed it a + // stable detached snapshot; the sort runs on the optimization thread between the + // sequential sample loop and the local searches, so no in-tree caller is affected. var sorted = SampledPoints.OrderBy(x => x.ParameterSet.Fitness).ToList(); SampledPoints.Clear(); SampledPoints.AddRange(sorted); diff --git a/Numerics/Mathematics/Special Functions/Debye.cs b/Numerics/Mathematics/Special Functions/Debye.cs index 8ec9660a..2127e360 100644 --- a/Numerics/Mathematics/Special Functions/Debye.cs +++ b/Numerics/Mathematics/Special Functions/Debye.cs @@ -12,13 +12,25 @@ namespace Numerics.Mathematics.SpecialFunctions /// /// /// Description: - /// In mathematics, the Debye function is given by the equation: + /// In mathematics, the family of Debye functions is given by the equation: /// /// - /// x - /// D(x) = x/x^n ∫ t^n / (e^t - 1) dt - /// 0 + /// x + /// D_n(x) = n/x^n ∫ t^n / (e^t - 1) dt + /// 0 /// + /// + /// The order n is fixed by each method on this class rather than passed as an argument. + /// is the order-3 Debye function D₃, and + /// is the order-1 Debye function D₁. The two are + /// different functions and are not interchangeable: D₃(1) = 0.6744156 while D₁(1) = 0.7775046. + /// + /// + /// The order of is visible in its own construction. Its small-argument + /// branch is 1 - 0.375 x + 0.05 x², which is the D₃ expansion, where the D₁ expansion is + /// 1 - 0.25 x + x²/36; its large-argument branch normalizes against π⁴/15 = 6 ζ(4), the D₃ limit + /// constant; and its unit test pins Function(1) = 0.6744156, which is D₃(1). + /// /// References: /// /// @@ -29,7 +41,7 @@ namespace Numerics.Mathematics.SpecialFunctions public class Debye { /// - /// Computes the Debye function. + /// Computes the order-3 Debye function D₃(x). /// /// The point in the series to evaluate. /// @@ -45,7 +57,7 @@ public class Debye /// /// /// - /// The Debye function evaluated at the given x + /// The order-3 Debye function evaluated at the given x /// public static double Function(double x) { @@ -90,5 +102,86 @@ public static double Function(double x) } + /// + /// The Maclaurin coefficients of the order-1 Debye function for the even powers x², x⁴, ... x²⁴. + /// + /// + /// The k-th entry is B(2k) / ((2k + 1) * (2k)!), where B(2k) is the 2k-th Bernoulli number. The entries + /// were evaluated as exact rationals and rounded once to double. + /// + private static readonly double[] DebyeSeriesCoefficients = + { + 2.7777777777777776E-02, -2.7777777777777778E-04, 4.7241118669690098E-06, + -9.1857730746619641E-08, 1.8978869988971000E-09, -4.0647616451442256E-11, + 8.9216910204564523E-13, -1.9939295860721074E-14, 4.5189800296199183E-16, + -1.0356517612181247E-17, 2.3952186210261870E-19, -5.5817858743250090E-21 + }; + + /// + /// The largest number of exponential terms summed in the large-argument branch of the order-1 Debye function. + /// + private const int DebyeMaximumTerms = 1000; + + /// + /// The size at which an exponential term is small enough to end the large-argument Debye summation. + /// + private const double DebyeTermTolerance = 1E-20; + + /// + /// Computes the order-1 Debye function D₁(x) for any real argument. + /// + /// The point to evaluate. Every real value is admissible. + /// The order-1 Debye function evaluated at the given x. + /// + /// + /// The order-1 Debye function is D₁(x) = (1/x) ∫[0 to x] t / (e^t - 1) dt. The integrand tends to 1 as + /// t tends to 0, so the removable limit D₁(0) = 1 is returned exactly. This is a different function from + /// , which is the order-3 Debye function; the two are not interchangeable. + /// + /// + /// Three branches are used. A negative argument is reduced by the reflection D₁(-y) = D₁(y) + y/2 for + /// y > 0, so the function is finite and smooth on the whole real line. For 0 < x ≤ 1 the Maclaurin + /// series D₁(x) = 1 - x/4 + Σ[k ≥ 1] B(2k) x^(2k) / ((2k + 1) (2k)!) is used, truncated after x²⁴, where + /// the next term is below 1E-22. For x > 1 the integral is written against its limit π²/6, giving + /// D₁(x) = π²/(6x) - Σ[k ≥ 1] e^(-k x) (1/k + 1/(k² x)), which converges geometrically and is summed + /// until the term falls below 1E-20. + /// + /// + /// Accuracy. Measured against mpmath at 60 decimal digits, evaluated by two independent routes + /// that agree to better than 1E-58, the worst relative error over x in [1E-8, 100] and its negative + /// mirror is 3 ulp, or 3.4E-16 absolute, at x near 1.75. At the endpoints D₁(100) = 0.016449340668482266 matches the + /// asymptote π²/600 = 0.016449340668482264, and the reflection D₁(-1) - D₁(1) = 0.5 holds exactly. + /// + /// + public static double FunctionOrderOne(double x) + { + if (x == 0d) return 1d; + + // D₁(-y) = D₁(y) + y/2 for y > 0. + if (x < 0d) return FunctionOrderOne(-x) - 0.5d * x; + + if (x <= 1d) + { + double squared = x * x; + double power = 1d; + double sum = 0d; + for (int k = 0; k < DebyeSeriesCoefficients.Length; k++) + { + power *= squared; + sum += DebyeSeriesCoefficients[k] * power; + } + return 1d - 0.25d * x + sum; + } + + double remainder = 0d; + for (int k = 1; k <= DebyeMaximumTerms; k++) + { + double term = Math.Exp(-k * x) * (1d / k + 1d / ((double)k * k * x)); + remainder += term; + if (term < DebyeTermTolerance) break; + } + return Math.PI * Math.PI / (6d * x) - remainder; + } + } } diff --git a/Test_Numerics/Distributions/Bivariate Copulas/Test_FrankCopula.cs b/Test_Numerics/Distributions/Bivariate Copulas/Test_FrankCopula.cs index e286a727..23aee789 100644 --- a/Test_Numerics/Distributions/Bivariate Copulas/Test_FrankCopula.cs +++ b/Test_Numerics/Distributions/Bivariate Copulas/Test_FrankCopula.cs @@ -393,11 +393,11 @@ public void Test_SetThetaFromTau_UnattainableTau() // bracket theta in [-100, 100] reaches. var concordant = PermutationWithInversions(RankFixtureLength, 0); var positive = Assert.ThrowsExactly(() => copula.SetThetaFromTau(ranks, concordant)); - StringAssert.Contains(positive.Message, "[-0.9607, 0.9607]"); + StringAssert.Contains(positive.Message, "~= [-0.96065, 0.96065]"); var discordant = PermutationWithInversions(RankFixtureLength, (int)RankFixturePairs); var negative = Assert.ThrowsExactly(() => copula.SetThetaFromTau(ranks, discordant)); - StringAssert.Contains(negative.Message, "[-0.9607, 0.9607]"); + StringAssert.Contains(negative.Message, "~= [-0.96065, 0.96065]"); } /// @@ -405,8 +405,9 @@ public void Test_SetThetaFromTau_UnattainableTau() /// /// /// The Frank independence limit is theta = 0, at which the generator and the distribution functions are - /// indeterminate, so the fit returns the bracket endpoint nearest independence instead. The resulting - /// copula must be indistinguishable from independence over the unit square. + /// indeterminate, so the fit returns the bracket endpoint nearest independence instead. A tau of exactly + /// zero takes the negative bracket, matching ParameterConstraints, so the endpoint is -0.001. The + /// resulting copula must be indistinguishable from independence over the unit square. /// [TestMethod] public void Test_SetThetaFromTau_Independence() @@ -417,7 +418,7 @@ public void Test_SetThetaFromTau_Independence() var copula = new FrankCopula(); copula.SetThetaFromTau(ranks, permuted); - Assert.AreEqual(0.001d, copula.Theta, 0d); + Assert.AreEqual(-0.001d, copula.Theta, 0d); Assert.AreEqual(0d, FrankCopula.KendallsTauFromTheta(copula.Theta), 1E-3); foreach (double u in new[] { 0.1, 0.3, 0.5, 0.7, 0.9 }) { diff --git a/Test_Numerics/Distributions/Bivariate Copulas/Test_JoeCopula.cs b/Test_Numerics/Distributions/Bivariate Copulas/Test_JoeCopula.cs index 9a1fa9c6..75428ef7 100644 --- a/Test_Numerics/Distributions/Bivariate Copulas/Test_JoeCopula.cs +++ b/Test_Numerics/Distributions/Bivariate Copulas/Test_JoeCopula.cs @@ -436,12 +436,12 @@ public void Test_SetThetaFromTau_UnattainableTau() // Perfect concordance gives tau = 1, above the largest tau the bracket theta in [1, 100] reaches. var concordant = PermutationWithInversions(RankFixtureLength, 0); var tooStrong = Assert.ThrowsExactly(() => copula.SetThetaFromTau(ranks, concordant)); - StringAssert.Contains(tooStrong.Message, "[0, 0.9803]"); + StringAssert.Contains(tooStrong.Message, "~= [0, 0.98025]"); // Perfect discordance gives tau = -1, and the Joe copula models positive dependence only. var discordant = PermutationWithInversions(RankFixtureLength, (int)RankFixturePairs); var negative = Assert.ThrowsExactly(() => copula.SetThetaFromTau(ranks, discordant)); - StringAssert.Contains(negative.Message, "[0, 0.9803]"); + StringAssert.Contains(negative.Message, "~= [0, 0.98025]"); } /// diff --git a/Test_Numerics/Mathematics/Special Functions/Test_SpecialFunctions.cs b/Test_Numerics/Mathematics/Special Functions/Test_SpecialFunctions.cs index 1f03f59e..e79b1b02 100644 --- a/Test_Numerics/Mathematics/Special Functions/Test_SpecialFunctions.cs +++ b/Test_Numerics/Mathematics/Special Functions/Test_SpecialFunctions.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Mathematics.SpecialFunctions; @@ -110,6 +110,53 @@ public void Test_Debye() } } + /// + /// Test the order-1 Debye function against a high-precision reference. + /// + /// + /// The reference values were computed with mpmath at 60 decimal digits by two independent routes that + /// agree to better than 1E-58: tanh-sinh quadrature of the defining integral D1(x) = (1/x) integral of + /// t / (e^t - 1) from 0 to x, and the polylogarithm closed form + /// D1(x) = pi^2 / (6x) + ln(1 - e^-x) - Li2(e^-x) / x. They are corroborated at the endpoints by the + /// asymptote D1(100) = pi^2 / 600 = 0.016449340668482264 and by the reflection D1(-1) = D1(1) + 0.5. + /// The relative tolerance of 1E-14 is well above the measured worst error of 3 ulp. + /// + [TestMethod] + public void Test_DebyeOrderOne() + { + var testX = new double[] { -100d, -30d, -10d, -5d, -1d, -0.5, -0.001, 0.001, 0.01, 0.1, 0.5, 1d, 1.5, 2d, 5d, 10d, 30d, 100d }; + var testValid = new double[] + { + 50.01644934066848, 15.05483113556151, 5.164443465679946, 2.820876197700146, + 1.2775046341122482, 1.1319271567906055, 1.0002500277777775, 0.9997500277777776, + 0.997502777775, 0.9752777500047232, 0.8819271567906055, 0.7775046341122482, + 0.686145310789402, 0.6069472846098101, 0.32087619770014614, 0.16444346567994603, + 0.054831135561510855, 0.016449340668482266 + }; + + for (int i = 0; i < testValid.Length; i++) + { + double result = Debye.FunctionOrderOne(testX[i]); + Assert.AreEqual(testValid[i], result, Math.Abs(testValid[i]) * 1E-14, $"The order-1 Debye function is out of tolerance at x = {testX[i]}."); + } + + // The integrand tends to 1 as t tends to 0, so the removable limit is returned exactly. + Assert.AreEqual(1d, Debye.FunctionOrderOne(0d), 0d); + + // D1(-x) - D1(x) = x / 2 for x > 0. The tolerance is scaled by the size of the values being + // subtracted rather than by the size of the difference: for a small x the two values are both near + // 1 and the subtraction itself costs about half an ulp of 1, which is far larger than half an ulp + // of x / 2. This is cancellation in the assertion, not error in the function. + foreach (double x in new[] { 0.001, 0.25, 1d, 1.75, 7.5, 40d }) + { + double reflected = Debye.FunctionOrderOne(-x); + Assert.AreEqual(0.5 * x, reflected - Debye.FunctionOrderOne(x), Math.Abs(reflected) * 1E-15, $"The order-1 Debye reflection failed at x = {x}."); + } + + // The order-3 function is a different function and must not be confused with this one. + Assert.AreNotEqual(Debye.Function(1d), Debye.FunctionOrderOne(1d)); + } + /// /// Test the factorial function /// From 00e74052d7aff32d1caac9dfd859e6734595710a Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 24 Aug 2026 17:27:17 -0600 Subject: [PATCH 067/222] Characterize the matrix regularization ridge escalation --- .../Test_MatrixRegularization.cs | 161 ++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 Test_Numerics/Mathematics/Linear Algebra/Test_MatrixRegularization.cs diff --git a/Test_Numerics/Mathematics/Linear Algebra/Test_MatrixRegularization.cs b/Test_Numerics/Mathematics/Linear Algebra/Test_MatrixRegularization.cs new file mode 100644 index 00000000..f6181e9f --- /dev/null +++ b/Test_Numerics/Mathematics/Linear Algebra/Test_MatrixRegularization.cs @@ -0,0 +1,161 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Mathematics.LinearAlgebra; + +namespace Mathematics.LinearAlgebra +{ + /// + /// A class characterizing , whose ridge-escalation loop is driven by + /// whether accepts a candidate matrix. + /// + /// + /// + /// Authors: + /// + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + /// + /// symmetrizes its input, then adds a + /// trace-scaled ridge of 1E-10 * trace / p and retries with the ridge multiplied by ten each time + /// the factorization is rejected, up to eight attempts. Tightening the Cholesky pivot test could in + /// principle make the loop reject a matrix it used to accept, escalate to a larger ridge, and return a + /// different matrix to every downstream consumer. These tests pin the returned matrix so that any such + /// escalation shows up as a failure rather than as a silent change in a fitted result. + /// + /// + /// The loop is structurally immune to the scale-relative pivot test at any realistic dimension. For a + /// positive semi-definite input the ridged matrix has a smallest eigenvalue of at least the ridge, so + /// every pivot is at least 1E-10 * trace / p while no diagonal entry exceeds the trace. The pivot + /// ratio is therefore at least 1E-10 / p, against a tolerance of p * 2^-52. Those two cross + /// only near p = 671; below that the first attempt always succeeds, exactly as it does today. + /// + /// + [TestClass] + public class Test_MatrixRegularization + { + /// + /// Asserts two matrices agree entry for entry. + /// + /// The expected matrix. + /// The matrix produced by the method under test. + /// The permitted absolute difference per entry. + private static void AssertMatricesEqual(Matrix expected, Matrix actual, double delta) + { + Assert.AreEqual(expected.NumberOfRows, actual.NumberOfRows); + Assert.AreEqual(expected.NumberOfColumns, actual.NumberOfColumns); + for (int i = 0; i < expected.NumberOfRows; i++) + { + for (int j = 0; j < expected.NumberOfColumns; j++) + Assert.AreEqual(expected[i, j], actual[i, j], delta, "entry [" + i + "," + j + "]"); + } + } + + /// + /// Builds the matrix the first ridge attempt produces: the symmetrized input plus + /// 1E-10 * trace / p on the diagonal. + /// + /// The input matrix. + /// The candidate the loop tests first. + private static Matrix FirstRidgeCandidate(Matrix M) + { + int p = M.NumberOfRows; + var S = new Matrix(p); + for (int i = 0; i < p; i++) + { + for (int j = 0; j < p; j++) + S[i, j] = 0.5d * (M[i, j] + M[j, i]); + } + double trace = 0d; + for (int i = 0; i < p; i++) trace += S[i, i]; + double ridge = trace > 0d ? 1E-10d * trace / p : 1E-10d; + for (int i = 0; i < p; i++) S[i, i] += ridge; + return S; + } + + /// + /// Verifies that a well-conditioned symmetric matrix is returned with the base ridge only. + /// + [TestMethod] + public void Test_MakeSymmetricPositiveDefinite_WellConditionedTakesTheBaseRidge() + { + var M = new Matrix(new[,] { { 4d, 1d, 0.5d }, { 1d, 3d, 0.25d }, { 0.5d, 0.25d, 2d } }); + AssertMatricesEqual(FirstRidgeCandidate(M), MatrixRegularization.MakeSymmetricPositiveDefinite(M), 0d); + } + + /// + /// Verifies that an exactly rank-deficient input is still resolved by the base ridge. + /// + /// + /// This is the case the scale-relative pivot test was introduced for: the third row of the input + /// equals the first, so the raw matrix is exactly rank two. The base ridge of + /// 1E-10 * 5 / 3 = 1.667E-10 lifts the smallest eigenvalue clear of the tolerance — + /// the final pivot ratio is about 8.3E-11 against a tolerance of 6.66E-16 — so the first attempt + /// succeeds under both the old absolute test and the new relative one, and the returned matrix is + /// unchanged. + /// + [TestMethod] + public void Test_MakeSymmetricPositiveDefinite_RankDeficientTakesTheBaseRidge() + { + var M = new Matrix(new[,] { { 2d, 0.5d, 2d }, { 0.5d, 1d, 0.5d }, { 2d, 0.5d, 2d } }); + var regularized = MatrixRegularization.MakeSymmetricPositiveDefinite(M); + AssertMatricesEqual(FirstRidgeCandidate(M), regularized, 0d); + + // The candidate the loop accepted really is accepted by the tightened test. + var chol = new CholeskyDecomposition(regularized); + Assert.IsTrue(chol.IsPositiveDefinite); + Assert.IsGreaterThan(6.661338147750960E-16d, chol.L[2, 2] * chol.L[2, 2] / regularized[2, 2]); + } + + /// + /// Verifies that an asymmetric input is symmetrized before the ridge is applied. + /// + [TestMethod] + public void Test_MakeSymmetricPositiveDefinite_SymmetrizesFirst() + { + var M = new Matrix(new[,] { { 2d, 0.8d }, { 0.2d, 2d } }); + var regularized = MatrixRegularization.MakeSymmetricPositiveDefinite(M); + AssertMatricesEqual(FirstRidgeCandidate(M), regularized, 0d); + Assert.AreEqual(0.5d, regularized[0, 1], 0d); + Assert.AreEqual(0.5d, regularized[1, 0], 0d); + } + + /// + /// Verifies the indefinite path, where the ridge must escalate, is reached identically. + /// + /// + /// The input has an eigenvalue of -1, which no ridge in the loop's range can lift, so the method + /// falls through to the last-resort ridge of 1E-4 * trace / p. Every rejection along the way + /// comes from a strictly negative pivot, which both the absolute and the relative test reject + /// identically, so this path cannot move. + /// + [TestMethod] + public void Test_MakeSymmetricPositiveDefinite_IndefiniteFallsThroughToTheLastResortRidge() + { + var M = new Matrix(new[,] { { 1d, 2d }, { 2d, 1d } }); + var regularized = MatrixRegularization.MakeSymmetricPositiveDefinite(M); + double expectedRidge = 1E-4d * 2d / 2d; + Assert.AreEqual(1d + expectedRidge, regularized[0, 0], 0d); + Assert.AreEqual(1d + expectedRidge, regularized[1, 1], 0d); + Assert.AreEqual(2d, regularized[0, 1], 0d); + } + + /// + /// Verifies that is untouched, + /// since it floors eigenvalues directly and never consults the Cholesky factorization. + /// + [TestMethod] + public void Test_Regularize_FloorsAndCapsEigenvalues() + { + var M = new Matrix(new[,] { { 1d, 1d }, { 1d, 1d } }); + var regularized = MatrixRegularization.Regularize(M); + + // Eigenvalues are 2 and 0; the floor is eps * trace / p = 1E-6 * 2 / 2 = 1E-6, and the cap is + // 50 * median(0, 2) = 50, which binds on neither. + Assert.AreEqual(1.0000005d, regularized[0, 0], 1E-12d); + Assert.AreEqual(1.0000005d, regularized[1, 1], 1E-12d); + Assert.AreEqual(0.9999995d, regularized[0, 1], 1E-12d); + Assert.AreEqual(0.9999995d, regularized[1, 0], 1E-12d); + } + } +} From abf05371ccdd5deaaef1b10b6b11295a1b205cc6 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 24 Aug 2026 17:32:31 -0600 Subject: [PATCH 068/222] Reject Cholesky pivots that are negligible relative to their own diagonal --- .../Linear Algebra/CholeskyDecomposition.cs | 117 ++++++++++- .../Multivariate/Test_MultivariateNormal.cs | 60 ++++-- .../Test_CholeskyDecomposition.cs | 186 +++++++++++++++++- .../Test_MatrixRegularization.cs | 2 +- 4 files changed, 339 insertions(+), 26 deletions(-) diff --git a/Numerics/Mathematics/Linear Algebra/CholeskyDecomposition.cs b/Numerics/Mathematics/Linear Algebra/CholeskyDecomposition.cs index 33a3a1d0..600861d4 100644 --- a/Numerics/Mathematics/Linear Algebra/CholeskyDecomposition.cs +++ b/Numerics/Mathematics/Linear Algebra/CholeskyDecomposition.cs @@ -1,4 +1,6 @@ using System; +using System.Globalization; + namespace Numerics.Mathematics.LinearAlgebra { @@ -43,14 +45,67 @@ public class CholeskyDecomposition { /// - /// Constructs new Cholesky Decomposition. + /// Constructs new Cholesky Decomposition using the default scale-relative pivot tolerance. /// /// The positive-definite symmetric input matrix A [0..n-1][0..n-1] that is to be Cholesky decomposed. + /// Thrown when the matrix A is not square. + /// Thrown when the matrix A is not positive-definite. + /// + /// The pivot tolerance is evaluated at the dimension of A. + /// public CholeskyDecomposition(Matrix A) + : this(A, DefaultRelativeTolerance(A.NumberOfRows)) + { + } + + /// + /// Constructs new Cholesky Decomposition with an explicit scale-relative pivot tolerance. + /// + /// The positive-definite symmetric input matrix A [0..n-1][0..n-1] that is to be Cholesky decomposed. + /// + /// The pivot tolerance, expressed as a fraction of the corresponding diagonal entry of A. A pivot is + /// rejected when it falls at or below relativeTolerance * A[i,i]. Pass zero to reproduce the + /// purely absolute pivot <= 0 test exactly. + /// + /// + /// Thrown when the matrix A is not square, or when is not a finite + /// value in the interval [0, 1). + /// + /// Thrown when the matrix A is not positive-definite. + /// + /// + /// The pivot at step i is the conditional variance of variable i given variables 0..i-1, so it is + /// naturally measured against A[i,i] — the unconditional variance of that same variable — rather + /// than against the largest diagonal of A. A test relative to the largest diagonal would falsely reject + /// a covariance that legitimately mixes a very small variance with a very large one, because the small + /// variable's pivot is small in absolute terms while being a perfectly healthy fraction of its own + /// diagonal. + /// + /// + /// The scale-relative test exists because an exactly rank-deficient matrix does not generally produce a + /// non-positive pivot in floating point. Rounding leaves a small positive residue instead, the + /// factorization completes, and the matrix is silently reported positive-definite with a wildly wrong + /// determinant and inverse. For example, the exactly rank-two covariance + /// [[2, 0.5, 2], [0.5, 1, 0.5], [2, 0.5, 2]] yields a final pivot of 4.44E-16 rather than zero; + /// under the absolute test it factorizes, and its log determinant comes out near -34.8 instead of the + /// log pseudo-determinant 1.2528. See . + /// + /// + /// When A[i,i] is not a positive finite number the threshold falls back to zero, which is the + /// absolute test. That case cannot weaken the result: a positive-definite matrix has a strictly positive + /// finite diagonal, so a non-positive or non-finite diagonal is rejected on its own merits. + /// + /// + public CholeskyDecomposition(Matrix A, double relativeTolerance) { IsPositiveDefinite = false; int i, j, k; + if (double.IsNaN(relativeTolerance) || double.IsInfinity(relativeTolerance) || relativeTolerance < 0d || relativeTolerance >= 1d) + { + throw new ArgumentOutOfRangeException(nameof(relativeTolerance), "The relative tolerance must be a finite value in the interval [0, 1)."); + } + RelativeTolerance = relativeTolerance; n = A.NumberOfRows; this.A = new Matrix(A.ToArray()); L = new Matrix(A.ToArray()); // Lower triangular matrix @@ -59,7 +114,7 @@ public CholeskyDecomposition(Matrix A) { throw new ArgumentOutOfRangeException(nameof(A), "The matrix A must be square."); } - + //Decomposing a matrix into Lower triangular for (i = 0; i < n; i++) { @@ -68,11 +123,23 @@ public CholeskyDecomposition(Matrix A) sum = L[i, j]; for (k = i - 1; k >= 0; k -= 1) - sum -= L[i, k] * L[j, k]; // Cholesky formula + sum -= L[i, k] * L[j, k]; // Cholesky formula if (i == j) { + // Reject a pivot that is negligible relative to its own diagonal entry. The diagonal + // guard keeps the threshold at zero — today's absolute test — whenever A[i,i] is not a + // positive finite number. + double diagonal = this.A[i, i]; + double threshold = diagonal > 0d && !double.IsInfinity(diagonal) ? relativeTolerance * diagonal : 0d; if (double.IsNaN(sum) || sum <= 0d) throw new Exception("Cholesky Decomposition failed. The input matrix is not positive-definite."); + if (sum <= threshold) + throw new Exception("Cholesky Decomposition failed. The input matrix is not positive-definite. The pivot at row " + + i.ToString(CultureInfo.InvariantCulture) + " is " + + (sum / diagonal).ToString("E6", CultureInfo.InvariantCulture) + + " times its diagonal entry, at or below the relative tolerance " + + relativeTolerance.ToString("E6", CultureInfo.InvariantCulture) + + ", so the matrix is numerically rank-deficient."); L[i, i] = Math.Sqrt(sum); } else @@ -81,20 +148,58 @@ public CholeskyDecomposition(Matrix A) } } } - + // Making sure 0 entries for upper triangular matrix for (i = 0; i < n; i++) { for (j = 0; j < i; j++) L[j, i] = 0.0d; } - // Failure of the decomposition indicates that the matrix A is not positive-definite. - // Success, means it is. + // Failure of the decomposition indicates that the matrix A is not positive-definite. + // Success, means it is. IsPositiveDefinite = true; } + /// + /// Returns the default scale-relative pivot tolerance for a matrix of the given dimension. + /// + /// The number of rows in the matrix to be decomposed. + /// The tolerance dimension * 2^-52, or zero when the dimension is not positive. + /// + /// + /// The value is n * 2^-52 ≈ n * 2.22E-16, expressed here as 2 * n * + /// because that constant is the unit roundoff 2^-53 + /// rather than the double-precision spacing 2^-52. + /// + /// + /// This tracks the standard backward-error bound for Cholesky factorization, in which the computed pivot + /// differs from the exact one by a quantity of order n unit roundoffs times the corresponding + /// diagonal entry. A pivot at or below that level carries no information beyond rounding noise. + /// + /// + /// The margin against legitimate matrices is large. For two variables with correlation ρ the pivot ratio + /// is 1 - ρ², so a false rejection at n = 2 requires 1 - ρ² <= 4.44E-16, that is ρ + /// within about two ulp of one. Measured across the Numerics test suite (29.8 million factorizations) the + /// smallest ratio produced by a genuinely positive-definite matrix is 2.0E-12, some 4,500 times the + /// tolerance that applies to it. + /// + /// + public static double DefaultRelativeTolerance(int dimension) + { + return dimension > 0 ? 2d * dimension * Tools.DoubleMachineEpsilon : 0d; + } + private readonly int n; // Number of rows in A + /// + /// The scale-relative pivot tolerance applied during the factorization. + /// + /// + /// A pivot was rejected when it fell at or below this fraction of the corresponding diagonal entry of + /// . Zero means the purely absolute test was used. + /// + public double RelativeTolerance { get; private set; } + /// /// Stores the decomposition. /// diff --git a/Test_Numerics/Distributions/Multivariate/Test_MultivariateNormal.cs b/Test_Numerics/Distributions/Multivariate/Test_MultivariateNormal.cs index ea799fef..c8b4a798 100644 --- a/Test_Numerics/Distributions/Multivariate/Test_MultivariateNormal.cs +++ b/Test_Numerics/Distributions/Multivariate/Test_MultivariateNormal.cs @@ -1044,33 +1044,57 @@ public void Test_SVD_ValidationAcceptsSemiDefiniteAndRejectsIndefinite() } /// - /// Documents what the Cholesky path does with the two singular reference covariances, which is the - /// behaviour issue #145 was filed about. + /// Verifies that the Cholesky path now rejects both singular reference covariances, and that the + /// singular value path returns the correct density for the one that used to slip through. /// /// - /// Case 3, the rank-one covariance, fails cleanly: the Cholesky factorization reaches a - /// non-positive pivot and throws. Case 2, the rank-two covariance, does not throw — its final - /// pivot evaluates to about 4.4E-16 rather than exactly zero, so the factorization completes with a - /// pivot of order 1E-8 and the resulting log density is wrong by about 17 nats. This test - /// pins both behaviours as they stand today and shows the singular value path getting the right - /// answer where the Cholesky path does not. Neither Cholesky behaviour is changed here. + /// + /// Case 3, the rank-one covariance, always failed cleanly: the Cholesky factorization reaches a + /// pivot of exactly zero and throws. + /// + /// + /// Case 2, the rank-two covariance, did not throw before this was fixed. Its final pivot + /// evaluates to 4.440892E-16 rather than exactly zero, so under the purely absolute + /// pivot <= 0 test the factorization completed with a factor entry of order 1E-8 and + /// reported the matrix positive-definite. The resulting log density at the origin was + /// +14.638629610696878 against the correct -2.4642585506570285 — wrong by 17.1 nats, a factor of + /// about 2.7E+7 on the density — with no exception, no warning and no flag. That silent wrong answer + /// is the failure mode issue #145 describes, and it is what the scale-relative pivot test in + /// now catches: the pivot ratio is 2.220446E-16 against a + /// tolerance of 6.661338E-16 at this dimension. + /// + /// + /// Rejecting the covariance is the right answer for the Cholesky path, which factorizes only + /// strictly positive-definite matrices; a caller who wants a density on a degenerate covariance + /// selects , which is asserted here to return the + /// oracle value. + /// /// [TestMethod] - public void Test_Cholesky_BehaviourOnSingularCovariancesIsUnchanged() + public void Test_Cholesky_RejectsSingularCovariances() { - // Case 3: a clean failure. - var exception = AssertThrowsAny(() => new MultivariateNormal(new[] { 0d, 0d }, Case3Covariance)); - Assert.IsGreaterThanOrEqualTo(0, exception.Message.IndexOf("positive-definite", StringComparison.OrdinalIgnoreCase)); + // Case 3: a clean failure, unchanged — the pivot is exactly zero. + var case3Exception = AssertThrowsAny(() => new MultivariateNormal(new[] { 0d, 0d }, Case3Covariance)); + Assert.IsGreaterThanOrEqualTo(0, case3Exception.Message.IndexOf("positive-definite", StringComparison.OrdinalIgnoreCase)); - // Case 2: no failure, but an unusable density — the silent failure mode the issue describes. - var cholesky = new MultivariateNormal(new[] { 0d, 0d, 0d }, Case2Covariance); - double choleskyLogPdf = cholesky.LogPDF(new[] { 0d, 0d, 0d }); - double oracle = -2.4642585506570285d; - Assert.IsGreaterThan(1d, Math.Abs(choleskyLogPdf - oracle), - "The Cholesky path is expected to be far from the correct density on a rank-deficient covariance."); + // Case 2: now rejected rather than silently factorized. + var case2Exception = AssertThrowsAny(() => new MultivariateNormal(new[] { 0d, 0d, 0d }, Case2Covariance)); + Assert.IsGreaterThanOrEqualTo(0, case2Exception.Message.IndexOf("positive-definite", StringComparison.OrdinalIgnoreCase)); + + // The non-throwing mutable path reports the same rejection without raising. + var mutable = new MultivariateNormal(new[] { 0d, 0d, 0d }, new[,] { { 1d, 0d, 0d }, { 0d, 1d, 0d }, { 0d, 0d, 1d } }); + Assert.IsFalse(mutable.TrySetCovariance(Case2Covariance)); + Assert.IsFalse(mutable.IsDensityValid); + // The singular value path is the supported way to get a density here, and it is correct. + double oracle = -2.4642585506570285d; var singular = new MultivariateNormal(new[] { 0d, 0d, 0d }, Case2Covariance, DecompositionMethod.SingularValue); Assert.AreEqual(oracle, singular.LogPDF(new[] { 0d, 0d, 0d }), OracleTolerance); + + // The old behaviour is still reachable through the explicit zero tolerance, and it is still wrong. + var legacy = new CholeskyDecomposition(new Matrix(Case2Covariance), 0d); + Assert.IsTrue(legacy.IsPositiveDefinite); + Assert.AreEqual(-34.790890420621793d, legacy.LogDeterminant(), 1E-5d); } /// diff --git a/Test_Numerics/Mathematics/Linear Algebra/Test_CholeskyDecomposition.cs b/Test_Numerics/Mathematics/Linear Algebra/Test_CholeskyDecomposition.cs index 25386978..2bbee05d 100644 --- a/Test_Numerics/Mathematics/Linear Algebra/Test_CholeskyDecomposition.cs +++ b/Test_Numerics/Mathematics/Linear Algebra/Test_CholeskyDecomposition.cs @@ -1,5 +1,6 @@ -using System; +using System; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics; using Numerics.Mathematics.LinearAlgebra; namespace Mathematics.LinearAlgebra @@ -239,6 +240,189 @@ public void Test_Back() Assert.AreEqual(x[i], right_x[i], 0.0001d); } + /// + /// The exactly rank-two covariance from USACE-RMC/Numerics#145, whose third row equals its first. + /// + private static readonly double[,] RankTwoCovariance = + { + { 2d, 0.5d, 2d }, + { 0.5d, 1d, 0.5d }, + { 2d, 0.5d, 2d } + }; + + /// + /// Asserts the action throws, and returns the exception, without requiring a specific type + /// (net481-compatible). + /// + /// The action expected to throw. + /// The exception that was thrown. + private static Exception AssertThrowsAny(Action action) + { + try + { + action(); + } + catch (Exception ex) + { + return ex; + } + Assert.Fail("The action was expected to throw."); + throw new InvalidOperationException("unreachable"); + } + + /// + /// Verifies that an exactly rank-deficient matrix is rejected rather than silently factorized. + /// + /// + /// Row three of is identical to row one, so the matrix is exactly + /// rank two and its third pivot is exactly zero in exact arithmetic. In floating point the pivot + /// evaluates to 4.440892E-16 — a positive number — so the purely absolute pivot <= 0 test + /// let the factorization complete and reported the matrix positive-definite. The pivot ratio is + /// 2.220446E-16, which the default tolerance of 3 * 2^-52 = 6.661338E-16 rejects with a + /// factor of three to spare. + /// + [TestMethod] + public void Test_RejectsExactlyRankDeficientMatrix() + { + var exception = AssertThrowsAny(() => new CholeskyDecomposition(new Matrix(RankTwoCovariance))); + Assert.IsGreaterThanOrEqualTo(0, exception.Message.IndexOf("positive-definite", StringComparison.OrdinalIgnoreCase)); + } + + /// + /// Pins the pre-existing behaviour of the absolute pivot test, reachable by passing a zero tolerance. + /// + /// + /// With relativeTolerance = 0 the test reduces to pivot <= 0 exactly, so the rank-two + /// covariance still factorizes and still yields the wrong answers it always did: a final factor entry + /// of 2.107342425544702E-08 and a log determinant of -34.790890420621793, against a true log + /// pseudo-determinant of 1.2527629684953678. This test exists so that the zero-tolerance escape hatch + /// is demonstrably identical to the old behaviour, not so that the old behaviour is endorsed. + /// + [TestMethod] + public void Test_ZeroToleranceReproducesTheAbsolutePivotTest() + { + var chol = new CholeskyDecomposition(new Matrix(RankTwoCovariance), 0d); + Assert.IsTrue(chol.IsPositiveDefinite); + Assert.AreEqual(0d, chol.RelativeTolerance, 0d); + Assert.AreEqual(2.107342425544702E-08d, chol.L[2, 2], 1E-14d); + Assert.AreEqual(-34.790890420621793d, chol.LogDeterminant(), 1E-5d); + } + + /// + /// Verifies that a genuinely positive-definite but severely ill-conditioned correlation matrix is + /// still accepted. + /// + /// + /// For two variables with correlation ρ the second pivot ratio is exactly 1 - ρ². At + /// ρ = 0.9999 the ratio is 1.999900E-04, some 4.5E+11 times the tolerance of 4.440892E-16 that + /// applies at n = 2. At ρ = 1 − 1E-12 — the tightest legitimately positive-definite matrix anywhere + /// in this test suite — the ratio is 1.999956E-12, still 4,503 times the tolerance. A false + /// rejection would require ρ within roughly two ulp of one, at which point the matrix is + /// numerically singular in any case. + /// + [TestMethod] + public void Test_IllConditionedCorrelationIsStillAccepted() + { + foreach (double rho in new[] { 0.9999d, 1d - 1E-12d }) + { + var A = new Matrix(new[,] { { 1d, rho }, { rho, 1d } }); + var chol = new CholeskyDecomposition(A); + Assert.IsTrue(chol.IsPositiveDefinite, "rho = " + rho.ToString("R")); + + // The second pivot is L[1,1]^2 and must match 1 - rho^2 to rounding. + Assert.AreEqual(1d - rho * rho, chol.L[1, 1] * chol.L[1, 1], 1E-16d); + Assert.IsGreaterThan(chol.RelativeTolerance, chol.L[1, 1] * chol.L[1, 1]); + } + } + + /// + /// Verifies that a covariance mixing a very large variance with a very small one is still accepted, + /// which is the reason the tolerance is relative to each diagonal entry rather than to the largest. + /// + /// + /// For [[1E12, 1], [1, 1E-11]] the determinant is 9, so the matrix is comfortably positive + /// definite, and the second pivot ratio is 0.9 — entirely healthy. Measured against the largest + /// diagonal instead, the same pivot would read 9E-24 and any sane tolerance would reject it. The + /// pivot is the conditional variance of the second variable, so its own diagonal is the only + /// meaningful scale. + /// + [TestMethod] + public void Test_TinyVarianceBesideLargeVarianceIsStillAccepted() + { + var A = new Matrix(new[,] { { 1E12d, 1d }, { 1d, 1E-11d } }); + var chol = new CholeskyDecomposition(A); + Assert.IsTrue(chol.IsPositiveDefinite); + + double pivotRatio = chol.L[1, 1] * chol.L[1, 1] / A[1, 1]; + Assert.AreEqual(0.9d, pivotRatio, 1E-12d); + Assert.IsLessThan(1E-23d, chol.L[1, 1] * chol.L[1, 1] / A[0, 0], + "The same pivot measured against the largest diagonal is negligible, which is why that scale is not used."); + } + + /// + /// Verifies the default tolerance value and that it is reported on the instance. + /// + /// + /// The default is n * 2^-52, expressed as 2 * n * Tools.DoubleMachineEpsilon because + /// that constant is the unit roundoff 2^-53. + /// + [TestMethod] + public void Test_DefaultRelativeTolerance() + { + Assert.AreEqual(0d, CholeskyDecomposition.DefaultRelativeTolerance(0), 0d); + Assert.AreEqual(0d, CholeskyDecomposition.DefaultRelativeTolerance(-4), 0d); + Assert.AreEqual(2d * 3d * Tools.DoubleMachineEpsilon, CholeskyDecomposition.DefaultRelativeTolerance(3), 0d); + Assert.AreEqual(6.661338147750960E-16d, CholeskyDecomposition.DefaultRelativeTolerance(3), 1E-30d); + + var A = new Matrix(new[,] { { 4d, 1d }, { 1d, 3d } }); + Assert.AreEqual(CholeskyDecomposition.DefaultRelativeTolerance(2), new CholeskyDecomposition(A).RelativeTolerance, 0d); + Assert.AreEqual(1E-8d, new CholeskyDecomposition(A, 1E-8d).RelativeTolerance, 0d); + } + + /// + /// Verifies that an out-of-range or non-finite tolerance is rejected. + /// + [TestMethod] + public void Test_InvalidRelativeToleranceIsRejected() + { + var A = new Matrix(new[,] { { 4d, 1d }, { 1d, 3d } }); + foreach (double tolerance in new[] { -1E-16d, 1d, 2d, double.NaN, double.PositiveInfinity, double.NegativeInfinity }) + { + var exception = AssertThrowsAny(() => new CholeskyDecomposition(A, tolerance)); + Assert.IsInstanceOfType(exception, typeof(ArgumentOutOfRangeException), "tolerance = " + tolerance.ToString("R")); + } + } + + /// + /// Verifies that a matrix rejected by the absolute test is still rejected, with the same message, and + /// that a well-conditioned matrix factorizes identically under either tolerance. + /// + /// + /// Only pivots that are strictly positive yet at or below the relative threshold change outcome. A + /// negative or NaN pivot takes the original branch and therefore carries the original message + /// verbatim, which the callers that catch and re-wrap this exception rely on. + /// + [TestMethod] + public void Test_NonPositiveDefiniteRejectionIsUnchanged() + { + var indefinite = new Matrix(new[,] { { 1d, 2d }, { 2d, 1d } }); + Assert.AreEqual( + "Cholesky Decomposition failed. The input matrix is not positive-definite.", + AssertThrowsAny(() => new CholeskyDecomposition(indefinite)).Message); + Assert.AreEqual( + "Cholesky Decomposition failed. The input matrix is not positive-definite.", + AssertThrowsAny(() => new CholeskyDecomposition(indefinite, 0d)).Message); + + var A = new Matrix(new[,] { { 16d, 4d, 8d }, { 4d, 5d, -4d }, { 8d, -4d, 22d } }); + var withDefault = new CholeskyDecomposition(A); + var withZero = new CholeskyDecomposition(A, 0d); + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + Assert.AreEqual(withZero.L[i, j], withDefault.L[i, j], 0d); + } + } + } } diff --git a/Test_Numerics/Mathematics/Linear Algebra/Test_MatrixRegularization.cs b/Test_Numerics/Mathematics/Linear Algebra/Test_MatrixRegularization.cs index f6181e9f..0a6a49b0 100644 --- a/Test_Numerics/Mathematics/Linear Algebra/Test_MatrixRegularization.cs +++ b/Test_Numerics/Mathematics/Linear Algebra/Test_MatrixRegularization.cs @@ -104,7 +104,7 @@ public void Test_MakeSymmetricPositiveDefinite_RankDeficientTakesTheBaseRidge() // The candidate the loop accepted really is accepted by the tightened test. var chol = new CholeskyDecomposition(regularized); Assert.IsTrue(chol.IsPositiveDefinite); - Assert.IsGreaterThan(6.661338147750960E-16d, chol.L[2, 2] * chol.L[2, 2] / regularized[2, 2]); + Assert.IsGreaterThan(chol.RelativeTolerance, chol.L[2, 2] * chol.L[2, 2] / regularized[2, 2]); } /// From c8e52183b541195edaa99286b6e4e65d7ace8cb1 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 24 Aug 2026 17:53:41 -0600 Subject: [PATCH 069/222] Adopt the scipy zero-eigenvalue threshold in the multivariate normal SVD path The singular value path carried two notions of a numerically zero eigenvalue about 6.7 orders of magnitude apart: an acceptance tolerance relative to the largest matrix entry, and a rank threshold relative to the largest singular value. Everything in that band was mishandled, because a tolerated eigenvalue was kept rather than zeroed. A covariance with a negative variance of -1E-10 on the diagonal was accepted, reported as positive-definite, and returned a log density of about +8.98 where the Cholesky path rejects the matrix. A single threshold of 1E6 * 2^-52 * max|lambda| now governs acceptance, the rank, the null space, the log pseudo-determinant and the pseudo-inverse. A covariance is rejected when its smallest eigenvalue falls below the negative threshold, and every eigenvalue within it is zeroed. Signed eigenvalues come from Rayleigh quotients, backed by a reconstruction test that also rejects an asymmetric covariance and the degenerate case where eigenvalues of equal magnitude and opposite sign make the singular subspace ambiguous. The constant is the relative spacing 2^-52, not the unit roundoff that Tools.DoubleMachineEpsilon carries, so the thresholds match scipy exactly. Rank detection on a rank-deficient covariance gains six orders of margin, and the validation now hands its decomposition to the factorization instead of each computing one, halving the cost of setting parameters on this path. Every previously asserted density is unchanged, and the Cholesky path stays bit-identical; its pin now also covers stratified sampling, the density, the Mahalanobis distance, the CDF and the interval probability. --- .../Multivariate/MultivariateNormal.cs | 252 ++++++++++++++---- .../Multivariate/Test_MultivariateNormal.cs | 226 +++++++++++++++- .../Test_SingularValueDecomp.cs | 2 +- 3 files changed, 417 insertions(+), 63 deletions(-) diff --git a/Numerics/Distributions/Multivariate/MultivariateNormal.cs b/Numerics/Distributions/Multivariate/MultivariateNormal.cs index 175bef11..30d0f739 100644 --- a/Numerics/Distributions/Multivariate/MultivariateNormal.cs +++ b/Numerics/Distributions/Multivariate/MultivariateNormal.cs @@ -128,22 +128,34 @@ public MultivariateNormal(double[] mean, double[,] covariance, DecompositionMeth private double[]? _standardDeviation; /// - /// The multiple of the machine epsilon used when comparing a covariance matrix against its own - /// symmetric reconstruction and when testing whether a point lies on the support of a degenerate - /// distribution. + /// The multiple of below which an eigenvalue of the covariance + /// matrix, or a null-space residual, counts as zero. /// /// - /// Both comparisons separate quantities that are zero up to accumulated roundoff from quantities - /// that are genuinely nonzero. The multiplier follows the convention used by - /// scipy.stats.multivariate_normal, which scales the double-precision epsilon by 1E6 before - /// deciding that an eigenvalue or a null-space residual is zero. It was chosen with several orders - /// of margin on both sides: the measured roundoff on the reference covariance matrices of - /// is of order 1E-15 relative to the - /// matrix scale, while a genuinely indefinite matrix or a genuinely off-support point misses by an - /// amount of order one. + /// This is the 1E6 factor that scipy.stats.multivariate_normal applies through + /// scipy.stats._multivariate._eigvalsh_to_eps, verified against scipy 1.17.1. It is generous + /// on purpose, and it is what makes rank detection reliable: the numerically zero eigenvalue of a + /// rank-deficient covariance is of order 1E-16 relative to the matrix scale, but so is the roundoff + /// in the decomposition itself, so a threshold placed at the roundoff level has almost no margin. + /// Placing it six orders higher leaves roughly 3E6 of margin below and, on any covariance whose + /// nonzero eigenvalues are not themselves within a factor of 1E-10 of the largest, a comparable + /// margin above. /// private const double ZeroToleranceFactor = 1E6; + /// + /// The relative spacing of double-precision numbers, 2⁻⁵² ≈ 2.220446049250313E-16. + /// + /// + /// This is deliberately not , which is the unit + /// roundoff 2⁻⁵³ — exactly half of this value. The zero thresholds of this class are calibrated to + /// match scipy.stats.multivariate_normal value for value, and NumPy's + /// np.finfo(float).eps, which scipy multiplies by , is the + /// relative spacing 2⁻⁵². Using the unit roundoff instead would halve every threshold below and + /// silently break that agreement. + /// + private const double RelativeMachineEpsilon = 2.220446049250313E-16; + // variables required for the multivariate CDF private Matrix _correlation = null!; private double[] _correl = null!; @@ -337,7 +349,12 @@ public double[] StandardDeviation /// internally and is unaffected. Nor does it govern and /// , which factorize the observed sub-covariance with its own Cholesky /// decomposition and return distributions that use the default - /// selector. + /// selector. Those two helpers therefore throw + /// when the sub-covariance they need is singular, even on a distribution built with + /// whose own density evaluates perfectly well — + /// which is exactly what a caller with collinear gridded cells runs into. Take the marginal or + /// conditional mean and covariance and construct the sub-distribution explicitly with the + /// constructor to work around it. /// /// /// Under the distribution follows the degenerate @@ -349,6 +366,30 @@ public double[] StandardDeviation /// infinity and returns zero there. /// /// + /// The zero threshold. A single threshold + /// ε = · · max|λ| decides + /// what counts as a zero eigenvalue, and it is applied consistently to acceptance, the rank, the + /// null space, the log pseudo-determinant and the pseudo-inverse. A covariance is rejected when its + /// smallest eigenvalue is below −ε, and every eigenvalue with |λ| ≤ ε — negative by roundoff or + /// exactly zero alike — is set to zero rather than kept. This is + /// scipy.stats._multivariate._eigvalsh_to_eps and _PSD, verified value for value + /// against scipy 1.17.1. One consequence is worth knowing: because ε is relative to the largest + /// eigenvalue, a covariance that mixes wildly different scales loses its smallest directions — for + /// Σ = diag(1E8, 1E-8) the threshold is 2.2E-2, so the second direction is treated as null. That is + /// scipy's behaviour as well, and it is the price of a scale-invariant threshold. + /// + /// + /// Two deliberate departures from scipy. First, a point is tested against the support with a + /// tolerance proportional to ‖x − μ‖, where scipy uses one proportional to the eigenvalue scale of + /// Σ. For a large-scale singular covariance scipy therefore admits points that are visibly off the + /// support — for Σ = 1E12 · [[1,1],[1,1]] scipy returns a finite density at (1, −1) — where this + /// class returns negative infinity. The answer here is the mathematically exact one and is kept. + /// Second, when Σ is identically zero the support is the single point μ; this class reports + /// = 1 and = 0 there, the counting-measure density that the + /// rank-0 case of the convention above implies, while scipy returns zero density even at μ. Every + /// point other than μ is off the support and scores zero in both. + /// + /// /// Added for . /// /// @@ -377,10 +418,12 @@ public double[] StandardDeviation /// public void SetParameters(double[] mean, double[,] covariance) { - // Validate parameters - ValidateParameters(mean, covariance, true); + // Validate parameters. Under the singular value path the validation already builds the + // decomposition it needs, so it is handed back and reused for the factorization rather than + // being recomputed: an O(n^3) factorization is the whole cost this selector trades away. + ValidateParameters(mean, covariance, true, out var singularValues); - _dimension = mean.Length; + _dimension = mean.Length; _mean = mean; _covariance = new Matrix(covariance); if (_decomposition == DecompositionMethod.Cholesky) @@ -393,7 +436,7 @@ public void SetParameters(double[] mean, double[,] covariance) } else { - FactorizeWithSingularValues(); + FactorizeWithSingularValues(singularValues!); } // Set up parameters for MVN CDF @@ -420,27 +463,35 @@ public void SetParameters(double[] mean, double[,] covariance) /// the degenerate density and the sampler need: the rank, the null space, the normalizing constant /// built on the log pseudo-determinant, and the sampling factor A = U·sqrt(W). /// + /// The decomposition of the covariance matrix, already validated as + /// symmetric positive semi-definite by . /// /// - /// The threshold below which a singular value counts as zero is captured once, immediately after the - /// decomposition, and is then passed explicitly to every query. - /// recomputes its on each call that takes a - /// threshold argument, so passing the captured value keeps the rank, the null space, the - /// pseudo-determinant and the pseudo-inverse solve in agreement about which singular values are zero. + /// The threshold below which a singular value counts as zero comes from + /// and is passed explicitly to every query — never left to the + /// default. This matters twice over. recomputes its + /// on each call that takes a threshold argument, + /// including when the argument is negative, so passing the value explicitly is the only way to keep + /// the rank, the null space, the pseudo-determinant and the pseudo-inverse solve in agreement about + /// which singular values are zero. And the value itself is scipy's, not the decomposition's own + /// roundoff-based default, which is six orders smaller and leaves too little margin to detect rank + /// reliably. /// /// - /// Because Σ is symmetric positive semi-definite — enforced by — the + /// Because Σ is symmetric positive semi-definite — enforced by — the /// left and right singular vectors coincide for every singular value above the threshold, so /// Σ = U·W·Uᵀ and A = U·sqrt(W) satisfies A·Aᵀ = Σ. This is the factor NumPy builds for /// multivariate_normal(..., method='svd'). Null directions get a zero column and therefore - /// carry no noise, which places every draw on the support of the distribution. + /// carry no noise, which places every draw on the support of the distribution. Note that a direction + /// whose eigenvalue was negative but within the threshold is zeroed here rather than kept, so it + /// contributes neither sampling noise nor a term to the log pseudo-determinant. /// /// - private void FactorizeWithSingularValues() + private void FactorizeWithSingularValues(SingularValueDecomposition singularValues) { _cholesky = null!; - _svd = new SingularValueDecomposition(_covariance); - _svdThreshold = _svd.Threshold; + _svd = singularValues; + _svdThreshold = SingularValueThreshold(_svd); _rank = _svd.Rank(_svdThreshold); _nullspace = _svd.Nullspace(_svdThreshold); double lndet = _svd.LogPseudoDeterminant(_svdThreshold); @@ -457,6 +508,27 @@ private void FactorizeWithSingularValues() _factor = factor; } + /// + /// The threshold below which an eigenvalue of the covariance matrix counts as zero. + /// + /// The decomposition of the covariance matrix. + /// + /// · · max|λ|, where max|λ| + /// is the largest singular value. Zero when the covariance matrix is identically zero. + /// + /// + /// This is scipy.stats._multivariate._eigvalsh_to_eps. The singular values of a symmetric + /// matrix are the absolute values of its eigenvalues and + /// returns them in descending order, so the first one is + /// max|λ|. Every consumer of the decomposition in this class — acceptance, rank, null space, log + /// pseudo-determinant and pseudo-inverse — is given this same value, so they cannot disagree about + /// which directions are null. + /// + private static double SingularValueThreshold(SingularValueDecomposition singularValues) + { + return ZeroToleranceFactor * RelativeMachineEpsilon * singularValues.W[0]; + } + /// /// Determines whether a point lies on the affine support μ + range(Σ) of the distribution. /// @@ -467,14 +539,25 @@ private void FactorizeWithSingularValues() /// support is the whole space. /// /// + /// /// The centred point is projected onto the orthonormal null-space basis returned by /// . The projection is compared against - /// times the machine epsilon, scaled by the magnitude of the - /// centred point so that the test stays meaningful for points far from the mean, where the roundoff - /// in the projection grows in proportion. On the reference cases of + /// · , scaled by the magnitude + /// of the centred point so that the test stays meaningful for points far from the mean, where the + /// roundoff in the projection grows in proportion. On the reference cases of /// an on-support point projects to at /// most 6E-16 while an off-support point projects to more than 0.7, so the test has several orders /// of margin on both sides. + /// + /// + /// The scaling is by ‖x − μ‖, which is not what + /// scipy.stats.multivariate_normal does: scipy scales its support tolerance by the + /// eigenvalue magnitude of Σ instead. The two agree whenever Σ is of order one, and diverge for a + /// large-scale singular Σ, where scipy's tolerance becomes very loose — for Σ = 1E12 · [[1,1],[1,1]] + /// scipy's tolerance is about 4.4E+5, so it returns a finite density at (1, −1), a point plainly off + /// the support. This class returns negative infinity there, which is the exact answer, and that is + /// deliberate. + /// /// private bool IsOnSupport(double[] x) { @@ -487,7 +570,7 @@ private bool IsOnSupport(double[] x) z[i] = x[i] - _mean[i]; norm += z[i] * z[i]; } - double tolerance = ZeroToleranceFactor * Tools.DoubleMachineEpsilon * Math.Max(1d, Math.Sqrt(norm)); + double tolerance = ZeroToleranceFactor * RelativeMachineEpsilon * Math.Max(1d, Math.Sqrt(norm)); for (int j = 0; j < _nullspace.NumberOfColumns; j++) { double projection = 0d; @@ -529,8 +612,33 @@ private void CreateCorrelationMatrix() /// The mean vector μ (mu) for the distribution. /// The covariance matrix Σ (sigma) for the distribution. /// Determines whether to throw an exception or not. + /// The reason the parameters are invalid, or null when they are valid. + /// Thrown when the parameters are invalid and + /// is true. + /// + /// The covariance is checked against the requirement of the decomposition method chosen at + /// construction: strictly positive-definite under , + /// symmetric positive semi-definite under . See + /// for the threshold that separates the two. + /// public ArgumentOutOfRangeException? ValidateParameters(double[] mean, double[,] covariance, bool throwException) { + return ValidateParameters(mean, covariance, throwException, out _); + } + + /// + /// Validate the parameters, handing back the decomposition built along the way so that + /// does not have to recompute it. + /// + /// The mean vector μ (mu) for the distribution. + /// The covariance matrix Σ (sigma) for the distribution. + /// Determines whether to throw an exception or not. + /// On return, the decomposition of the covariance matrix under + /// when the parameters are valid; null otherwise. + /// The reason the parameters are invalid, or null when they are valid. + private ArgumentOutOfRangeException? ValidateParameters(double[] mean, double[,] covariance, bool throwException, out SingularValueDecomposition? singularValues) + { + singularValues = null; if (mean == null) { var ex = new ArgumentOutOfRangeException(nameof(mean), "Mean vector must not be null."); @@ -581,10 +689,15 @@ private void CreateCorrelationMatrix() if (throwException) throw ex; else return ex; } } - else if (!IsSymmetricPositiveSemiDefinite(m)) + else { - var ex = new ArgumentOutOfRangeException(nameof(Covariance), "Covariance matrix is not symmetric positive-semi-definite."); - if (throwException) throw ex; else return ex; + var svd = new SingularValueDecomposition(m); + if (!IsSymmetricPositiveSemiDefinite(svd, m)) + { + var ex = new ArgumentOutOfRangeException(nameof(Covariance), "Covariance matrix is not symmetric positive-semi-definite."); + if (throwException) throw ex; else return ex; + } + singularValues = svd; } return null; } @@ -593,47 +706,70 @@ private void CreateCorrelationMatrix() /// Determines whether a matrix is symmetric and positive semi-definite, the requirement of the /// singular value decomposition path. /// + /// The decomposition of the candidate covariance matrix. /// The candidate covariance matrix. - /// True when the matrix is symmetric with no negative eigenvalue. + /// True when the matrix is symmetric with no eigenvalue below −ε. /// /// - /// Singular values are unsigned, so they cannot by themselves distinguish a negative eigenvalue from - /// a positive one. The test instead compares the matrix against its own symmetric reconstruction - /// U·W·Uᵀ. For a symmetric matrix the singular value decomposition returns Wⱼ = |λⱼ| with left and - /// right singular vectors that agree up to the sign of λⱼ, so the reconstruction reproduces the - /// matrix exactly when every eigenvalue is non-negative and misses by about 2·|λ| for each negative - /// eigenvalue. An asymmetric matrix likewise fails to reconstruct. The test therefore checks exactly - /// the property the sampling factor A = U·sqrt(W) depends on, and unlike a sign test on the singular - /// vectors it stays correct when eigenvalues of equal magnitude and opposite sign make the singular - /// subspace ambiguous. + /// Singular values are unsigned — Wⱼ = |λⱼ| — so they cannot by themselves tell a negative eigenvalue + /// from a positive one. The signed eigenvalues are recovered as the Rayleigh quotients + /// λⱼ = uⱼᵀ·Σ·uⱼ, which is exact whenever the columns of U are eigenvectors of Σ. The matrix is + /// rejected when min(λ) < −ε, with ε from — the same threshold + /// that later decides the rank, the null space, the pseudo-determinant and the pseudo-inverse. + /// Sharing it is what makes the outcome coherent: an eigenvalue this test tolerates as + /// negative-by-roundoff is one the factorization then zeroes, so it can contribute neither + /// sampling noise nor a log|λ| term to the pseudo-determinant. This is + /// scipy.stats._multivariate._PSD, verified against scipy 1.17.1. + /// + /// + /// The Rayleigh quotients alone are not sufficient, because they are only the eigenvalues when U is + /// an eigenbasis. When Σ is asymmetric, or when eigenvalues of equal magnitude and opposite sign make + /// the singular subspace ambiguous — Σ = [[0, 2], [2, 0]] has eigenvalues +2 and −2, and every + /// quotient comes out at zero — U is not an eigenbasis and the quotients say nothing useful. The + /// second test closes that gap by requiring Σ to equal its own reconstruction Σⱼ λⱼ uⱼ uⱼᵀ, which is + /// precisely the property the sampling factor A = U·sqrt(W) relies on. For [[0, 2], [2, 0]] the + /// reconstruction misses by 2.0 and the matrix is correctly rejected. /// /// - /// The comparison uses times the machine epsilon, scaled by the - /// magnitude of the largest entry so that the test is invariant to the units of the covariance. This - /// tolerates eigenvalues that are negative only by roundoff, matching the convention of - /// scipy.stats.multivariate_normal. + /// Both comparisons use ε, which is proportional to the largest eigenvalue and therefore invariant + /// to the units of the covariance. Measured margins: the reconstruction residual of a genuine + /// symmetric covariance is about 4E-15 against an ε of 1E-9 on the reference cases, and a genuinely + /// indefinite or asymmetric matrix misses by an amount of order one. /// /// - private static bool IsSymmetricPositiveSemiDefinite(Matrix covariance) + private static bool IsSymmetricPositiveSemiDefinite(SingularValueDecomposition singularValues, Matrix covariance) { int n = covariance.NumberOfRows; - double scale = 0d; - for (int i = 0; i < n; i++) + double threshold = SingularValueThreshold(singularValues); + + // Signed eigenvalues by Rayleigh quotient. A single negative eigenvalue below the threshold + // means the matrix is genuinely indefinite, not merely singular. + var eigenvalues = new double[n]; + for (int j = 0; j < n; j++) { - for (int j = 0; j < n; j++) - scale = Math.Max(scale, Math.Abs(covariance[i, j])); + double quotient = 0d; + for (int i = 0; i < n; i++) + { + double row = 0d; + for (int k = 0; k < n; k++) + row += covariance[i, k] * singularValues.U[k, j]; + quotient += singularValues.U[i, j] * row; + } + eigenvalues[j] = quotient; + if (!(quotient >= -threshold)) return false; } - double tolerance = ZeroToleranceFactor * Tools.DoubleMachineEpsilon * Math.Max(1d, scale); - var svd = new SingularValueDecomposition(covariance); + + // The quotients are the eigenvalues only if U diagonalizes the matrix, so require the + // reconstruction to hold. This is also what rejects an asymmetric covariance. for (int i = 0; i < n; i++) { for (int j = i; j < n; j++) { double reconstructed = 0d; for (int k = 0; k < n; k++) - reconstructed += svd.U[i, k] * svd.W[k] * svd.U[j, k]; - if (!(Math.Abs(reconstructed - covariance[i, j]) <= tolerance)) return false; - if (!(Math.Abs(reconstructed - covariance[j, i]) <= tolerance)) return false; + reconstructed += eigenvalues[k] * singularValues.U[i, k] * singularValues.U[j, k]; + if (!(Math.Abs(reconstructed - covariance[i, j]) <= threshold)) return false; + if (!(Math.Abs(reconstructed - covariance[j, i]) <= threshold)) return false; } } return true; @@ -871,7 +1007,7 @@ public double Mahalanobis(double[] x) { if (x.Length != Dimension) throw new ArgumentOutOfRangeException(nameof(x), "The vector must be the same dimension as the distribution."); - // + // var z = new double[_mean.Length]; for (int i = 0; i < x.Length; i++) z[i] = x[i] - _mean[i]; diff --git a/Test_Numerics/Distributions/Multivariate/Test_MultivariateNormal.cs b/Test_Numerics/Distributions/Multivariate/Test_MultivariateNormal.cs index c8b4a798..0f7a23a1 100644 --- a/Test_Numerics/Distributions/Multivariate/Test_MultivariateNormal.cs +++ b/Test_Numerics/Distributions/Multivariate/Test_MultivariateNormal.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Distributions; using Numerics.Mathematics.LinearAlgebra; @@ -954,7 +955,15 @@ public void Test_SVD_SeededDrawsOnRankOneCovarianceAreCollinear() /// /// The expected values were captured from the library immediately before the decomposition selector /// of issue #145 was added, and are asserted exactly — not within a tolerance — so the Cholesky path - /// is held bit-identical. + /// is held bit-identical. Every entry point that was rewired to the shared sampling factor is + /// covered: , + /// , + /// and + /// ; so are the three that gained a branch on the + /// selector, , and + /// ; and so are + /// and , which were + /// not rewired but share the factorization. /// [TestMethod] public void Test_Cholesky_SeededOutputIsUnchanged() @@ -994,10 +1003,45 @@ public void Test_Cholesky_SeededOutputIsUnchanged() Assert.AreEqual(1.3592242172276996d, inverse[1], 0d); Assert.AreEqual(4.460838864683783d, inverse[2], 0d); + var bins = new List + { + new StratificationBin(0.00d, 0.25d), + new StratificationBin(0.25d, 0.50d), + new StratificationBin(0.50d, 0.75d), + new StratificationBin(0.75d, 1.00d) + }; + var stratified = cholesky.StratifiedRandomValues(bins, 5150); + var expectedStratified = new[,] + { + { -1.3006987607520157d, -0.8144318863167115d, 4.461886627255922d }, + { 0.3627212720712497d, 4.646543433116568d, 0.8644127494580243d }, + { 1.6372787279287504d, 1.5022701945239427d, 0.7967225481719957d }, + { 3.3006987607520157d, 2.4317362224467263d, 3.204286114090976d } + }; + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < 3; j++) + Assert.AreEqual(expectedStratified[i, j], stratified[i, j], 0d); + } + Assert.AreEqual(-4.2849940472992305d, cholesky.LogPDF(new[] { 1d, 2d, 3d }), 0d); Assert.AreEqual(-6.9894058120051135d, cholesky.LogPDF(new[] { 0d, 0d, 0d }), 0d); Assert.AreEqual(-5.108155812005113d, cholesky.LogPDF(new[] { 2.5d, 1d, 4d }), 0d); Assert.AreEqual(-7.226170517887466d, cholesky.LogPDF(new[] { -1d, 5d, 2d }), 0d); + + // PDF carries its own branch on the decomposition selector, so it is pinned separately + // rather than inferred from LogPDF. + Assert.AreEqual(0.013773703511785982d, cholesky.PDF(new[] { 1d, 2d, 3d }), 0d); + Assert.AreEqual(0.0009215939690854924d, cholesky.PDF(new[] { 0d, 0d, 0d }), 0d); + Assert.AreEqual(0.006047224866023802d, cholesky.PDF(new[] { 2.5d, 1d, 4d }), 0d); + Assert.AreEqual(0.000727300722148393d, cholesky.PDF(new[] { -1d, 5d, 2d }), 0d); + Assert.AreEqual(1.6463235294117646d, cholesky.Mahalanobis(new[] { 2.5d, 1d, 4d }), 0d); + + // The CDF advances the lattice generator, so this is pinned on an instance that has not + // evaluated it yet. + var forCdf = new MultivariateNormal(Case1Mean, Case1Covariance); + Assert.AreEqual(0.42500215172393263d, forCdf.CDF(new[] { 2d, 3d, 4d }), 0d); + Assert.AreEqual(0.370099135561744d, forCdf.Interval(new[] { 0d, 0d, 0d }, new[] { 3d, 4d, 5d }), 0d); } /// @@ -1140,9 +1184,14 @@ public void Test_Decomposition_CdfAndConditionalHelpersAreUnaffected() var cholesky = new MultivariateNormal(Case1Mean, Case1Covariance); var singular = new MultivariateNormal(Case1Mean, Case1Covariance, DecompositionMethod.SingularValue); - // MVNDST is a randomized lattice rule that advances its generator, so compare first - // evaluations on freshly constructed instances. - Assert.AreEqual(cholesky.CDF(new[] { 2d, 3d, 4d }), singular.CDF(new[] { 2d, 3d, 4d }), 0d); + // MVNDST is a randomized lattice rule that advances its generator, so this must be the first + // evaluation on each freshly constructed instance, and it is pinned rather than compared + // between the two: a comparison would also pass if both instances had drifted together, and + // would depend on argument evaluation order. A second call on the same instance returns + // 0.4250624631430677, which is how far the lattice shift moves the answer. + const double expectedCdf = 0.42500215172393263d; + Assert.AreEqual(expectedCdf, cholesky.CDF(new[] { 2d, 3d, 4d }), 0d); + Assert.AreEqual(expectedCdf, singular.CDF(new[] { 2d, 3d, 4d }), 0d); // The conditional and marginal helpers return distributions on the default selector. var conditional = singular.Conditional(new[] { 2 }, new[] { 3d }); @@ -1157,6 +1206,175 @@ public void Test_Decomposition_CdfAndConditionalHelpersAreUnaffected() Assert.AreEqual(choleskyMarginal.LogPDF(new[] { 1d, 2d }), marginal.LogPDF(new[] { 1d, 2d }), 0d); } + + /// + /// A covariance with a negative variance small enough to be roundoff must be accepted and treated + /// as a degenerate normal with that direction removed, not kept as a positive variance. + /// + /// + /// + /// Σ = [[4, 0], [0, −1E-10]] is the case that forced this threshold to be reworked. The class + /// originally carried two different notions of "numerically zero" about 6.7 orders of magnitude + /// apart: an acceptance tolerance of roughly 5.6E-11·max|Σᵢⱼ| and a rank threshold of roughly + /// 1.5E-16·λmax. Everything in that band was mishandled, because a tolerated eigenvalue was + /// kept rather than zeroed — the direction acquired sqrt(|λ|) of sampling noise and + /// contributed log|λ| to the log pseudo-determinant. This matrix was accepted, reported + /// true, and returned a log density of about + /// +8.98 at the origin: a negative variance silently turned into a positive one, which is the + /// failure mode issue #145 exists to remove. + /// + /// + /// A single threshold ε = 1E6·2⁻⁵²·max|λ| = 8.881784197001252E-10 now governs acceptance, rank, + /// null space, pseudo-determinant and pseudo-inverse alike. The eigenvalue −1E-10 lies inside it, + /// so the matrix is accepted and that direction is zeroed, giving a rank-1 normal on the x-axis with + /// log pseudo-determinant log(4) = 1.3862943611198906 and + /// logpdf(0,0) = −0.5·(log(2π) + log(4)) = −1.612085713764618. Oracle: + /// scipy.stats.multivariate_normal(mean, cov, allow_singular=True), scipy 1.17.1, which + /// returns exactly that. + /// + /// + [TestMethod] + public void Test_SVD_RoundoffNegativeEigenvalueIsZeroedNotKept() + { + var mean = new[] { 0d, 0d }; + var covariance = new[,] { { 4d, 0d }, { 0d, -1E-10d } }; + var singular = new MultivariateNormal(mean, covariance, DecompositionMethod.SingularValue); + + // Rank 1, not 2: the negative direction is zeroed, so the covariance is not positive-definite. + Assert.IsFalse(singular.IsPositiveDefinite); + + Assert.AreEqual(-1.612085713764618d, singular.LogPDF(new[] { 0d, 0d }), OracleTolerance); + Assert.AreEqual(-1.737085713764618d, singular.LogPDF(new[] { 1d, 0d }), OracleTolerance); + Assert.AreEqual(Math.Exp(-1.612085713764618d), singular.PDF(new[] { 0d, 0d }), 1E-15d); + + // The support is the x-axis, so any point off it has zero density. Before the fix this + // returned a finite value, because the zeroed direction was still being sampled and inverted. + Assert.AreEqual(double.NegativeInfinity, singular.LogPDF(new[] { 0d, 1d })); + Assert.AreEqual(0d, singular.PDF(new[] { 0d, 1d }), 0d); + + // The zeroed direction carries no sampling noise at all: every draw lands on the x-axis. + var sample = singular.GenerateRandomValues(100, 999); + for (int i = 0; i < 100; i++) + Assert.AreEqual(0d, sample[i, 1], 0d); + + // The default Cholesky selector still rejects this matrix outright. The two paths diverge here + // by design — Cholesky factorizes only strictly positive-definite matrices — and that + // divergence is asserted rather than left to chance. + AssertThrowsAny(() => new MultivariateNormal(mean, covariance)); + } + + /// + /// A genuinely indefinite covariance must still be rejected by the singular value path, and the + /// accept/reject boundary must sit exactly at the scipy threshold. + /// + /// + /// Tolerating eigenvalues that are negative only by roundoff must not become tolerance for real + /// negative variance. With max|λ| = 4 the threshold is ε = 1E6·2⁻⁵²·4 = 8.881784197001252E-10, and + /// the sweep below pins the cut to that value: −8.8E-10 is inside and accepted, −8.9E-10 is outside + /// and rejected. scipy.stats.multivariate_normal raises + /// "The input matrix must be symmetric positive semidefinite" for [[4,0],[0,−1]] under the + /// same rule. + /// + [TestMethod] + public void Test_SVD_GenuinelyIndefiniteCovarianceIsStillRejected() + { + var mean = new[] { 0d, 0d }; + + // A full-sized negative eigenvalue, far outside the threshold. + AssertThrowsOutOfRange(() => new MultivariateNormal(mean, new[,] { { 4d, 0d }, { 0d, -1d } }, DecompositionMethod.SingularValue)); + + // Just inside the threshold: accepted, and the direction is zeroed. + var accepted = new MultivariateNormal(mean, new[,] { { 4d, 0d }, { 0d, -8.8E-10d } }, DecompositionMethod.SingularValue); + Assert.IsFalse(accepted.IsPositiveDefinite); + Assert.AreEqual(-1.612085713764618d, accepted.LogPDF(new[] { 0d, 0d }), OracleTolerance); + + // Just outside it: rejected. + AssertThrowsOutOfRange(() => new MultivariateNormal(mean, new[,] { { 4d, 0d }, { 0d, -8.9E-10d } }, DecompositionMethod.SingularValue)); + + // The non-throwing path agrees on both sides of the boundary. + var mutable = new MultivariateNormal(mean, new[,] { { 1d, 0d }, { 0d, 1d } }, DecompositionMethod.SingularValue); + Assert.IsTrue(mutable.TrySetCovariance(new[,] { { 4d, 0d }, { 0d, -8.8E-10d } })); + Assert.IsFalse(mutable.TrySetCovariance(new[,] { { 4d, 0d }, { 0d, -8.9E-10d } })); + Assert.IsFalse(mutable.IsDensityValid); + } + + /// + /// Guards the margin by which rank detection separates a numerically zero singular value from a + /// genuine one, on the headline case of the feature. + /// + /// + /// Case 2 is rank-deficient by exactly one, and its null singular value comes out at about + /// 3.23E-16 rather than exactly zero. Against the roundoff-based default threshold of the + /// decomposition, 6.107E-16, that is a margin of only 1.9x — thin enough that a slightly different + /// matrix, or a slightly less accurate decomposition, misdetects the rank, at which point + /// comes back empty and off-support detection is + /// silently disabled. The scipy threshold of 1E6·2⁻⁵²·λmax ≈ 9.233E-10 turns that 1.9x into about + /// 2.9E6, while still sitting about 9E8 below the smallest genuine singular value. This test + /// pins both ends of that window so a future tightening cannot quietly re-break detection. + /// + [TestMethod] + public void Test_SVD_RankDetectionMarginIsLarge() + { + var svd = new SingularValueDecomposition(new Matrix(Case2Covariance)); + double threshold = 1E6 * 2.220446049250313E-16 * svd.W[0]; + + Assert.AreEqual(9.233308329420933E-10d, threshold, 1E-20d); + + // The numerically zero singular value is far below the threshold. + Assert.IsLessThan(1E-14d, svd.W[2]); + Assert.IsGreaterThan(1E5d, threshold / svd.W[2]); + + // The smallest genuine singular value is far above it. + Assert.IsGreaterThan(1E7d, svd.W[1] / threshold); + + // And the rank that follows is the one the density depends on. + Assert.AreEqual(2, svd.Rank(threshold)); + Assert.AreEqual(1, svd.Nullity(threshold)); + } + + /// + /// Pins the rank-0 convention: an identically zero covariance is a point mass at the mean. + /// + /// + /// + /// With Σ = 0 the support is the single point μ and the rank is zero, so the normalizing constant, + /// the log pseudo-determinant and the quadratic form are all zero and the log density at μ is zero, + /// i.e. a density of 1. That is the counting-measure density the rank-r convention of + /// implies when r = 0, it needs no special case, and + /// it is consistent with the sampler, which returns μ exactly every time. Every other point is off + /// the support and scores zero. + /// + /// + /// This is a deliberate departure from scipy.stats.multivariate_normal, which returns + /// negative infinity even at μ. Verified against scipy 1.17.1: the rank and log pseudo-determinant + /// that scipy itself reports for this matrix are 0 and 0.0, which imply a log density of zero, but + /// its support test compares the residual with a strict < against a threshold that is + /// itself exactly zero here, so no point at all passes — including the mean. + /// + /// + [TestMethod] + public void Test_SVD_ZeroCovarianceIsAPointMassAtTheMean() + { + var mean = new[] { 3d, -1d }; + var singular = new MultivariateNormal(mean, new[,] { { 0d, 0d }, { 0d, 0d } }, DecompositionMethod.SingularValue); + + Assert.IsFalse(singular.IsPositiveDefinite); + Assert.AreEqual(0d, singular.LogPDF(mean), 0d); + Assert.AreEqual(1d, singular.PDF(mean), 0d); + + Assert.AreEqual(double.NegativeInfinity, singular.LogPDF(new[] { 3d, 0d })); + Assert.AreEqual(0d, singular.PDF(new[] { 3d, 0d }), 0d); + Assert.AreEqual(double.NegativeInfinity, singular.LogPDF(new[] { 0d, -1d })); + + // Every draw is the mean exactly, which is what makes the density at the mean the right answer. + var sample = singular.GenerateRandomValues(10, 271828); + for (int i = 0; i < 10; i++) + { + Assert.AreEqual(mean[0], sample[i, 0], 0d); + Assert.AreEqual(mean[1], sample[i, 1], 0d); + } + } + #endregion } } diff --git a/Test_Numerics/Mathematics/Linear Algebra/Test_SingularValueDecomp.cs b/Test_Numerics/Mathematics/Linear Algebra/Test_SingularValueDecomp.cs index 84080938..877f412c 100644 --- a/Test_Numerics/Mathematics/Linear Algebra/Test_SingularValueDecomp.cs +++ b/Test_Numerics/Mathematics/Linear Algebra/Test_SingularValueDecomp.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Mathematics.LinearAlgebra; From e8ad6d87a91f569a8f5d12e63cb7c01c698d657b Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 24 Aug 2026 18:04:30 -0600 Subject: [PATCH 070/222] Document the residual rank-deficiency the Cholesky pivot test does not catch --- .../Linear Algebra/CholeskyDecomposition.cs | 52 ++++++-- .../Test_CholeskyDecomposition.cs | 116 ++++++++++++++++++ .../Test_MatrixRegularization.cs | 4 +- 3 files changed, 160 insertions(+), 12 deletions(-) diff --git a/Numerics/Mathematics/Linear Algebra/CholeskyDecomposition.cs b/Numerics/Mathematics/Linear Algebra/CholeskyDecomposition.cs index 600861d4..8b3a15c5 100644 --- a/Numerics/Mathematics/Linear Algebra/CholeskyDecomposition.cs +++ b/Numerics/Mathematics/Linear Algebra/CholeskyDecomposition.cs @@ -51,7 +51,15 @@ public class CholeskyDecomposition /// Thrown when the matrix A is not square. /// Thrown when the matrix A is not positive-definite. /// + /// /// The pivot tolerance is evaluated at the dimension of A. + /// + /// + /// Success is not a rank certificate. The pivot test rejects most numerically rank-deficient + /// matrices but not all of them — for a covariance estimated from m = n - 1 observations, 11% to + /// 18% still factorize — so being true does not prove full rank. Use a + /// singular value decomposition when the rank genuinely has to be known. + /// /// public CholeskyDecomposition(Matrix A) : this(A, DefaultRelativeTolerance(A.NumberOfRows)) @@ -91,6 +99,16 @@ public CholeskyDecomposition(Matrix A) /// log pseudo-determinant 1.2528. See . /// /// + /// This test is not a rank certificate. It rejects most numerically rank-deficient matrices but + /// not all of them, so being true does not prove the matrix has full + /// rank. Measured over 1,000 trials per dimension on a covariance estimated from m = n - 1 + /// observations of n variables — exactly rank m, and the commonest way rank deficiency + /// arises in practice — the test removes roughly two thirds of the matrices the absolute test had + /// accepted, while 11% to 18% still factorize. When the rank of a matrix genuinely has to be known, + /// use a singular value decomposition; for a multivariate normal that is + /// DecompositionMethod.SingularValue, which is the only reliable rank test in this library. + /// + /// /// When A[i,i] is not a positive finite number the threshold falls back to zero, which is the /// absolute test. That case cannot weaken the result: a positive-definite matrix has a strictly positive /// finite diagonal, so a non-positive or non-finite diagonal is rejected on its own merits. @@ -164,24 +182,38 @@ public CholeskyDecomposition(Matrix A, double relativeTolerance) /// Returns the default scale-relative pivot tolerance for a matrix of the given dimension. /// /// The number of rows in the matrix to be decomposed. - /// The tolerance dimension * 2^-52, or zero when the dimension is not positive. + /// + /// The tolerance, approximately dimension * 2^-52, or zero when the dimension is not positive. + /// /// /// - /// The value is n * 2^-52 ≈ n * 2.22E-16, expressed here as 2 * n * + /// The value is approximately n * 2^-52 ≈ n * 2.22E-16, computed as 2 * n * /// because that constant is the unit roundoff 2^-53 - /// rather than the double-precision spacing 2^-52. + /// rather than the double-precision spacing 2^-52. The library constant is a decimal-truncated + /// 2^-53, so the computed tolerance exceeds n * 2^-52 by about 3.1E-15 relative. /// /// /// This tracks the standard backward-error bound for Cholesky factorization, in which the computed pivot - /// differs from the exact one by a quantity of order n unit roundoffs times the corresponding - /// diagonal entry. A pivot at or below that level carries no information beyond rounding noise. + /// differs from the exact one by at most γ_i * A[i,i] — of order n unit roundoffs times + /// the corresponding diagonal entry. A pivot at or below that level carries no information beyond + /// rounding noise. Because the noise floor scales with A[i,i] and not with the largest diagonal, + /// the test is invariant under the rescaling D*A*D for a positive diagonal D, which is the + /// correct invariance for a covariance: changing the units of one variable must not change whether the + /// matrix is accepted. + /// + /// + /// The margin against falsely rejecting a legitimate matrix is large. For two variables with correlation + /// ρ the pivot ratio is 1 - ρ², so a false rejection at n = 2 requires + /// 1 - ρ² <= 4.44E-16, that is ρ within about two ulp of one. Measured across the Numerics + /// test suite (29.8 million factorizations) the smallest ratio produced by a genuinely positive-definite + /// matrix is 2.0E-12, some 4,500 times the tolerance that applies to it. /// /// - /// The margin against legitimate matrices is large. For two variables with correlation ρ the pivot ratio - /// is 1 - ρ², so a false rejection at n = 2 requires 1 - ρ² <= 4.44E-16, that is ρ - /// within about two ulp of one. Measured across the Numerics test suite (29.8 million factorizations) the - /// smallest ratio produced by a genuinely positive-definite matrix is 2.0E-12, some 4,500 times the - /// tolerance that applies to it. + /// The margin in the other direction is far smaller, and no tolerance of this form can close it. A + /// rank-deficient matrix whose accumulated rounding leaves a pivot ratio above the tolerance is still + /// accepted, and that is not rare: for a covariance estimated from m = n - 1 observations of + /// n variables, 11% to 18% of trials still factorize. Raising the tolerance would eat into the + /// false-rejection margin without fixing this; determining rank requires a singular value decomposition. /// /// public static double DefaultRelativeTolerance(int dimension) diff --git a/Test_Numerics/Mathematics/Linear Algebra/Test_CholeskyDecomposition.cs b/Test_Numerics/Mathematics/Linear Algebra/Test_CholeskyDecomposition.cs index 2bbee05d..d189313a 100644 --- a/Test_Numerics/Mathematics/Linear Algebra/Test_CholeskyDecomposition.cs +++ b/Test_Numerics/Mathematics/Linear Algebra/Test_CholeskyDecomposition.cs @@ -280,6 +280,15 @@ private static Exception AssertThrowsAny(Action action) /// let the factorization complete and reported the matrix positive-definite. The pivot ratio is /// 2.220446E-16, which the default tolerance of 3 * 2^-52 = 6.661338E-16 rejects with a /// factor of three to spare. + /// + /// This case being caught must not be read as a guarantee that rank deficiency is caught in general. + /// The test rejects most numerically rank-deficient matrices but not all of them: over 1,000 trials + /// per dimension on a covariance estimated from m = n - 1 observations of n variables, + /// it removes about two thirds of the matrices the absolute test accepted, and 11% to 18% still + /// factorize. A successful factorization is not a rank certificate; + /// DecompositionMethod.SingularValue on MultivariateNormal is the only reliable rank + /// test in this library. + /// /// [TestMethod] public void Test_RejectsExactlyRankDeficientMatrix() @@ -423,6 +432,113 @@ public void Test_NonPositiveDefiniteRejectionIsUnchanged() } } + /// + /// A correlation whose second pivot ratio is exactly 3 * 2^-52, straddling the tolerance + /// between n = 2 and n = 4. + /// + /// + /// The first factor row is exact — L[0,0] = 1 and L[1,0] = ρ — and + /// 1 - fl(ρ²) is exact by Sterbenz, so the pivot is bit-reproducible at + /// 6.661338147750939E-16 rather than being an artifact of rounding order. + /// + private const double ThreeUlpCorrelation = 0.99999999999999967d; + + /// + /// Verifies that the tolerance really does scale with the dimension, by presenting the same + /// near-singular two-variable block at two different dimensions and getting opposite verdicts. + /// + /// + /// The block's pivot ratio is exactly 3 * 2^-52 = 6.661338E-16. At n = 2 the tolerance + /// is 2 * 2^-52 = 4.440892E-16 and the block is accepted; padded with two independent unit + /// variances to n = 4 the tolerance is 4 * 2^-52 = 8.881784E-16 and the identical block + /// is rejected. Nothing about the block changed, only the dimension. Were the n factor dropped + /// from the tolerance would be + /// 2.220446E-16 at both sizes and the 4x4 assertion here would fail, so this test pins the scaling as + /// behaviour and not merely as a formula. + /// + [TestMethod] + public void Test_ToleranceScalesWithDimension() + { + double rho = ThreeUlpCorrelation; + double expectedPivot = 3d * 2d * Tools.DoubleMachineEpsilon; + + // n = 2: tolerance 2 * 2^-52, below the pivot, so the block factorizes. + var small = new CholeskyDecomposition(new Matrix(new[,] { { 1d, rho }, { rho, 1d } })); + Assert.IsTrue(small.IsPositiveDefinite); + Assert.AreEqual(expectedPivot, small.L[1, 1] * small.L[1, 1], 1E-24d); + Assert.IsGreaterThan(small.RelativeTolerance, small.L[1, 1] * small.L[1, 1]); + + // n = 4: the same block, padded. Tolerance 4 * 2^-52 now exceeds the pivot. + var padded = new Matrix(new[,] + { + { 1d, rho, 0d, 0d }, + { rho, 1d, 0d, 0d }, + { 0d, 0d, 1d, 0d }, + { 0d, 0d, 0d, 1d } + }); + Assert.IsLessThan(CholeskyDecomposition.DefaultRelativeTolerance(4), expectedPivot); + var exception = AssertThrowsAny(() => new CholeskyDecomposition(padded)); + Assert.IsGreaterThanOrEqualTo(0, exception.Message.IndexOf("positive-definite", StringComparison.OrdinalIgnoreCase)); + + // The same 4x4 is accepted once the tolerance is lowered below the pivot, confirming the pivot + // itself — not some other property of the padded matrix — is what the dimension changed. + Assert.IsTrue(new CholeskyDecomposition(padded, 2d * Tools.DoubleMachineEpsilon).IsPositiveDefinite); + } + + /// + /// Verifies the diagonal guard: when A[i,i] is not a positive finite number the threshold falls + /// back to zero, so the outcome is identical to the purely absolute test. + /// + /// + /// The class is public and can be handed anything, so the guard is exercised with a zero matrix, a + /// zero on the diagonal behind a healthy leading entry, a negative diagonal, an infinite diagonal and + /// a NaN diagonal. Each is compared against the same matrix at relativeTolerance = 0 rather + /// than against a hard-coded verdict, which is exactly the property the guard is there to provide: + /// a non-positive or non-finite diagonal cannot make the relative test behave differently from the + /// absolute one. The infinite case is accepted by both, which is pre-existing behaviour, not an + /// endorsement. + /// + [TestMethod] + public void Test_NonPositiveOrNonFiniteDiagonalFallsBackToTheAbsoluteTest() + { + var cases = new[] + { + new[,] { { 0d, 0d }, { 0d, 0d } }, + new[,] { { 1d, 0d }, { 0d, 0d } }, + new[,] { { -1d, 0d }, { 0d, 1d } }, + new[,] { { double.PositiveInfinity, 0d }, { 0d, 1d } }, + new[,] { { double.NaN, 0d }, { 0d, 1d } } + }; + + foreach (var entries in cases) + { + var A = new Matrix(entries); + string label = "[[" + entries[0, 0].ToString("R") + ",...]]"; + + CholeskyDecomposition withDefault = null, withZero = null; + Exception defaultException = null, zeroException = null; + try { withDefault = new CholeskyDecomposition(A); } + catch (Exception ex) { defaultException = ex; } + try { withZero = new CholeskyDecomposition(A, 0d); } + catch (Exception ex) { zeroException = ex; } + + Assert.AreEqual(zeroException == null, defaultException == null, + "The relative and absolute tests must agree on " + label); + if (defaultException != null) + { + // A guarded diagonal takes the original branch, so the message is the original one. + Assert.AreEqual(zeroException.Message, defaultException.Message, label); + Assert.AreEqual("Cholesky Decomposition failed. The input matrix is not positive-definite.", + defaultException.Message, label); + } + else + { + Assert.AreEqual(withZero.L[0, 0], withDefault.L[0, 0], 0d, label); + Assert.AreEqual(withZero.L[1, 1], withDefault.L[1, 1], 0d, label); + } + } + } + } } diff --git a/Test_Numerics/Mathematics/Linear Algebra/Test_MatrixRegularization.cs b/Test_Numerics/Mathematics/Linear Algebra/Test_MatrixRegularization.cs index 0a6a49b0..1ba4ade5 100644 --- a/Test_Numerics/Mathematics/Linear Algebra/Test_MatrixRegularization.cs +++ b/Test_Numerics/Mathematics/Linear Algebra/Test_MatrixRegularization.cs @@ -89,8 +89,8 @@ public void Test_MakeSymmetricPositiveDefinite_WellConditionedTakesTheBaseRidge( /// /// This is the case the scale-relative pivot test was introduced for: the third row of the input /// equals the first, so the raw matrix is exactly rank two. The base ridge of - /// 1E-10 * 5 / 3 = 1.667E-10 lifts the smallest eigenvalue clear of the tolerance — - /// the final pivot ratio is about 8.3E-11 against a tolerance of 6.66E-16 — so the first attempt + /// 1E-10 * 5 / 3 = 1.666667E-10 lifts the smallest eigenvalue clear of the tolerance — + /// the final pivot ratio is 1.666668E-10 against a tolerance of 6.66E-16 — so the first attempt /// succeeds under both the old absolute test and the new relative one, and the returned matrix is /// unchanged. /// From 133336686a65935043b97409bdb83768d99ce3ac Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 24 Aug 2026 18:29:34 -0600 Subject: [PATCH 071/222] Pin the linear moments to an exact rational oracle on a skewed large sample --- .../Data/Statistics/Test_Statistics.cs | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/Test_Numerics/Data/Statistics/Test_Statistics.cs b/Test_Numerics/Data/Statistics/Test_Statistics.cs index 99b77e01..6a2a269c 100644 --- a/Test_Numerics/Data/Statistics/Test_Statistics.cs +++ b/Test_Numerics/Data/Statistics/Test_Statistics.cs @@ -368,6 +368,98 @@ public void Test_ComputeLinearMoments_LargeSample() Assert.AreEqual(0.1940943d, referenceMoments[3], 1E-7); } + /// + /// Test LinearMoments on a large, genuinely skewed sample against an exact rational-arithmetic oracle. + /// + /// + /// + /// This is the non-degenerate companion to . That + /// test uses an evenly spaced sample whose τ₃ and τ₄ are analytically exactly zero at every length, so + /// it cannot distinguish a sign error, a b₂-only correction or a partial fix from a complete one. This + /// test pins all four moments to non-zero values at two sample lengths. + /// + /// + /// The sample. xᵢ = kᵢ² / 64 with kᵢ = (i · 7919) mod 10007 for i = 1..n. Every value is an exact + /// integer divided by 64, a power of two, so the sample is exactly representable in IEEE-754 and the + /// test reproduces the oracle's input bit for bit. The generator itself cannot overflow: i · 7919 peaks + /// at 11,561,740 and k² at 100,120,036, both far inside . Squaring a + /// near-uniform k makes the sample right-skewed, which is what gives it a non-zero τ₃. + /// + /// + /// The oracle. Computed in exact rational arithmetic, where overflow is impossible by + /// construction, via the textbook binomial probability-weighted-moment form + /// b_r = (1/n) · Σᵢ C(i−1, r) / C(n−1, r) · x₍ᵢ₎ — a different arithmetic route than the + /// falling-factorial form the library uses. The values also agree with closed-form theory: this sample + /// is a systematic enumeration of a squared uniform, and for the quantile function Q(p) = p² theory + /// gives λ₁/λ₂ = 2, τ₃ = 0.2 and τ₄ = 0 exactly. The oracle returns 2.0013, 0.19955 and −2.2E−05, + /// converging on those with the expected finite-sample discreteness, so it is not merely + /// self-consistent. + /// + /// + /// The tolerance, and why τ₄ needs an absolute floor. The oracle values are exact, so the only + /// error present is the library's own double accumulation — but that error is absolute, not relative, + /// and τ₄ is the one moment small enough for the difference to matter. τ₄ is recovered as + /// 5·(2·(2·b₃ − 3·b₂) + b₀)/λ₂ + 6, an O(1) quantity plus 6, so a τ₄ near zero is the residue of + /// cancelling two numbers of order six. Its accuracy is therefore floored near the roundoff of six + /// accumulated across n terms, independent of how small τ₄ itself is. Measured against the exact + /// oracle, the library agrees to 0 ulp on λ₁, 1.1E-15 relative on λ₂ and 2.5E-14 relative on τ₃, but + /// only to 1.9E-14 and 2.7E-14 absolute on τ₄ — which at τ₄ ≈ 2.2E-05 is 8.5E-10 in relative + /// terms. This was verified to be arithmetic and not a defect by transcribing the library's formula + /// into an independent double-precision evaluation, which reproduces the library's returned value + /// digit for digit. The tolerance below is therefore relative 1E-12 with an absolute floor of 1E-13, + /// roughly four times the worst measured deviation. That floor costs the test nothing: the overflow + /// it guards moved τ₄ by 1.9E-01 and 1.7E+01, twelve to fifteen orders of magnitude above it. + /// + /// + /// What is being guarded. The probability-weighted-moment numerators were formed in 32-bit + /// integer arithmetic and wrapped silently on large samples (USACE-RMC/Numerics#146). On this same + /// sample the pre-fix code returned τ₄ = −0.185 at n = 1293 against an exact −3.80E−06, and + /// τ₄ = −17.01 at n = 1460 against an exact +1.40E−04. τ₄ is bounded roughly in [−0.25, 1] for any real + /// distribution, so −17.01 is not an imprecise answer but a meaningless one. n = 1292 is the last + /// length whose b₃ numerator fits in an — the pre-fix code is bit-compatible there, + /// which is why the test also pins a length above the threshold. n = 1460 is four years of daily data, + /// the scenario the issue reports as triggering it in practice. + /// + /// + [TestMethod] + public void Test_ComputeLinearMoments_ExactOracle() + { + // Exact L-moments {λ₁, λ₂, τ₃, τ₄} keyed by sample length. + var oracle = new Dictionary + { + { 1292, new[] { 522512.5024550116d, 261084.88504577902d, 0.19954627498563463d, -2.204844394426912E-05d } }, + { 1460, new[] { 522816.5913848459d, 261222.97693894972d, 0.19969563852408587d, 1.4021596010174597E-04d } } + }; + var names = new[] { "L-mean (λ₁)", "L-scale (λ₂)", "L-skewness (τ₃)", "L-kurtosis (τ₄)" }; + + foreach (var testCase in oracle) + { + int n = testCase.Key; + var data = new double[n]; + for (int i = 1; i <= n; i++) + { + int k = (i * 7919) % 10007; + data[i - 1] = k * (double)k / 64d; + } + + var lmoms = Numerics.Data.Statistics.Statistics.LinearMoments(data); + for (int m = 0; m < 4; m++) + { + double expected = testCase.Value[m]; + // Relative 1E-12, with an absolute floor of 1E-13 that binds only on τ₄. τ₄ is the + // residue of cancelling two O(6) quantities, so its error is absolute (measured at most + // 2.7E-14) and does not shrink with τ₄ itself. See the remarks above. + double tolerance = Math.Max(Math.Abs(expected) * 1E-12, 1E-13); + Assert.AreEqual(expected, lmoms[m], tolerance, + $"{names[m]} disagrees with the exact oracle at n = {n}."); + } + + // τ₄ is bounded roughly in [-0.25, 1] for any real distribution. The pre-fix code returned + // -17.01 at n = 1460, so this alone separates a corrupted result from a merely imprecise one. + Assert.IsTrue(lmoms[3] > -0.25d && lmoms[3] < 1d, $"L-kurtosis (τ₄) is out of range at n = {n}."); + } + } + /// /// Test the Percentile method against R's "quantile()" method from the "stats" package. /// From 142f6e943921a5b4d97f617b6343cc422f38f3d2 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 24 Aug 2026 18:29:36 -0600 Subject: [PATCH 072/222] Factorize the multivariate normal once on the non-throwing parameter path --- .../Multivariate/MultivariateNormal.cs | 75 +++++++++++++++++-- 1 file changed, 69 insertions(+), 6 deletions(-) diff --git a/Numerics/Distributions/Multivariate/MultivariateNormal.cs b/Numerics/Distributions/Multivariate/MultivariateNormal.cs index 30d0f739..303c3a83 100644 --- a/Numerics/Distributions/Multivariate/MultivariateNormal.cs +++ b/Numerics/Distributions/Multivariate/MultivariateNormal.cs @@ -147,12 +147,24 @@ public MultivariateNormal(double[] mean, double[,] covariance, DecompositionMeth /// The relative spacing of double-precision numbers, 2⁻⁵² ≈ 2.220446049250313E-16. /// /// - /// This is deliberately not , which is the unit - /// roundoff 2⁻⁵³ — exactly half of this value. The zero thresholds of this class are calibrated to - /// match scipy.stats.multivariate_normal value for value, and NumPy's + /// + /// This is deliberately not , which is approximately + /// the unit roundoff 2⁻⁵³, or about half of this value. The zero thresholds of this class are + /// calibrated to match scipy.stats.multivariate_normal value for value, and NumPy's /// np.finfo(float).eps, which scipy multiplies by , is the /// relative spacing 2⁻⁵². Using the unit roundoff instead would halve every threshold below and /// silently break that agreement. + /// + /// + /// The literal above is written out rather than derived as 2d * Tools.DoubleMachineEpsilon, + /// and that matters. is the decimal literal + /// 1.11022302462516E-16, which is a truncated 2⁻⁵³ and not 2⁻⁵³ itself: the exact value is + /// 1.1102230246251565E-16, so the ratio between the two constants is 1.9999999999999938 rather than + /// 2. Doubling the truncated constant would therefore not yield 2⁻⁵² bit-exactly, and the + /// thresholds built on it would drift off scipy's by a few ulps — enough to break an exact-agreement + /// test without producing any visible symptom. Do not "simplify" this back to a multiple of + /// . + /// /// private const double RelativeMachineEpsilon = 2.220446049250313E-16; @@ -379,6 +391,19 @@ public double[] StandardDeviation /// scipy's behaviour as well, and it is the price of a scale-invariant threshold. /// /// + /// What that rank drop looks like from the outside. It is silent: no exception is raised and no + /// diagnostic is produced. What the caller observes is that returns negative + /// infinity — and zero — at essentially every point, because after the drop the + /// support is the retained subspace and a general point has a non-negligible component off it. Inside + /// a likelihood evaluation or an MCMC loop that reads as total collapse rather than as a + /// conditioning problem, and the rank is not exposed publicly, so the only available signal is + /// returning false for a covariance the caller believes is full + /// rank. The remedy is to remove the scale disparity rather than the threshold: standardize Σ to a + /// correlation matrix, fit and evaluate in those units where all variances are of order one, and + /// rescale afterwards. With the scales shared, a relative threshold no longer discards directions + /// that are small only because of their units. + /// + /// /// Two deliberate departures from scipy. First, a point is tested against the support with a /// tolerance proportional to ‖x − μ‖, where scipy uses one proportional to the eigenvalue scale of /// Σ. For a large-scale singular covariance scipy therefore admits points that are visibly off the @@ -422,8 +447,27 @@ public void SetParameters(double[] mean, double[,] covariance) // decomposition it needs, so it is handed back and reused for the factorization rather than // being recomputed: an O(n^3) factorization is the whole cost this selector trades away. ValidateParameters(mean, covariance, true, out var singularValues); + SetParametersCore(mean, covariance, singularValues); + } - _dimension = mean.Length; + /// + /// Applies already-validated parameters, reusing the decomposition built during validation. + /// + /// The validated mean vector μ (mu) for the distribution. + /// The validated covariance matrix Σ (sigma) for the distribution. + /// The decomposition of produced by + /// validation under ; null under + /// , where it is not used. + /// + /// Split out of so that both the throwing entry point and the + /// non-throwing can validate once and factorize once. The + /// decomposition is a deterministic function of , so reusing the + /// instance built during validation produces exactly the same factorization that recomputing it + /// would — the saving is the duplicated O(n³) work, not a change of result. + /// + private void SetParametersCore(double[] mean, double[,] covariance, SingularValueDecomposition? singularValues) + { + _dimension = mean.Length; _mean = mean; _covariance = new Matrix(covariance); if (_decomposition == DecompositionMethod.Cholesky) @@ -558,6 +602,20 @@ private static double SingularValueThreshold(SingularValueDecomposition singular /// the support. This class returns negative infinity there, which is the exact answer, and that is /// deliberate. /// + /// + /// The absolute floor of the tolerance. Because the scale factor is max(1, ‖x − μ‖), the + /// smallest tolerance this test ever applies is + /// · = 2.220446049250313E-10. + /// It is not an independently chosen constant: it follows the class epsilon, so it is exactly + /// one factor of above the same ε that decides the rank, the null + /// space and the pseudo-determinant, and it moves only if that epsilon moves. It was + /// 1.11022302462516E-10 while the class used and doubled + /// when the constant was aligned with NumPy's relative spacing. The measured margins above — + /// on-support residuals at most 6E-16 and off-support residuals at least 0.7 — leave roughly five + /// orders of headroom below the tolerance and nine above it, so neither value changes any outcome on + /// the reference cases. Do not retune it in isolation; changing it would decouple this test from the + /// threshold that produced the null space it is testing against. + /// /// private bool IsOnSupport(double[] x) { @@ -797,9 +855,14 @@ public bool TrySetParameters(double[] mean, double[,] covariance) // ones, so the non-throwing contract absorbs both failure modes. try { - if (ValidateParameters(mean, covariance, false) is null) + // Validate through the overload that hands back the decomposition, then apply it directly. + // Routing through the public SetParameters would validate a second time and build a second + // singular value decomposition of the same matrix, doubling the O(n^3) cost on exactly the + // path this method exists to serve: proposal and likelihood loops that swap a covariance per + // evaluation. The decomposition is deterministic, so the factorization is unchanged. + if (ValidateParameters(mean, covariance, false, out var singularValues) is null) { - SetParameters(mean, covariance); + SetParametersCore(mean, covariance, singularValues); _densityValid = true; return true; } From 1f93736b1ec1d901b395847c30a27495d0db4f15 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 24 Aug 2026 18:33:42 -0600 Subject: [PATCH 073/222] Report the MCMC transition counts and the parallel likelihood contract --- Numerics/Sampling/MCMC/Base/MCMCSampler.cs | 97 +++++++- .../Sampling/MCMC/Test_MCMCTransitionCount.cs | 210 ++++++++++++++++++ docs/sampling/mcmc.md | 53 +++++ 3 files changed, 359 insertions(+), 1 deletion(-) create mode 100644 Test_Numerics/Sampling/MCMC/Test_MCMCTransitionCount.cs diff --git a/Numerics/Sampling/MCMC/Base/MCMCSampler.cs b/Numerics/Sampling/MCMC/Base/MCMCSampler.cs index c801aa71..f50e6acf 100644 --- a/Numerics/Sampling/MCMC/Base/MCMCSampler.cs +++ b/Numerics/Sampling/MCMC/Base/MCMCSampler.cs @@ -157,7 +157,81 @@ public int ThinningInterval } /// - /// The number of simulations that have been run with this instance of the sampler. + /// The number of chain transitions a single Markov chain performs during a call to . + /// + /// + /// ( + ceil( / )) + /// × . + /// + /// + /// + /// Why this is not . The configured iteration count does not describe + /// the work a run actually does, and it under-reports it substantially at the defaults. Two + /// multipliers sit between the two numbers. First, runs + /// ceil( / ) recorded iterations beyond + /// in order to collect the posterior output. Second, every recorded + /// iteration advances the chain times, because thinning is applied by + /// discarding intermediate transitions rather than by discarding recorded draws. Each of those + /// transitions costs at least one evaluation of , so this + /// property, not , is what a runtime estimate should be built on. + /// + /// + /// Warmup is already included. is a subset of + /// , not an addition to it — ValidateSettings rejects a warmup longer + /// than half of — so it must not be added again when reasoning about total + /// cost. + /// + /// + /// At the defaults ( = 3,500, = 10,000, + /// = 4, = 20) this is + /// (3,500 + 2,500) × 20 = 120,000 transitions per chain, against a user-facing + /// that reads 3,500 — a factor of roughly 34. + /// + /// + /// The type is because the product overflows well inside the + /// range of settings the sampler accepts: there is no upper bound on , so a + /// run of 10⁸ iterations at the default thinning already exceeds . + /// + /// + /// This reports the settings as they are currently configured and does not validate them; the + /// settings are checked by ValidateSettings when is called. + /// + /// + /// The count describes the base loop and the base SampleChain, neither of + /// which any chain sampler in this library overrides. It does not describe + /// , which replaces with a single non-Markovian importance + /// sampling pass and never advances a chain. + /// + /// + public long TransitionCount + { + get + { + // Derived from the same expressions Sample() uses, so the two cannot drift apart: + // Sample() computes outputIterations = ceil(OutputLength / NumberOfChains) and + // totalIterations = Iterations + outputIterations, then SampleChain advances the chain + // ThinningInterval times per iteration. The sum is widened to long before the + // multiplication so that large settings do not overflow. + int outputIterations = (int)Math.Ceiling(OutputLength / (double)NumberOfChains); + return ((long)Iterations + outputIterations) * ThinningInterval; + } + } + + /// + /// The number of chain transitions performed across all chains during a call to . + /// + /// × . + /// + /// The total evaluation budget of a run. At the defaults this is 120,000 × 4 = 480,000 transitions, + /// and therefore at least that many evaluations of . See + /// for why this differs so widely from . When + /// is true these are distributed across worker threads, so this is + /// the total work rather than the critical path. + /// + public long TotalTransitionCount => TransitionCount * NumberOfChains; + + /// + /// The number of simulations that have been run with this instance of the sampler. /// protected int _simulations = 0; @@ -199,6 +273,27 @@ public int ThinningInterval /// /// Determines if the chains should be sampled in parallel. Default = true. /// + /// + /// + /// Thread-safety requirement. When this is true — which is the default — + /// advances all chains inside a + /// , and every chain calls + /// the same delegate instance. The log-likelihood is + /// therefore invoked concurrently from multiple threads, as is the gradient delegate for + /// gradient-based samplers. That delegate must be thread-safe or stateless. A likelihood that closes + /// over mutable state — an automatic-differentiation tape, a reused workspace or buffer, a native + /// solver handle, a cached factorization, a non-thread-safe PRNG — races by default, and the + /// resulting corruption is silent: it surfaces as an implausible posterior rather than as an + /// exception. + /// + /// + /// If your likelihood is not thread-safe, set this to false. That is the supported answer, and + /// it is the only one. Per-chain resources cannot be selected from inside the callback, because + /// neither nor the gradient delegate receives a chain index — they take + /// only the parameter vector, so a callback has no way to learn which chain is calling it. Those + /// signatures are part of the public API and are not going to change to add one. + /// + /// public bool ParallelizeChains { get; set; } = true; /// diff --git a/Test_Numerics/Sampling/MCMC/Test_MCMCTransitionCount.cs b/Test_Numerics/Sampling/MCMC/Test_MCMCTransitionCount.cs new file mode 100644 index 00000000..3542bdd8 --- /dev/null +++ b/Test_Numerics/Sampling/MCMC/Test_MCMCTransitionCount.cs @@ -0,0 +1,210 @@ +using System.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Distributions; +using Numerics.Mathematics.LinearAlgebra; +using Numerics.Mathematics.Optimization; +using Numerics.Sampling.MCMC; + +namespace Sampling.MCMC +{ + /// + /// Unit tests for the derived work-estimate properties on . + /// + /// + /// + /// does not describe the work a run performs. Two multipliers sit + /// between the two figures: Sample() runs ceil(OutputLength / NumberOfChains) + /// recorded iterations beyond Iterations to collect the posterior output, and each recorded + /// iteration advances the chain ThinningInterval times. + /// and report the product. + /// + /// + /// The expected values below are computed by hand rather than from the properties' own expressions, and + /// ties the arithmetic to the + /// real Sample() loop so that the two cannot drift apart. + /// + /// + [TestClass] + public class Test_MCMCTransitionCount + { + /// + /// Builds a minimal, cheap RWMH sampler. The transition counts are pure functions of the settings, + /// so the priors and likelihood only have to be well formed. + /// + /// A two-parameter RWMH sampler left at its default settings. + private static RWMH CreateSampler() + { + var priors = new List { new Uniform(-10d, 10d), new Uniform(0.1d, 10d) }; + double logLH(double[] x) => new Normal(x[0], x[1]).LogPDF(0d); + return new RWMH(priors, logLH, Matrix.Identity(2)); + } + + /// + /// At the shipped defaults a single chain performs 120,000 transitions and the four chains together + /// perform 480,000, against an that reads 3,500. + /// + [TestMethod] + public void TransitionCount_AtTheDefaults() + { + var sampler = CreateSampler(); + + // Guard the defaults the hand-computed values below depend on. + Assert.AreEqual(3500, sampler.Iterations); + Assert.AreEqual(10000, sampler.OutputLength); + Assert.AreEqual(4, sampler.NumberOfChains); + Assert.AreEqual(20, sampler.ThinningInterval); + Assert.AreEqual(1750, sampler.WarmupIterations); + + // (3500 + ceil(10000 / 4)) * 20 = (3500 + 2500) * 20 = 120,000. + Assert.AreEqual(120000L, sampler.TransitionCount); + Assert.AreEqual(480000L, sampler.TotalTransitionCount); + + // Warmup is a subset of Iterations, not an addition to it, so it is already inside the figure. + Assert.IsLessThanOrEqualTo((int)(0.5 * sampler.Iterations), sampler.WarmupIterations); + } + + /// + /// A configuration in which OutputLength / NumberOfChains does not divide evenly, so the + /// ceiling in the output-iteration count is exercised. + /// + [TestMethod] + public void TransitionCount_WhenOutputDoesNotDivideEvenly() + { + var sampler = CreateSampler(); + sampler.Iterations = 1000; + sampler.OutputLength = 10001; + sampler.NumberOfChains = 3; + sampler.ThinningInterval = 7; + + // 10001 / 3 = 3333.667, so ceil gives 3334 — one more than truncation would. + // (1000 + 3334) * 7 = 4334 * 7 = 30,338. + Assert.AreEqual(30338L, sampler.TransitionCount); + Assert.AreEqual(91014L, sampler.TotalTransitionCount); + } + + /// + /// A second non-default configuration with a different uneven division and a different thinning + /// interval. + /// + [TestMethod] + public void TransitionCount_AtASecondNonDefaultConfiguration() + { + var sampler = CreateSampler(); + sampler.Iterations = 250; + sampler.OutputLength = 1000; + sampler.NumberOfChains = 7; + sampler.ThinningInterval = 3; + + // 1000 / 7 = 142.857, so ceil gives 143. + // (250 + 143) * 3 = 393 * 3 = 1179. + Assert.AreEqual(1179L, sampler.TransitionCount); + Assert.AreEqual(8253L, sampler.TotalTransitionCount); + + // A thinning interval of 1 removes that multiplier entirely, leaving the recorded iterations. + sampler.ThinningInterval = 1; + Assert.AreEqual(393L, sampler.TransitionCount); + Assert.AreEqual(2751L, sampler.TotalTransitionCount); + } + + /// + /// The counts are so that settings the sampler accepts cannot overflow them. + /// + /// + /// Nothing bounds from above, so the product leaves + /// range long before the settings become unreasonable. In + /// arithmetic the per-chain figure below would have wrapped to a negative number. + /// + [TestMethod] + public void TransitionCount_DoesNotOverflowForLargeSettings() + { + var sampler = CreateSampler(); + sampler.Iterations = 200000000; + sampler.OutputLength = 10000; + sampler.NumberOfChains = 4; + sampler.ThinningInterval = 20; + + // (200,000,000 + 2,500) * 20 = 4,000,050,000, which exceeds int.MaxValue (2,147,483,647). + Assert.AreEqual(4000050000L, sampler.TransitionCount); + Assert.AreEqual(16000200000L, sampler.TotalTransitionCount); + Assert.IsGreaterThan(int.MaxValue, sampler.TransitionCount); + } + + /// + /// Ties to the transitions Sample() actually + /// performs, so the property cannot drift away from the loop it describes. + /// + /// + /// SampleChain is invoked once per recorded iteration per chain and advances the chain + /// ThinningInterval times, so the observed call count times the thinning interval must equal + /// the advertised per-chain transition count. Chains are sampled serially here so the counters are + /// deterministic. The configuration is the smallest one ValidateSettings accepts, to keep the + /// run cheap. + /// + [TestMethod] + public void TransitionCount_MatchesTheTransitionsSampleActuallyPerforms() + { + var priors = new List { new Uniform(-5d, 5d), new Uniform(0.5d, 5d) }; + double logLH(double[] x) => new Normal(x[0], x[1]).LogPDF(0d); + var sampler = new CountingRWMH(priors, logLH, Matrix.Identity(2)) + { + Iterations = 100, + WarmupIterations = 10, + OutputLength = 100, + NumberOfChains = 2, + ThinningInterval = 3, + InitialIterations = 10, + ParallelizeChains = false, + PRNGSeed = 12345 + }; + + // ceil(100 / 2) = 50 output iterations, so 150 recorded iterations, each of 3 transitions. + Assert.AreEqual(450L, sampler.TransitionCount); + Assert.AreEqual(900L, sampler.TotalTransitionCount); + + sampler.Sample(); + + long observedPerChain = sampler.ChainCallCount[0] * sampler.ThinningInterval; + Assert.AreEqual(sampler.TransitionCount, observedPerChain, + "TransitionCount must equal the transitions Sample() performs on one chain."); + + long observedTotal = 0; + for (int i = 0; i < sampler.NumberOfChains; i++) + { + Assert.AreEqual(sampler.ChainCallCount[0], sampler.ChainCallCount[i], + "Every chain is advanced the same number of times."); + observedTotal += sampler.ChainCallCount[i] * sampler.ThinningInterval; + } + Assert.AreEqual(sampler.TotalTransitionCount, observedTotal, + "TotalTransitionCount must equal the transitions Sample() performs across all chains."); + } + + /// + /// An RWMH sampler that records how many times each chain is advanced. + /// + private sealed class CountingRWMH : RWMH + { + /// + /// Initializes a counting sampler. + /// + /// Parameter priors. + /// Log-likelihood function. + /// Proposal covariance. + public CountingRWMH(List priors, LogLikelihood target, Matrix proposalSigma) + : base(priors, target, proposalSigma) + { + } + + /// + /// The number of times each chain has been advanced, indexed by chain. + /// + public long[] ChainCallCount { get; } = new long[64]; + + /// + protected override ParameterSet SampleChain(int index, ParameterSet state) + { + ChainCallCount[index]++; + return base.SampleChain(index, state); + } + } + } +} diff --git a/docs/sampling/mcmc.md b/docs/sampling/mcmc.md index a639e4b7..c42d8d4b 100644 --- a/docs/sampling/mcmc.md +++ b/docs/sampling/mcmc.md @@ -91,6 +91,59 @@ List[] MarkovChains // Raw MCMC chains ParameterSet MAP // Maximum a posteriori estimate ``` +### How much work a run actually does + +`Iterations` does not describe the work performed, and at the defaults it under-reports it by a factor +of about 34. Two multipliers sit in between: + +- `Sample()` runs `ceil(OutputLength / NumberOfChains)` recorded iterations *beyond* `Iterations` to + collect the posterior output. `OutputLength` defaults to 10,000. +- Each recorded iteration advances the chain `ThinningInterval` times. Thinning discards intermediate + transitions, not recorded draws, so those transitions are all performed. + +```text +transitions per chain = (Iterations + ceil(OutputLength / NumberOfChains)) * ThinningInterval + = (3500 + ceil(10000 / 4)) * 20 + = (3500 + 2500) * 20 + = 120,000 per chain + = 480,000 across the 4 default chains +``` + +Every transition costs at least one log-likelihood evaluation, so 480,000 is the evaluation budget of +a default run. Note that `WarmupIterations` is a *subset* of `Iterations`, not an addition to it — +`ValidateSettings` rejects a warmup longer than half of `Iterations` — so it is already inside these +figures and must not be added again. + +Two read-only properties report these directly, so a runtime estimate does not have to reproduce the +arithmetic: + +```cs +long TransitionCount // transitions per chain (120,000 at the defaults) +long TotalTransitionCount // transitions over all chains (480,000 at the defaults) +``` + +They are `long` because there is no upper bound on `Iterations`, and the product overflows `int` for +settings the sampler otherwise accepts. + +### Thread safety of the likelihood + +`ParallelizeChains` defaults to `true`, which means `Sample()` advances all chains concurrently and +every chain calls the **same** `LogLikelihoodFunction` delegate instance. The log-likelihood — and, for +gradient-based samplers, the gradient — is therefore invoked from multiple threads at once and must be +thread-safe or stateless. + +A likelihood that closes over mutable state (an autodiff tape, a reused buffer or workspace, a native +solver handle, a cached factorization, a non-thread-safe PRNG) races under the default setting, and the +corruption is silent: it shows up as an implausible posterior, not as an exception. + +```cs +sampler.ParallelizeChains = false; // required if the likelihood is not thread-safe +``` + +Setting it to `false` is the supported remedy and the only one. The delegates receive just the +parameter vector and no chain index, so a callback cannot select a per-chain resource from inside +itself. + ## Defining the Model ### Step 1: Define Prior Distributions From 8ff91f185d14e51b66367f8996b921e44cb60c0f Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 24 Aug 2026 18:38:05 -0600 Subject: [PATCH 074/222] Expose the multivariate Student t lattice generator and pin the D>=3 CDF to scipy --- .../Multivariate/MultivariateStudentT.cs | 44 +++++- .../Multivariate/Test_MultivariateStudentT.cs | 130 ++++++++++++++++++ 2 files changed, 172 insertions(+), 2 deletions(-) diff --git a/Numerics/Distributions/Multivariate/MultivariateStudentT.cs b/Numerics/Distributions/Multivariate/MultivariateStudentT.cs index df61a289..67ea29b0 100644 --- a/Numerics/Distributions/Multivariate/MultivariateStudentT.cs +++ b/Numerics/Distributions/Multivariate/MultivariateStudentT.cs @@ -108,6 +108,42 @@ public MultivariateStudentT(double degreesOfFreedom, double[] location, double[, private double _lnconstant; private double[]? _variance; private double[]? _standardDeviation; + private Random _MVNUNI = new MersenneTwister(MultivariateNormal.DefaultMVNUNISeed); + + /// + /// The uniform(0,1) random number generator used by the inner multivariate normal CDF for dimensions + /// greater than two. + /// + /// + /// + /// Above two dimensions integrates the χ²(ν) mixing variable against + /// , which is the Genz MVNDST randomized lattice rule + /// and draws its lattice shifts from this generator. The multivariate t CDF therefore carries a small + /// stochastic error above two dimensions and only reproduces when the generator is seeded. At one and + /// two dimensions the CDF is a closed form and never touches this generator. + /// + /// + /// The default is a Mersenne twister seeded with , + /// which is what makes the default result reproducible — but it also means every instance left at the + /// default replays the identical lattice shifts, so the quadrature errors of separate instances are + /// correlated rather than independent and do not average out when many evaluations are aggregated. + /// Assign a seeded generator to tie results to a caller's own seed and to decorrelate the error + /// across instances. This mirrors so that the two classes + /// behave the same way. + /// + /// + /// MVNDST advances the generator, so successive CDF evaluations on the same instance consume + /// successive shifts and are not bit-identical to one another above two dimensions; two freshly + /// constructed instances with the same parameters and the same seed are. The generator is not + /// thread-safe, so an instance shared across threads must be cloned per thread. + /// + /// + /// Thrown when the assigned generator is null. + public Random MVNUNI + { + get { return _MVNUNI; } + set { _MVNUNI = value ?? throw new ArgumentNullException(nameof(MVNUNI)); } + } /// /// Gets the number of variables for the distribution. @@ -493,8 +529,11 @@ public override double CDF(double[] x) for (int i = 0; i < Dimension; i++) zVec[i] = x[i] - _location[i]; - // Create MVN with zero mean and the scale matrix Σ for CDF evaluation - var mvn = new MultivariateNormal(new double[Dimension], _scaleMatrix.ToArray()); + // Create MVN with zero mean and the scale matrix Σ for CDF evaluation. The caller's generator is + // handed to it so that the lattice shifts MVNDST draws come from this instance's MVNUNI rather + // than from a private default-seeded one; without this the property could be assigned and would + // have no effect on the result. + var mvn = new MultivariateNormal(new double[Dimension], _scaleMatrix.ToArray()) { MVNUNI = _MVNUNI }; double sum = 0.0; for (int k = 0; k < K; k++) @@ -719,6 +758,7 @@ public override MultivariateDistribution Clone() _scaleMatrix = this._scaleMatrix.Clone(), _cholesky = new CholeskyDecomposition(this._scaleMatrix.Clone()), _lnconstant = this._lnconstant, + _MVNUNI = this._MVNUNI, }; return clone; } diff --git a/Test_Numerics/Distributions/Multivariate/Test_MultivariateStudentT.cs b/Test_Numerics/Distributions/Multivariate/Test_MultivariateStudentT.cs index 3a96a283..ccbee038 100644 --- a/Test_Numerics/Distributions/Multivariate/Test_MultivariateStudentT.cs +++ b/Test_Numerics/Distributions/Multivariate/Test_MultivariateStudentT.cs @@ -297,6 +297,136 @@ public void Test_CDF_2D() $"CDF at (0,0) = {cdf_00:F4}, expected ≈ 0.0454"); } + /// + /// Verify the 3-D CDF against scipy.stats.multivariate_t.cdf. + /// + /// + /// + /// Above two dimensions the CDF takes a different route than the 1-D and 2-D closed forms: it is a + /// 200-stratum quadrature over the χ²(ν) mixing variable against the Genz MVNDST randomized-lattice + /// multivariate normal CDF. Neither nor touches + /// that path, so these are the first tests to cover it. + /// + /// + /// The oracle is scipy 1.17.1's multivariate_t(loc, shape, df).cdf(x), an independent + /// implementation. The correspondence with this class was confirmed on the existing 2-D fixture, + /// where scipy returns 0.3075081, 0.9166859 and 0.0453216 against the 0.3076, 0.9166 and 0.0454 that + /// asserts. scipy's cdf is itself randomised quasi-Monte-Carlo, so the + /// values below are means over 8 seeds, with a seed-to-seed spread of 3E-05 to 8E-05. + /// + /// + /// The tolerances are measured rather than guessed. This implementation agrees with the scipy means + /// to 1.21E-05, 1.30E-05, 2.05E-05 and 1.46E-05 at the four points, which is the same order as + /// scipy's own repeatability, so each tolerance below is that measurement rounded out. All four are + /// far inside the 1E-02 that the 2-D tests use. + /// + /// + [TestMethod] + public void Test_CDF_3D_Scipy() + { + double[,] shape = { { 1.0, 0.5, 0.3 }, { 0.5, 1.0, 0.4 }, { 0.3, 0.4, 1.0 } }; + var mvt = new MultivariateStudentT(5.0, new double[3], shape); + + // measured agreement 1.21E-05 + Assert.AreEqual(0.2236624425, mvt.CDF(new[] { 0.0, 0.0, 0.0 }), 5E-5); + // measured agreement 1.30E-05 + Assert.AreEqual(0.6307840101, mvt.CDF(new[] { 1.0, 1.0, 1.0 }), 5E-5); + // measured agreement 2.05E-05 + Assert.AreEqual(0.7480738713, mvt.CDF(new[] { 2.0, 1.5, 1.0 }), 5E-5); + // measured agreement 1.46E-05 + Assert.AreEqual(0.1566553047, mvt.CDF(new[] { -1.0, 0.5, 2.0 }), 5E-5); + } + + /// + /// Verify the 4-D CDF against scipy.stats.multivariate_t.cdf and, at the origin, against an + /// analytic value. + /// + /// + /// + /// The origin is not a Monte Carlo reference. For any elliptically symmetric distribution centred at + /// the origin the lower-orthant probability is exactly 1/2ᵈ by sign symmetry, so the expected value + /// at (0,0,0,0) is 0.0625 exactly. scipy reproduces it with zero spread across seeds, and this + /// implementation returns it exactly, so it is asserted tightly. + /// + /// + /// The two off-origin points are scipy means over 8 seeds, with spreads of 4.3E-05 and 5.8E-05. This + /// implementation agrees to 6.04E-06 and 4.78E-05 respectively; the tolerances below are those + /// measurements rounded out. + /// + /// + [TestMethod] + public void Test_CDF_4D_Scipy() + { + double[,] shape = + { + { 1.0, 0.0, 0.0, 0.0 }, + { 0.0, 1.0, 0.0, 0.0 }, + { 0.0, 0.0, 1.0, 0.0 }, + { 0.0, 0.0, 0.0, 1.0 } + }; + var mvt = new MultivariateStudentT(4.0, new double[4], shape); + + // Analytic: 1/2^4 by sign symmetry about the origin. Returned exactly. + Assert.AreEqual(0.0625, mvt.CDF(new[] { 0.0, 0.0, 0.0, 0.0 }), 1E-12, + "The lower-orthant probability at the centre of a symmetric 4-D distribution is exactly 1/16."); + + // measured agreement 6.04E-06 + Assert.AreEqual(0.4642714854, mvt.CDF(new[] { 1.0, 1.0, 1.0, 1.0 }), 5E-5); + // measured agreement 4.78E-05 + Assert.AreEqual(0.8086925605, mvt.CDF(new[] { 2.0, 2.0, 2.0, 2.0 }), 1E-4); + } + + /// + /// Verify that the D>=3 CDF is reproducible and that is + /// actually wired into it. + /// + /// + /// Above two dimensions the CDF draws lattice shifts from , + /// so the result is reproducible only because that generator is seeded by default. Two freshly + /// constructed instances therefore agree bit for bit, and so do two instances given generators with + /// the same non-default seed — but the non-default seed must produce a different answer, otherwise + /// the property would be inert and the escape hatch it advertises would not exist. The difference is + /// bounded to confirm it is quadrature noise on the same quantity and not a different quantity. + /// + [TestMethod] + public void Test_CDF_3D_MVNUNI_Reproducibility() + { + double[,] shape = { { 1.0, 0.5, 0.3 }, { 0.5, 1.0, 0.4 }, { 0.3, 0.4, 1.0 } }; + var point = new[] { 1.0, 1.0, 1.0 }; + + // Two fresh instances at the default seed agree exactly. + double defaultA = new MultivariateStudentT(5.0, new double[3], shape).CDF(point); + double defaultB = new MultivariateStudentT(5.0, new double[3], shape).CDF(point); + Assert.AreEqual(defaultA, defaultB, 0d, + "Two default-seeded instances must return bit-identical CDF values."); + + // Two instances given the same non-default seed also agree exactly. + var seededA = new MultivariateStudentT(5.0, new double[3], shape) { MVNUNI = new MersenneTwister(987654321) }; + var seededB = new MultivariateStudentT(5.0, new double[3], shape) { MVNUNI = new MersenneTwister(987654321) }; + double valueA = seededA.CDF(point); + double valueB = seededB.CDF(point); + Assert.AreEqual(valueA, valueB, 0d, + "Two instances seeded alike must return bit-identical CDF values."); + + // ...and they must differ from the default-seed result, or the property is not wired in. + Assert.AreNotEqual(defaultA, valueA, + "Assigning a different MVNUNI seed must change the D>=3 CDF, otherwise the property is inert."); + + // The difference is quadrature noise on the same probability, not a different probability. + Assert.AreEqual(defaultA, valueA, 1E-3, + "The seed only perturbs the lattice shifts, so the two results must agree to quadrature error."); + } + + /// + /// Verify that rejects a null generator. + /// + [TestMethod] + public void Test_MVNUNI_NullThrows() + { + var mvt = CreateStandard2D(); + Assert.Throws(() => mvt.MVNUNI = null!); + } + #endregion #region Sampling Tests From b54c8e897077eed66d9b31db8d8685ff016448ad Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 24 Aug 2026 18:39:47 -0600 Subject: [PATCH 075/222] Correct the central moment overload docs and record three known behaviors --- .../Interpolation/Support/Interpolater.cs | 30 +++++++++++- .../Data/Paired Data/OrderedPairedData.cs | 29 +++++++++++- .../Base/UnivariateDistributionBase.cs | 47 +++++++++++++++++-- .../Mathematics/Optimization/Local/BFGS.cs | 9 ++++ Numerics/Sampling/MCMC/SNIS.cs | 25 +++++++++- 5 files changed, 132 insertions(+), 8 deletions(-) diff --git a/Numerics/Data/Interpolation/Support/Interpolater.cs b/Numerics/Data/Interpolation/Support/Interpolater.cs index 9cb625bc..eb499cb9 100644 --- a/Numerics/Data/Interpolation/Support/Interpolater.cs +++ b/Numerics/Data/Interpolation/Support/Interpolater.cs @@ -42,7 +42,11 @@ public Interpolater(IList xValues, IList yValues, SortOrder sort if (sortOrder == SortOrder.Descending && xValues[i] > xValues[i - 1]) throw new ArgumentException(nameof(xValues), "The x values are not in descending order."); } this.XValues = xValues; - this.YValues = yValues; + this.YValues = yValues; + // This expression always evaluates to exactly 1 and never scales with the table size, despite + // its appearance. The constructor above requires Count >= 2, so Math.Pow(Count, 0.25) >= 1.189 + // and the (int) truncation is always at least 1, leaving Math.Min(1, >= 1) == 1. The intent was + // presumably Math.Max. See the remarks on deltaStart for why this is left as is. deltaStart = Math.Min(1, (int)Math.Pow((double)Count, 0.25)); SortOrder = sortOrder; @@ -59,8 +63,30 @@ public Interpolater(IList xValues, IList yValues, SortOrder sort public int SearchStart { get; set; } = 0; /// - /// Keeps track of the difference is start locations. + /// The maximum distance between consecutive search results for which those searches are still + /// treated as correlated, selecting the hunt search over bisection. /// + /// + /// + /// This is always exactly 1. The constructor assigns + /// Math.Min(1, (int)Math.Pow(Count, 0.25)), and because the constructor also requires + /// Count >= 2 the right-hand term is never below 1, so the minimum is always 1. The + /// expression reads as though the window grows with the table size, but it does not, at any + /// . + /// + /// + /// The consequence is confined to which search path runs and never to the value returned. + /// Hunt and bisection are required to return the same bracket for the same input, and + /// Test_Search cross-checks both against Search.Sequential. A window of 1 simply means + /// the hunt search is selected less often than a size-scaled window would select it, which is a + /// performance characteristic and not a correctness one. It is therefore left as is deliberately. + /// + /// + /// This field is , and and + /// are public and settable, so a consumer that wants different search + /// behaviour can override the heuristic rather than depend on this value. + /// + /// protected int deltaStart = 0; /// diff --git a/Numerics/Data/Paired Data/OrderedPairedData.cs b/Numerics/Data/Paired Data/OrderedPairedData.cs index 4de6b316..febc2eb3 100644 --- a/Numerics/Data/Paired Data/OrderedPairedData.cs +++ b/Numerics/Data/Paired Data/OrderedPairedData.cs @@ -45,13 +45,38 @@ public class OrderedPairedData : IList, INotifyCollectionChanged public bool UseSmartSearch { get; set; } = true; /// - /// Keeps track of the difference is start locations. + /// The maximum distance between consecutive x-search results for which those searches are still + /// treated as correlated, selecting the hunt search over bisection. /// + /// + /// + /// This is permanently 0. It is initialized to zero here and assigned nowhere else in the + /// class, so the correlated test in — + /// Math.Abs(start - XSearchStart) > XdeltaStart — reports correlated only when a search + /// lands on exactly the same index as the previous one. The equivalent field on + /// Interpolater is at least assigned, though it too is always 1. + /// + /// + /// The consequence is confined to which search path runs and never to the value returned. + /// Hunt and bisection return the same bracket for the same input, and Test_Search cross-checks + /// both against Search.Sequential. A window of 0 simply means bisection is chosen in almost + /// every case, which is a performance characteristic and not a correctness one. It is therefore left + /// as is deliberately. and are public and + /// settable, so a consumer can steer the search directly rather than depend on this heuristic. + /// + /// private int XdeltaStart = 0; /// - /// Keeps track of the difference is start locations. + /// The maximum distance between consecutive y-search results for which those searches are still + /// treated as correlated, selecting the hunt search over bisection. /// + /// + /// Permanently 0 for the same reason as : it is never assigned outside this + /// declaration, so reports correlated only on an exact index repeat. + /// The effect is confined to which search path runs, never to the bracket returned. See + /// for the full note. + /// private int YdeltaStart = 0; /// diff --git a/Numerics/Distributions/Univariate/Base/UnivariateDistributionBase.cs b/Numerics/Distributions/Univariate/Base/UnivariateDistributionBase.cs index 871e6992..7442a33c 100644 --- a/Numerics/Distributions/Univariate/Base/UnivariateDistributionBase.cs +++ b/Numerics/Distributions/Univariate/Base/UnivariateDistributionBase.cs @@ -282,10 +282,29 @@ public double[] InverseCDF(IList probabilities) } /// - /// Returns the central moments {Mean, Standard Deviation, Skew, and Kurtosis} of the distribution using numerical integration with Adaptive Simpson's rule. + /// Returns the central moments {Mean, Standard Deviation, Skew, and Kurtosis} of the distribution using adaptive numerical integration with the Gauss-Kronrod rule. /// - /// The desired tolerance for the solution. Default = ~Sqrt(Machine Epsilon), or 1E-8. + /// The desired relative tolerance for the solution. Default = ~Sqrt(Machine Epsilon), or 1E-8. /// Mean, Standard Deviation, Skew, and Kurtosis. + /// + /// + /// This overload takes a tolerance, not a step count. There is a sibling overload, + /// , whose argument is a number of integration steps. The two are + /// distinguished only by the type of the single argument, so the choice is invisible at the call + /// site: CentralMoments(1E-8) requests a relative tolerance of 1E-8 from this adaptive + /// routine, while CentralMoments(1000) selects the fixed-step trapezoidal overload with 1,000 + /// steps. Writing CentralMoments(1) when a tolerance of 1.0 was meant silently runs the other + /// method with a single step. The C++ port team flagged this pair as a foot-gun; the signatures are + /// kept for source compatibility. + /// + /// + /// This overload integrates with over the range + /// [InverseCDF(1E-16), InverseCDF(1 − 1E-16)], subdividing until the requested relative tolerance is + /// met. A moment whose integration fails is returned as rather than + /// throwing. Within this library only NoncentralT uses this overload; every other in-repo + /// caller uses the fixed-step one. + /// + /// public virtual double[] CentralMoments(double tolerance = 1E-8) { double u1 = 0, u2 = 0, u3 = 0, u4 = 0; @@ -315,9 +334,31 @@ public virtual double[] CentralMoments(double tolerance = 1E-8) } /// - /// Returns the central moments {Mean, Standard Deviation, Skew, and Kurtosis} of the distribution using numerical integration with Trapezoidal rule. + /// Returns the central moments {Mean, Standard Deviation, Skew, and Kurtosis} of the distribution using numerical integration with Trapezoidal rule. /// ///Number of integration steps. Default = 300. + /// Mean, Standard Deviation, Skew, and Kurtosis. + /// + /// + /// This overload takes a step count, not a tolerance. There is a sibling overload, + /// , whose argument is an integration tolerance. The two are + /// distinguished only by the type of the single argument, so the choice is invisible at the call + /// site: CentralMoments(1000) selects this fixed-step routine with 1,000 steps, while + /// CentralMoments(1E-8) selects the adaptive overload with a relative tolerance of 1E-8. + /// Writing CentralMoments(1) when a tolerance was meant silently runs this method with a + /// single step. The C++ port team flagged this pair as a foot-gun; the signatures are kept for + /// source compatibility. + /// + /// + /// This overload stratifies [InverseCDF(1E-8), InverseCDF(1 − 1E-8)] into + /// equal bins and accumulates a trapezoidal sum, so the cost and the accuracy are both fixed by + /// and there is no convergence check. Note that its integration range is + /// narrower than the adaptive overload's, so the two do not agree exactly even when both converge. + /// Within this library CompetingRisks, EmpiricalDistribution, + /// GeneralizedNormal, KappaFour, Mixture and TruncatedDistribution all + /// use this overload; only NoncentralT uses the adaptive one. + /// + /// public virtual double[] CentralMoments(int steps = 300) { double a = InverseCDF(1E-8); diff --git a/Numerics/Mathematics/Optimization/Local/BFGS.cs b/Numerics/Mathematics/Optimization/Local/BFGS.cs index c48d33fb..c5a73af8 100644 --- a/Numerics/Mathematics/Optimization/Local/BFGS.cs +++ b/Numerics/Mathematics/Optimization/Local/BFGS.cs @@ -98,6 +98,15 @@ protected override void Optimize() { int D = NumberOfParameters; double EPS = Tools.DoubleMachineEpsilon; + // TOLX is declared here to match Numerical Recipes' dfpmin, but it is never compared against + // anything in this method: the outer parameter-change convergence test of dfpmin — exit when the + // largest relative step falls below TOLX — is NOT implemented. The TOLX that is actually used is + // a separate local in LineSearchArmijo, where it sets alamin = TOLX / test; that is the inner + // lnsrch step-size floor and is unrelated to outer convergence. Convergence here is therefore + // decided solely by CheckConvergence's relative-function-change test, so there is no stagnation + // exit: a run that stops improving its parameters while the function value still moves will keep + // iterating to MaxIterations. Do not assume such an exit exists. Adding it is deferred to a + // separate task behind a measurement gate, since it would change which iterate is returned. double TOLX = 4 * EPS, STPMX = 100.0; bool cancel = false, check = false; diff --git a/Numerics/Sampling/MCMC/SNIS.cs b/Numerics/Sampling/MCMC/SNIS.cs index 714b9a73..e378c533 100644 --- a/Numerics/Sampling/MCMC/SNIS.cs +++ b/Numerics/Sampling/MCMC/SNIS.cs @@ -82,6 +82,25 @@ protected override void ValidateSettings() /// /// Perform importance sampling. /// + /// + /// + /// The resampling list is ordered by Fitness, not by Weight. After the + /// normalized posterior weights are computed, the sample list is sorted ascending on + /// ParameterSet.Fitness, and the resampling CDF is then accumulated from + /// ParameterSet.Weight in that order. The two fields do not always hold the same quantity. + /// With no importance distribution supplied the weight is set to the log-likelihood itself, so + /// Fitness and Weight coincide and the ordering is monotone in both. When an + /// importance distribution is supplied the weight becomes the log-likelihood minus the proposal + /// log-density, so the two diverge and the list is not sorted by the quantity the CDF accumulates. + /// + /// + /// Inverse-CDF resampling does not require a sorted CDF to be correct — the CDF is non-decreasing + /// regardless, because every increment is clamped non-negative — so this affects which sample each + /// plotting position selects, not the validity of the draw. Whether Fitness or Weight + /// is the intended sort key is an open question pending review, so the behaviour is deliberately + /// left unchanged; do not "fix" the comparator without that decision. + /// + /// public override void Sample() { InitializeChains(); @@ -168,7 +187,11 @@ public override void Sample() MarkovChains[0][idx] = new ParameterSet(MarkovChains[0][idx].Values, MarkovChains[0][idx].Fitness, w); }); - // Sort list in ascending order of posterior weights + // Sort the list in ascending order of Fitness (the log-likelihood), which is NOT the same key + // the CDF below accumulates: that runs on Weight. The two coincide only when no importance + // distribution was supplied, where weight = logLH; with one, weight = logLH - mvn.LogPDF, so + // the list is not ordered by the accumulated quantity. See the remarks on Sample(). Which of + // the two is the intended key is pending review, so this comparator is deliberately unchanged. MarkovChains[0].Sort((x, y) => x.Fitness.CompareTo(y.Fitness)); var cdf = new double[Iterations]; cdf[0] = Math.Max(0.0, MarkovChains[0][0].Weight); From e5a1da61f8814e131ecf7652e0fa6ffc5f37ba5c Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 24 Aug 2026 19:56:56 -0600 Subject: [PATCH 076/222] Store the adapted NUTS metric as the inverse mass and stop flooring it The windowed mass-matrix adaptation wrote the estimated posterior variance into the mass and its reciprocal into the inverse mass, which is the reverse of the convention the rest of the sampler uses: momentum is drawn with standard deviation sqrt(M), kinetic energy is 0.5 r' M^-1 r, and position flows as M^-1 r, so a coordinate of posterior standard deviation s needs M = 1 / s^2 to step in proportion to s. The regularization also blended (priorRange / 6)^2 into every coordinate unconditionally, so it acted as a variance floor tied to the prior width rather than as shrinkage. On a diffuse prior that floor exceeds the true posterior variances and flattens the metric to isotropy. The window variance is now used whenever it is usable and the prior-scaled fallback is engaged only when it is not: when the window holds fewer than ten draws, or when the variance is not finite and positive. Retained variances are floored at 1e-12 of the largest variance in the same window, which bounds the metric's condition number on the window's own scale rather than on the prior range. --- Numerics/Sampling/MCMC/NUTS.cs | 70 +++- .../Sampling/MCMC/Test_NUTS_MassMatrix.cs | 375 ++++++++++++++++++ 2 files changed, 438 insertions(+), 7 deletions(-) create mode 100644 Test_Numerics/Sampling/MCMC/Test_NUTS_MassMatrix.cs diff --git a/Numerics/Sampling/MCMC/NUTS.cs b/Numerics/Sampling/MCMC/NUTS.cs index 1f462c34..88b2ae55 100644 --- a/Numerics/Sampling/MCMC/NUTS.cs +++ b/Numerics/Sampling/MCMC/NUTS.cs @@ -776,6 +776,18 @@ private void AccumulateWelfordStatistics(int chainIndex, double[] sample) } } + /// + /// The smallest number of draws an adaptation window must contain before its Welford variance + /// is trusted. Shorter windows use the prior-scaled fallback variance instead. + /// + private const int MIN_ADAPT_WINDOW_COUNT = 10; + + /// + /// The smallest per-coordinate variance retained in an adapted metric, as a fraction of the + /// largest variance in the same window. This caps the diagonal metric's condition number at 1e12. + /// + private const double RELATIVE_VARIANCE_FLOOR = 1e-12; + /// /// Updates the diagonal mass matrix from the accumulated Welford statistics at the end /// of an adaptation window. Resets Welford accumulators and dual averaging state so the @@ -783,26 +795,70 @@ private void AccumulateWelfordStatistics(int chainIndex, double[] sample) /// /// The chain index. /// The current parameter state, used to find a new reasonable step size after the metric change. + /// + /// + /// Metric convention. The estimated posterior variance is the inverse mass, not the mass. + /// This class draws momentum with standard deviation sqrt(M), evaluates kinetic energy as + /// 0.5 * r' * M^-1 * r, and flows position as q += M^-1 * r * epsilon. A coordinate with + /// posterior standard deviation s therefore needs M = 1 / s^2 for its leapfrog step to + /// scale like s. The window variance is stored in and its + /// reciprocal in , which is the same correspondence Stan uses when it keeps + /// the estimated variance in inv_e_metric. + /// + /// + /// Regularization. Stan shrinks the window variance toward a small absolute constant because it + /// works in an unconstrained space where the variance is O(1). This sampler works on the natural scale, + /// where no absolute constant is meaningful, so the window variance is used directly whenever it is + /// usable and the prior-scaled fallback (priorRange / 6)^2 is engaged only when it is not. + /// Blending the fallback in unconditionally would impose a floor tied to the prior width rather than to + /// the posterior, which on a diffuse prior flattens the metric to isotropy and erases the adaptation. + /// + /// + /// Guards. Two conditions decide that a window cannot produce a usable variance. First, the window + /// must contain at least draws: the relative standard error of a + /// sample variance is approximately sqrt(2 / (n - 1)), which is still about 47% at ten draws and + /// worse below that, so a shorter window is noise rather than an estimate. Second, the variance must be + /// finite and strictly positive. Windows failing either condition take the fallback. A coordinate that + /// barely moved can still return a positive but degenerate variance, so every retained variance is then + /// floored at times the largest variance in the same window. That + /// floor is expressed on the window's own scale rather than on the prior range, so it is invariant to a + /// global rescaling of the target, and it caps the condition number of the diagonal metric at 1e12, + /// well inside double precision. + /// + /// private void UpdateMassMatrix(int chainIndex, ParameterSet currentState) { int n = _welfordCount[chainIndex]; if (n < 2) return; + // Estimate the posterior variance of every coordinate from this window, falling back to the + // prior-scaled variance only for coordinates whose window estimate is unusable. + var estimatedVariance = new double[NumberOfParameters]; + double largestVariance = 0d; for (int j = 0; j < NumberOfParameters; j++) { double variance = _welfordM2[chainIndex][j] / (n - 1); - // Stan regularization: (n/(n+5)) * var + 1e-3 * (5/(n+5)) - // Stan operates in unconstrained space where variance ~ O(1), so 1e-3 is fine. - // We operate in natural scale, so use a scale-aware fallback instead. + // Fallback: (prior_range / 6)^2 as a conservative variance estimate. double priorRange = _upperBounds[j] - _lowerBounds[j]; double fallbackVariance = (priorRange * priorRange) / 36.0; if (!Tools.IsFinite(fallbackVariance) || fallbackVariance <= 0) fallbackVariance = 1.0; - double shrinkage = 5.0; - double regularized = (n / (n + shrinkage)) * variance + (shrinkage / (n + shrinkage)) * fallbackVariance; - _massMatrix[chainIndex][j] = regularized; - _inverseMassMatrix[chainIndex][j] = 1.0 / regularized; + + bool windowIsUsable = n >= MIN_ADAPT_WINDOW_COUNT && Tools.IsFinite(variance) && variance > 0; + estimatedVariance[j] = windowIsUsable ? variance : fallbackVariance; + + if (estimatedVariance[j] > largestVariance) + largestVariance = estimatedVariance[j]; + } + + // Bound the metric's condition number against the window's own scale. + double varianceFloor = largestVariance * RELATIVE_VARIANCE_FLOOR; + for (int j = 0; j < NumberOfParameters; j++) + { + double inverseMass = Math.Max(estimatedVariance[j], varianceFloor); + _inverseMassMatrix[chainIndex][j] = inverseMass; + _massMatrix[chainIndex][j] = 1.0 / inverseMass; } // Reset Welford accumulators for next window diff --git a/Test_Numerics/Sampling/MCMC/Test_NUTS_MassMatrix.cs b/Test_Numerics/Sampling/MCMC/Test_NUTS_MassMatrix.cs new file mode 100644 index 00000000..66348a5e --- /dev/null +++ b/Test_Numerics/Sampling/MCMC/Test_NUTS_MassMatrix.cs @@ -0,0 +1,375 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Distributions; +using Numerics.Mathematics.LinearAlgebra; +using Numerics.Sampling.MCMC; + +namespace Sampling.MCMC +{ + /// + /// Unit tests for the NUTS diagonal mass-matrix adaptation. + /// + /// + /// + /// Authors: + /// + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + /// + /// Description: + /// + /// + /// The oracle for these tests is analytic. The target is a zero-mean diagonal Gaussian whose + /// per-coordinate standard deviations are chosen in advance, so the exact posterior standard + /// deviation and variance of every coordinate are known in closed form and no reference from + /// another library is involved. The adapted metric is read back through reflection because the + /// sampler does not expose it; that is deliberate, so the assertions do not require widening the + /// public surface of . + /// + /// + /// The scale range is four orders of magnitude in standard deviation, eight in variance. An + /// identity metric cannot cover that range with one step size, so a working adaptation is + /// visible both in the recovered spread and in the per-transition leapfrog cost. + /// + /// + [TestClass] + public class Test_NUTS_MassMatrix + { + /// + /// Zero-mean diagonal Gaussian with log-spaced per-coordinate standard deviations. + /// + private sealed class DiagonalGaussian + { + /// The exact posterior standard deviation of each coordinate. + public readonly double[] StandardDeviations; + + /// + /// Creates the target. + /// + /// The number of coordinates. + /// The standard deviation of the first coordinate. + /// The standard deviation of the last coordinate. + public DiagonalGaussian(int dimension, double smallestSd, double largestSd) + { + StandardDeviations = new double[dimension]; + double lo = Math.Log10(smallestSd); + double hi = Math.Log10(largestSd); + for (int j = 0; j < dimension; j++) + StandardDeviations[j] = Math.Pow(10.0, lo + (hi - lo) * j / (dimension - 1.0)); + } + + /// The log-likelihood, up to an additive constant. + /// The parameter vector. + /// The log-density. + public double LogLikelihood(double[] x) + { + double sum = 0d; + for (int j = 0; j < StandardDeviations.Length; j++) + { + double z = x[j] / StandardDeviations[j]; + sum += -0.5 * z * z; + } + return sum; + } + + /// The exact gradient of the log-likelihood. + /// The parameter vector. + /// The gradient. + public Vector Gradient(IList x) + { + var g = new Vector(StandardDeviations.Length); + for (int j = 0; j < StandardDeviations.Length; j++) + g[j] = -x[j] / (StandardDeviations[j] * StandardDeviations[j]); + return g; + } + } + + /// + /// Builds a single-chain NUTS sampler on the diagonal Gaussian with a deterministic start. + /// + /// The target. + /// The half-width of the uniform prior on every coordinate. + /// Whether to adapt the mass matrix. + /// The number of warmup iterations. + /// The number of iterations. + /// The maximum tree depth. + /// An unsampled sampler. + private static NUTS BuildSampler(DiagonalGaussian target, double halfWidth, bool adapt, + int warmup, int iterations, int maxTreeDepth) + { + var priors = new List(); + for (int j = 0; j < target.StandardDeviations.Length; j++) + priors.Add(new Uniform(-halfWidth, halfWidth)); + + return new NUTS(priors, target.LogLikelihood, maxTreeDepth: maxTreeDepth, + gradientFunction: target.Gradient) + { + NumberOfChains = 1, + ParallelizeChains = false, + // With one chain and one initial iteration the chain starts at the prior mean, + // which is the mode of this target, so the run is fully deterministic. + InitialIterations = 1, + ThinningInterval = 1, + WarmupIterations = warmup, + Iterations = iterations, + OutputLength = 2000, + PRNGSeed = 12345, + AdaptMassMatrix = adapt + }; + } + + /// + /// Reads one of the sampler's private diagonal metric arrays for the given chain. + /// + /// The sampler. + /// The private field name. + /// The metric diagonal of chain zero. + private static double[] ReadMetric(NUTS sampler, string fieldName) + { + var field = typeof(NUTS).GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance); + Assert.IsNotNull(field, "The private field " + fieldName + " was not found on NUTS."); + var metric = field!.GetValue(sampler) as double[][]; + Assert.IsNotNull(metric, "The private field " + fieldName + " was not a double[][]."); + return metric![0]; + } + + /// + /// Computes the sample standard deviation of one coordinate of the recorded output. + /// + /// A sampled sampler. + /// The coordinate index. + /// The sample standard deviation. + private static double RecoveredStandardDeviation(NUTS sampler, int coordinate) + { + var draws = sampler.Output[0]; + double mean = 0d; + for (int i = 0; i < draws.Count; i++) mean += draws[i].Values[coordinate]; + mean /= draws.Count; + double m2 = 0d; + for (int i = 0; i < draws.Count; i++) + { + double e = draws[i].Values[coordinate] - mean; + m2 += e * e; + } + return Math.Sqrt(m2 / (draws.Count - 1)); + } + + /// + /// With mass-matrix adaptation enabled, the sampler must recover the analytically known + /// posterior standard deviation of every coordinate across four orders of magnitude. + /// + /// + /// The stated band is recovered sd / true sd in [0.75, 1.30] on all twenty coordinates. + /// It is wide enough to absorb single-chain Monte Carlo error on 2,000 draws and any + /// cross-framework divergence of this chaotic trajectory, and narrow enough that the + /// pre-fix sampler fails it: with the additive prior-scaled floor in place the widest + /// coordinate returns about 0.33 to 0.37 of its true width. + /// + [TestMethod] + public void Test_NUTS_AdaptedMetric_RecoversKnownScales() + { + var target = new DiagonalGaussian(20, 1e-3, 1e1); + var sampler = BuildSampler(target, 1000d, adapt: true, warmup: 600, iterations: 1500, maxTreeDepth: 10); + sampler.Sample(); + + for (int j = 0; j < target.StandardDeviations.Length; j++) + { + double ratio = RecoveredStandardDeviation(sampler, j) / target.StandardDeviations[j]; + Assert.IsTrue(ratio >= 0.75 && ratio <= 1.30, + $"Coordinate {j} (true sd {target.StandardDeviations[j]:E3}) recovered at {ratio:F4} of its true width; expected [0.75, 1.30]."); + } + + Assert.AreEqual(0, sampler.DivergenceCounts[0], "The adapted run must not diverge on a Gaussian target."); + } + + /// + /// The adapted metric must track the true per-coordinate variances rather than collapsing + /// toward isotropy, and it must do so in the sense the sampler consumes it. + /// + /// + /// + /// The estimated posterior variance belongs in the inverse mass: momentum is drawn with + /// standard deviation sqrt(M), so a coordinate of posterior standard deviation s must carry + /// M = 1 / s^2 for its leapfrog step to scale like s. This test asserts that directly, within + /// a factor of four on the variance, which is a factor of two on the standard deviation. + /// + /// + /// It fails on all three of the broken arrangements this fix replaces: an additive + /// prior-scaled floor drives the whole diagonal to one value, storing the variance in the + /// mass instead of the inverse mass reverses the ordering, and doing both leaves a metric + /// anti-correlated with the truth. + /// + /// + [TestMethod] + public void Test_NUTS_AdaptedMetric_TracksTrueVariances() + { + var target = new DiagonalGaussian(20, 1e-3, 1e1); + var sampler = BuildSampler(target, 1000d, adapt: true, warmup: 600, iterations: 1500, maxTreeDepth: 10); + sampler.Sample(); + + var inverseMass = ReadMetric(sampler, "_inverseMassMatrix"); + var mass = ReadMetric(sampler, "_massMatrix"); + + double smallest = double.MaxValue, largest = 0d; + for (int j = 0; j < target.StandardDeviations.Length; j++) + { + double trueVariance = target.StandardDeviations[j] * target.StandardDeviations[j]; + Assert.IsTrue(inverseMass[j] >= 0.25 * trueVariance && inverseMass[j] <= 4.0 * trueVariance, + $"Coordinate {j}: inverse mass {inverseMass[j]:E4} is not within a factor of four of the true variance {trueVariance:E4}."); + Assert.AreEqual(1.0 / inverseMass[j], mass[j], 1e-12 * Math.Abs(1.0 / inverseMass[j]), + $"Coordinate {j}: the mass and inverse mass are not reciprocals."); + + if (inverseMass[j] < smallest) smallest = inverseMass[j]; + if (inverseMass[j] > largest) largest = inverseMass[j]; + } + + // The true variances span 1e-8 of their own range. A metric flattened toward isotropy + // by an additive floor spans less than 1.02. + Assert.IsGreaterThan(1e4, largest / smallest, + $"The adapted metric spans only a factor of {largest / smallest:E3}; it has collapsed toward isotropy."); + } + + /// + /// Adaptation must make the ill-conditioned target dramatically cheaper per transition than + /// the identity metric does. + /// + [TestMethod] + public void Test_NUTS_AdaptedMetric_ReducesLeapfrogCost() + { + var target = new DiagonalGaussian(20, 1e-3, 1e1); + + var adapted = BuildSampler(target, 1000d, adapt: true, warmup: 600, iterations: 1500, maxTreeDepth: 10); + adapted.Sample(); + + // With an identity metric this target saturates the tree-depth cap on essentially every + // transition, which is 2^10 - 1 = 1023 leapfrog steps. + Assert.IsLessThan(100d, adapted.MeanLeapfrogSteps[0], + $"The adapted sampler used {adapted.MeanLeapfrogSteps[0]:F2} leapfrog steps per transition; an identity metric costs on the order of 1,000 here."); + Assert.IsLessThan(adapted.DiagnosticSampleCounts[0], adapted.MaxTreeDepthHitCounts[0] * 100, + $"The adapted sampler exhausted the tree-depth cap on {adapted.MaxTreeDepthHitCounts[0]} of {adapted.DiagnosticSampleCounts[0]} transitions."); + } + + /// + /// A window holding fewer than the minimum number of draws cannot estimate a variance, so + /// the prior-scaled fallback must be used for every coordinate. + /// + /// + /// With 12 warmup iterations the initial and terminal buffers are one iteration each and the + /// single adaptation window closes at iteration 10, having accumulated nine draws. That is + /// below the ten-draw minimum, so the metric must be exactly the fallback variance + /// (priorRange / 6)^2 on every coordinate. + /// + [TestMethod] + public void Test_NUTS_ShortAdaptationWindow_UsesFallbackVariance() + { + var target = new DiagonalGaussian(4, 1e-2, 1e0); + var sampler = BuildSampler(target, 1000d, adapt: true, warmup: 12, iterations: 100, maxTreeDepth: 6); + sampler.Sample(); + + double priorRange = 2000d; + double expected = (priorRange * priorRange) / 36.0; + var inverseMass = ReadMetric(sampler, "_inverseMassMatrix"); + for (int j = 0; j < inverseMass.Length; j++) + { + Assert.AreEqual(expected, inverseMass[j], 1e-9 * expected, + $"Coordinate {j} did not fall back to the prior-scaled variance on a nine-draw window."); + } + } + + /// + /// A window holding at least the minimum number of draws must use its own variance rather + /// than the prior-scaled fallback. + /// + /// + /// With 14 warmup iterations the single adaptation window closes at iteration 12 with ten + /// accumulated draws, which is exactly the minimum. The posterior of this target is many + /// orders of magnitude narrower than the prior, so any use of the window's own variance is + /// unmistakable. + /// + [TestMethod] + public void Test_NUTS_SufficientAdaptationWindow_UsesWindowVariance() + { + var target = new DiagonalGaussian(4, 1e-2, 1e0); + var sampler = BuildSampler(target, 1000d, adapt: true, warmup: 14, iterations: 100, maxTreeDepth: 6); + sampler.Sample(); + + double fallback = (2000d * 2000d) / 36.0; + var inverseMass = ReadMetric(sampler, "_inverseMassMatrix"); + for (int j = 0; j < inverseMass.Length; j++) + { + Assert.IsTrue(inverseMass[j] > 0d && inverseMass[j] < 1d, + $"Coordinate {j} returned {inverseMass[j]:E4}; a ten-draw window on this target cannot produce a variance of order the prior's {fallback:E4}."); + } + } + + /// + /// With disabled the sampler must reproduce the exact + /// draws it produced before the mass-matrix adaptation was corrected. + /// + /// + /// The reference values were captured from the unmodified sampler and are asserted to a + /// relative tolerance of 1e-12, which is far tighter than any behavioural change could be + /// and loose enough to absorb the last-place differences the transcendental functions show + /// between .NET Framework and .NET. The disabled path never enters the adaptation code, so + /// this is a guard that the change stayed inside it. + /// + [TestMethod] + public void Test_NUTS_AdaptationDisabled_ReproducesCapturedDraws() + { + var target = new DiagonalGaussian(6, 1e-2, 1e1); + var priors = new List(); + for (int j = 0; j < 6; j++) priors.Add(new Uniform(-1000d, 1000d)); + var sampler = new NUTS(priors, target.LogLikelihood, maxTreeDepth: 6, gradientFunction: target.Gradient) + { + NumberOfChains = 1, + ParallelizeChains = false, + InitialIterations = 1, + ThinningInterval = 1, + WarmupIterations = 60, + Iterations = 150, + OutputLength = 100, + PRNGSeed = 12345, + AdaptMassMatrix = false + }; + sampler.Sample(); + + AssertClose(0.010797384971651152, sampler.StepSizes[0], "step size"); + AssertClose(54.85263157894737, sampler.MeanLeapfrogSteps[0], "mean leapfrog steps"); + + double[] firstDraw = { 0.0011958505341814842, -0.07526409822522193, -0.21184584905057402, + -0.6582920358936465, 2.1468119667523973, -0.6069130233917713 }; + double[] lastDraw = { 0.004230279378397197, -0.009370317476825615, -0.15308785669000735, + -0.726029878587165, 3.652148817280656, -4.4283508900986215 }; + var last = sampler.Output[0][sampler.Output[0].Count - 1]; + for (int j = 0; j < 6; j++) + { + AssertClose(firstDraw[j], sampler.Output[0][0].Values[j], $"first draw coordinate {j}"); + AssertClose(lastDraw[j], last.Values[j], $"last draw coordinate {j}"); + } + + // The metric must still be the identity that the constructor installed. + var inverseMass = ReadMetric(sampler, "_inverseMassMatrix"); + var mass = ReadMetric(sampler, "_massMatrix"); + for (int j = 0; j < 6; j++) + { + Assert.AreEqual(1d, inverseMass[j], 0d, $"Coordinate {j} inverse mass was modified with adaptation disabled."); + Assert.AreEqual(1d, mass[j], 0d, $"Coordinate {j} mass was modified with adaptation disabled."); + } + } + + /// + /// Asserts a value matches a captured reference to a relative tolerance of 1e-12. + /// + /// The captured reference value. + /// The value produced by this run. + /// A description used in the failure message. + private static void AssertClose(double expected, double actual, string what) + { + Assert.AreEqual(expected, actual, 1e-12 * Math.Abs(expected), + $"The disabled-adaptation path changed: {what} was {actual:R}, expected {expected:R}."); + } + } +} From 5390272011a443302819e0ec8055a7636a38cc95 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 24 Aug 2026 20:07:07 -0600 Subject: [PATCH 077/222] Adapt the NUTS diagonal mass matrix by default An identity metric forces one step size to serve every parameter, so on a posterior whose parameters differ in scale NUTS saturates MaxTreeDepth on nearly every transition. On a 50-parameter Gaussian with standard deviations spanning 1e-3 to 1e1 the adapted metric costs 7.0 leapfrog steps per transition against 4,091 without it, measured over six seeds with a spread of 0.37, and removes every maximum-tree-depth hit. On the two-parameter distribution fits the library is usually applied to, the metric has little to correct and adaptation costs up to 38% more leapfrog steps per transition. Against that, the three rstan comparison tests move closer to their reference on every statistic and the divergence count across them falls from 444 to 2. Set AdaptMassMatrix to false to sample with the fixed metric supplied through Mass, which reproduces the previous behavior exactly. --- Numerics/Sampling/MCMC/NUTS.cs | 29 ++++++++++++++++++++++++----- docs/sampling/mcmc.md | 6 +++++- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/Numerics/Sampling/MCMC/NUTS.cs b/Numerics/Sampling/MCMC/NUTS.cs index 88b2ae55..da878220 100644 --- a/Numerics/Sampling/MCMC/NUTS.cs +++ b/Numerics/Sampling/MCMC/NUTS.cs @@ -295,12 +295,31 @@ private double[] ComputeDiagnosticMeans(double[] sums) } /// - /// Gets or sets whether to adapt the diagonal mass matrix during warmup. - /// When enabled, uses Stan-style windowed adaptation with Welford's online algorithm - /// to estimate the posterior variance per parameter and precondition the Hamiltonian dynamics. - /// Default = false. + /// Gets or sets whether to adapt the diagonal mass matrix during warmup. Default = true. /// - public bool AdaptMassMatrix { get; set; } = false; + /// + /// + /// When enabled, the sampler uses Stan-style windowed adaptation with Welford's online algorithm + /// to estimate the posterior variance of each parameter during warmup, and takes that variance as + /// the coordinate's inverse mass. Each leapfrog step then scales with the coordinate's own posterior + /// width, which is what lets one step size serve a posterior whose parameters differ in scale. + /// + /// + /// The default is because an identity metric forces the step size to track the + /// narrowest direction while the trajectory has to span the widest, so on an ill-conditioned posterior + /// NUTS saturates on nearly every transition. On a 50-parameter Gaussian + /// whose standard deviations span 1e-3 to 1e1, adaptation reduces the cost from about + /// 4,090 leapfrog steps per transition to 7 and removes every maximum-tree-depth hit. On small, + /// well-conditioned fits the metric has little to correct and the adaptation costs up to about 40% + /// more leapfrog steps per transition, which is the price of the general case. + /// + /// + /// Set this to to sample with the fixed metric supplied through + /// . Doing so reproduces the sampler's behaviour exactly as it was before this + /// property defaulted to . + /// + /// + public bool AdaptMassMatrix { get; set; } = true; /// protected override void ValidateCustomSettings() diff --git a/docs/sampling/mcmc.md b/docs/sampling/mcmc.md index c42d8d4b..305132c7 100644 --- a/docs/sampling/mcmc.md +++ b/docs/sampling/mcmc.md @@ -569,18 +569,22 @@ After warmup, the step size is fixed to $\exp(\log \bar{\varepsilon})$. - Divergence threshold: if $H - H_0 > 1000$, the trajectory is considered divergent and tree-building stops - NUTS always accepts a candidate from the tree (acceptance is built into the multinomial weighting), so `AcceptCount` increments every iteration - Step size adaptation occurs only during the warmup phase, with step sizes clamped to $[10^{-10}, \, 10^{5}]$ +- `AdaptMassMatrix` defaults to `true`. During warmup the diagonal metric is estimated with Welford's online algorithm over Stan-style doubling windows, and each parameter's estimated posterior variance becomes its inverse mass, so the leapfrog step in that direction scales with the parameter's own posterior width. Without it a single step size has to serve every parameter at once, and on a posterior whose parameters differ in scale the sampler saturates `MaxTreeDepth` on nearly every transition. Set it to `false` to sample with the fixed metric supplied through `Mass` ```cs var nuts = new NUTS(priors, logLikelihood); // NUTS-specific settings nuts.NumberOfChains = 4; -nuts.WarmupIterations = 1000; // Step size adapts during warmup +nuts.WarmupIterations = 1000; // Step size and diagonal metric adapt during warmup nuts.Iterations = 2000; // Optional: set step size and max tree depth // nuts = new NUTS(priors, logLikelihood, stepSize: 0.5, maxTreeDepth: 10); +// Optional: sample with the fixed identity metric instead of the adapted diagonal one +// nuts.AdaptMassMatrix = false; + Console.WriteLine("Running No-U-Turn Sampler..."); nuts.Sample(); From 9bdf6290ca7d01cc23e37f471e427c61b344692b Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 24 Aug 2026 20:22:33 -0600 Subject: [PATCH 078/222] Document the multivariate Student t CDF thread-safety loss and correct the review findings --- .../Multivariate/MultivariateNormal.cs | 8 ++- .../Multivariate/MultivariateStudentT.cs | 46 ++++++++++++++- .../Base/UnivariateDistributionBase.cs | 23 ++++++-- Numerics/Sampling/MCMC/Base/MCMCSampler.cs | 53 ++++++++++++----- .../Data/Statistics/Test_Statistics.cs | 17 ++++-- .../Multivariate/Test_MultivariateStudentT.cs | 25 +++++--- .../Sampling/MCMC/Test_MCMCTransitionCount.cs | 59 ++++++++++++++++--- docs/sampling/mcmc.md | 6 +- 8 files changed, 189 insertions(+), 48 deletions(-) diff --git a/Numerics/Distributions/Multivariate/MultivariateNormal.cs b/Numerics/Distributions/Multivariate/MultivariateNormal.cs index 303c3a83..22319091 100644 --- a/Numerics/Distributions/Multivariate/MultivariateNormal.cs +++ b/Numerics/Distributions/Multivariate/MultivariateNormal.cs @@ -158,9 +158,11 @@ public MultivariateNormal(double[] mean, double[,] covariance, DecompositionMeth /// /// The literal above is written out rather than derived as 2d * Tools.DoubleMachineEpsilon, /// and that matters. is the decimal literal - /// 1.11022302462516E-16, which is a truncated 2⁻⁵³ and not 2⁻⁵³ itself: the exact value is - /// 1.1102230246251565E-16, so the ratio between the two constants is 1.9999999999999938 rather than - /// 2. Doubling the truncated constant would therefore not yield 2⁻⁵² bit-exactly, and the + /// 1.11022302462516E-16, which is 2⁻⁵³ rounded to fifteen significant figures and not 2⁻⁵³ itself. + /// 2⁻⁵³ is exactly 1.1102230246251565E-16, so the literal is the larger of the two, at + /// 1.000000000000003 times 2⁻⁵³, and the ratio between the two class constants is + /// 1.9999999999999938 rather than 2. Doubling the rounded constant would therefore + /// not yield 2⁻⁵² bit-exactly, and the /// thresholds built on it would drift off scipy's by a few ulps — enough to break an exact-agreement /// test without producing any visible symptom. Do not "simplify" this back to a multiple of /// . diff --git a/Numerics/Distributions/Multivariate/MultivariateStudentT.cs b/Numerics/Distributions/Multivariate/MultivariateStudentT.cs index 67ea29b0..744ece4d 100644 --- a/Numerics/Distributions/Multivariate/MultivariateStudentT.cs +++ b/Numerics/Distributions/Multivariate/MultivariateStudentT.cs @@ -134,8 +134,17 @@ public MultivariateStudentT(double degreesOfFreedom, double[] location, double[, /// /// MVNDST advances the generator, so successive CDF evaluations on the same instance consume /// successive shifts and are not bit-identical to one another above two dimensions; two freshly - /// constructed instances with the same parameters and the same seed are. The generator is not - /// thread-safe, so an instance shared across threads must be cloned per thread. + /// constructed instances with the same parameters and the same seed are. + /// + /// + /// Not thread-safe. has no internal synchronization, so above + /// two dimensions a single instance must not have called concurrently + /// from several threads. Give each thread its own instance, or assign each thread's instance a + /// generator of its own (mvt.MVNUNI = new MersenneTwister(seedForThisThread)). Note that + /// is not a remedy: it copies this reference, so a clone shares one + /// generator with the original and races exactly as the original would. It cannot do otherwise, + /// because the property is typed and an arbitrary has no + /// general deep copy. /// /// /// Thrown when the assigned generator is null. @@ -497,6 +506,30 @@ public double Mahalanobis(double[] x) /// MVN CDF at K=200 stratified quantiles of the χ²(ν) distribution and averaging. /// /// + /// Above two dimensions this method is stochastic, stateful, and not thread-safe. The inner + /// MVNDST is a randomized lattice rule that draws its shifts from , which is + /// instance state, so three things follow that do not apply at one or two dimensions (where the CDF + /// is a closed form and touches no instance state): + /// + /// + /// First, the result carries a small quadrature error, of the order of the + /// 1E-4 absolute tolerance MVNDST is given, rather than being exact to roundoff. + /// Second, each call advances the generator, so repeated calls at the same point on the same + /// instance return slightly different values; it is two freshly constructed instances with + /// the same parameters and seed that agree bit for bit, not two calls on one instance. + /// Third, and most easily overlooked: because the generator is shared instance state and + /// has no internal synchronization, calling this method + /// concurrently on a single shared instance is a data race. A caller parallelising over + /// quantiles — Parallel.For(… => mvt.CDF(points[i])) over one instance — will get + /// corrupted lattice shifts, and therefore silently wrong probabilities, or an + /// from inside the generator. + /// + /// + /// The remedy is one instance per thread, or a distinct generator per thread assigned through + /// . does not help, because it copies the generator by + /// reference and the clone races with its original. See for the full note. + /// + /// /// Reference: Genz, A. and Bretz, F. (2009). "Computation of Multivariate Normal and t Probabilities." /// Lecture Notes in Statistics, Vol. 195. Springer. /// @@ -747,6 +780,15 @@ public double[] InverseCDF(double[] probabilities) /// Creates a deep copy of this distribution. /// /// A new instance with identical parameters. + /// + /// The parameters, and the factorization built from them, are copied deeply. is + /// the one exception: it is copied by reference, so the clone and the original share a single + /// generator. Cloning is therefore not a way to make concurrent calls + /// safe above two dimensions, and the clone's CDF results are not reproducible independently of the + /// original's. Assign the clone its own generator when either matters. The reference copy is not an + /// oversight: is typed , and an arbitrary + /// exposes no general deep copy. + /// public override MultivariateDistribution Clone() { var clone = new MultivariateStudentT() diff --git a/Numerics/Distributions/Univariate/Base/UnivariateDistributionBase.cs b/Numerics/Distributions/Univariate/Base/UnivariateDistributionBase.cs index 7442a33c..a3951e76 100644 --- a/Numerics/Distributions/Univariate/Base/UnivariateDistributionBase.cs +++ b/Numerics/Distributions/Univariate/Base/UnivariateDistributionBase.cs @@ -292,7 +292,7 @@ public double[] InverseCDF(IList probabilities) /// , whose argument is a number of integration steps. The two are /// distinguished only by the type of the single argument, so the choice is invisible at the call /// site: CentralMoments(1E-8) requests a relative tolerance of 1E-8 from this adaptive - /// routine, while CentralMoments(1000) selects the fixed-step trapezoidal overload with 1,000 + /// routine, while CentralMoments(1000) selects the fixed-step bin-expectation overload with 1,000 /// steps. Writing CentralMoments(1) when a tolerance of 1.0 was meant silently runs the other /// method with a single step. The C++ port team flagged this pair as a foot-gun; the signatures are /// kept for source compatibility. @@ -334,7 +334,7 @@ public virtual double[] CentralMoments(double tolerance = 1E-8) } /// - /// Returns the central moments {Mean, Standard Deviation, Skew, and Kurtosis} of the distribution using numerical integration with Trapezoidal rule. + /// Returns the central moments {Mean, Standard Deviation, Skew, and Kurtosis} of the distribution using a fixed-step discrete expectation over stratified bins. /// ///Number of integration steps. Default = 300. /// Mean, Standard Deviation, Skew, and Kurtosis. @@ -350,10 +350,21 @@ public virtual double[] CentralMoments(double tolerance = 1E-8) /// source compatibility. /// /// - /// This overload stratifies [InverseCDF(1E-8), InverseCDF(1 − 1E-8)] into - /// equal bins and accumulates a trapezoidal sum, so the cost and the accuracy are both fixed by - /// and there is no convergence check. Note that its integration range is - /// narrower than the adaptive overload's, so the two do not agree exactly even when both converge. + /// What this overload actually computes, which is worth stating precisely because a port + /// cannot be written from the word "trapezoidal" alone: it stratifies + /// [InverseCDF(1E-8), InverseCDF(1 − 1E-8)] into equal bins, takes each + /// bin's probability mass ΔFᵢ as a difference of values, and accumulates + /// the discrete expectation Σᵢ xᵢᵏ · ΔFᵢ. It is therefore a bin-probability (midpoint) expectation + /// against the distribution, not a trapezoidal rule applied to the integrand x·f(x). The + /// representative point xᵢ is the bin midpoint for interior bins, the upper bound for the first bin + /// and the lower bound for the last. The standard deviation is recovered from the raw second moment + /// as √(E[X²] − E[X]²), and skewness and kurtosis are accumulated as already-standardized powers. + /// + /// + /// The first and last bins carry the whole of their tails — ΔF₀ is CDF(upper bound of bin 0), taken + /// from −∞, and the final ΔF is 1 − CDF(lower bound of the last bin) — so despite the 1E-8 stratification + /// endpoints the total probability sums to one and the effective range is not truncated. Cost + /// and accuracy are both fixed by and there is no convergence check. /// Within this library CompetingRisks, EmpiricalDistribution, /// GeneralizedNormal, KappaFour, Mixture and TruncatedDistribution all /// use this overload; only NoncentralT uses the adaptive one. diff --git a/Numerics/Sampling/MCMC/Base/MCMCSampler.cs b/Numerics/Sampling/MCMC/Base/MCMCSampler.cs index f50e6acf..67140c96 100644 --- a/Numerics/Sampling/MCMC/Base/MCMCSampler.cs +++ b/Numerics/Sampling/MCMC/Base/MCMCSampler.cs @@ -189,12 +189,17 @@ public int ThinningInterval /// /// /// The type is because the product overflows well inside the - /// range of settings the sampler accepts: there is no upper bound on , so a - /// run of 10⁸ iterations at the default thinning already exceeds . + /// range of settings the sampler accepts: nothing bounds from above, and at + /// the default output length, chain count and thinning interval the per-chain product passes + /// at about 1.074E8 iterations. passes + /// it four times sooner, at about 2.68E7 iterations, because of the multiplication by the default + /// four chains. /// /// /// This reports the settings as they are currently configured and does not validate them; the - /// settings are checked by ValidateSettings when is called. + /// settings are checked by ValidateSettings when is called. The one + /// exception is : a value below one is not a samplable configuration and + /// would otherwise divide by zero here, so it reports 0 rather than a framework-dependent number. /// /// /// The count describes the base loop and the base SampleChain, neither of @@ -207,23 +212,42 @@ public long TransitionCount { get { - // Derived from the same expressions Sample() uses, so the two cannot drift apart: - // Sample() computes outputIterations = ceil(OutputLength / NumberOfChains) and - // totalIterations = Iterations + outputIterations, then SampleChain advances the chain - // ThinningInterval times per iteration. The sum is widened to long before the - // multiplication so that large settings do not overflow. - int outputIterations = (int)Math.Ceiling(OutputLength / (double)NumberOfChains); - return ((long)Iterations + outputIterations) * ThinningInterval; + // NumberOfChains is only validated by ValidateSettings when Sample() runs, but this property + // is readable at any time. At zero chains the division below would be Infinity, and casting + // Infinity to int saturates to int.MaxValue on .NET Core while being unspecified on .NET + // Framework, so the answer would differ by target framework. Report 0 for a configuration + // that cannot be sampled instead. + if (NumberOfChains < 1) return 0L; + + // OutputIterations is the same member Sample() uses, so the two cannot drift apart. Each + // recorded iteration advances the chain ThinningInterval times. The sum is widened to long + // before the multiplication so that large settings do not overflow. + return ((long)Iterations + OutputIterations) * ThinningInterval; } } + /// + /// The number of recorded iterations that runs beyond + /// in order to collect the posterior output, ceil( / ). + /// + /// + /// Shared by and so that the reported work and the + /// work actually performed are computed from a single expression rather than two copies of it. + /// Callers must ensure is at least one; does so via + /// ValidateSettings and guards it directly. + /// + private int OutputIterations => (int)Math.Ceiling(OutputLength / (double)NumberOfChains); + /// /// The number of chain transitions performed across all chains during a call to . /// /// × . /// - /// The total evaluation budget of a run. At the defaults this is 120,000 × 4 = 480,000 transitions, - /// and therefore at least that many evaluations of . See + /// At the defaults this is 120,000 × 4 = 480,000 transitions. Treat it as a lower bound on the + /// evaluation count, not as a budget: every transition costs at least one evaluation of + /// , but a gradient-based sampler such as HMC or NUTS spends many + /// likelihood and gradient evaluations per transition, and chain initialization adds further + /// evaluations on top of all of these. See /// for why this differs so widely from . When /// is true these are distributed across worker threads, so this is /// the total work rather than the critical path. @@ -613,8 +637,9 @@ public virtual void Sample() InitializeCustomSettings(); } - // Output settings - int outputIterations = (int)Math.Ceiling(OutputLength / (double)NumberOfChains); + // Output settings. OutputIterations is shared with TransitionCount so that the advertised work + // and the work performed here cannot drift apart. + int outputIterations = OutputIterations; int totalIterations = Iterations + outputIterations; int outputCount = 0; Output = new List[NumberOfChains]; diff --git a/Test_Numerics/Data/Statistics/Test_Statistics.cs b/Test_Numerics/Data/Statistics/Test_Statistics.cs index 6a2a269c..2fde8d91 100644 --- a/Test_Numerics/Data/Statistics/Test_Statistics.cs +++ b/Test_Numerics/Data/Statistics/Test_Statistics.cs @@ -415,10 +415,18 @@ public void Test_ComputeLinearMoments_LargeSample() /// integer arithmetic and wrapped silently on large samples (USACE-RMC/Numerics#146). On this same /// sample the pre-fix code returned τ₄ = −0.185 at n = 1293 against an exact −3.80E−06, and /// τ₄ = −17.01 at n = 1460 against an exact +1.40E−04. τ₄ is bounded roughly in [−0.25, 1] for any real - /// distribution, so −17.01 is not an imprecise answer but a meaningless one. n = 1292 is the last - /// length whose b₃ numerator fits in an — the pre-fix code is bit-compatible there, - /// which is why the test also pins a length above the threshold. n = 1460 is four years of daily data, - /// the scenario the issue reports as triggering it in practice. + /// distribution, so −17.01 is not an imprecise answer but a meaningless one. + /// + /// + /// The three lengths are chosen to straddle the overflow threshold. n = 1292 is the last + /// length whose b₃ numerator fits in an , so the pre-fix code is bit-compatible + /// there; that case pins the correct answer but does not by itself guard the bug. n = 1293 is the + /// first length that overflows, so it pins the threshold itself: exact τ₄ is −3.797E−06 against a + /// pre-fix −0.185. n = 1460 is four years of daily data, the scenario the issue reports as + /// triggering it in practice. n = 1293 is also the clearest evidence that the tolerance floor below + /// is the right shape: τ₄ there is six times smaller than at n = 1292, yet the absolute error is + /// unchanged in order (1.56E-14 against 1.88E-14), which is what a magnitude-independent + /// cancellation floor looks like and is not what a relative error bound would predict. /// /// [TestMethod] @@ -428,6 +436,7 @@ public void Test_ComputeLinearMoments_ExactOracle() var oracle = new Dictionary { { 1292, new[] { 522512.5024550116d, 261084.88504577902d, 0.19954627498563463d, -2.204844394426912E-05d } }, + { 1293, new[] { 522161.99051382445d, 261046.5194639539d, 0.19994530607607433d, -3.796995958157631E-06d } }, { 1460, new[] { 522816.5913848459d, 261222.97693894972d, 0.19969563852408587d, 1.4021596010174597E-04d } } }; var names = new[] { "L-mean (λ₁)", "L-scale (λ₂)", "L-skewness (τ₃)", "L-kurtosis (τ₄)" }; diff --git a/Test_Numerics/Distributions/Multivariate/Test_MultivariateStudentT.cs b/Test_Numerics/Distributions/Multivariate/Test_MultivariateStudentT.cs index ccbee038..3d196c01 100644 --- a/Test_Numerics/Distributions/Multivariate/Test_MultivariateStudentT.cs +++ b/Test_Numerics/Distributions/Multivariate/Test_MultivariateStudentT.cs @@ -325,16 +325,22 @@ public void Test_CDF_2D() public void Test_CDF_3D_Scipy() { double[,] shape = { { 1.0, 0.5, 0.3 }, { 0.5, 1.0, 0.4 }, { 0.3, 0.4, 1.0 } }; - var mvt = new MultivariateStudentT(5.0, new double[3], shape); + + // A fresh instance per point. Above two dimensions the CDF advances the instance's MVNUNI, so + // reusing one instance would make each assertion depend on how many CDF calls preceded it, and + // the pinned values would shift if a call were inserted or reordered. Constructing per point + // makes every value a first-call value, which is also the protocol the agreements below were + // measured under. + double Cdf(double[] point) => new MultivariateStudentT(5.0, new double[3], shape).CDF(point); // measured agreement 1.21E-05 - Assert.AreEqual(0.2236624425, mvt.CDF(new[] { 0.0, 0.0, 0.0 }), 5E-5); + Assert.AreEqual(0.2236624425, Cdf(new[] { 0.0, 0.0, 0.0 }), 5E-5); // measured agreement 1.30E-05 - Assert.AreEqual(0.6307840101, mvt.CDF(new[] { 1.0, 1.0, 1.0 }), 5E-5); + Assert.AreEqual(0.6307840101, Cdf(new[] { 1.0, 1.0, 1.0 }), 5E-5); // measured agreement 2.05E-05 - Assert.AreEqual(0.7480738713, mvt.CDF(new[] { 2.0, 1.5, 1.0 }), 5E-5); + Assert.AreEqual(0.7480738713, Cdf(new[] { 2.0, 1.5, 1.0 }), 5E-5); // measured agreement 1.46E-05 - Assert.AreEqual(0.1566553047, mvt.CDF(new[] { -1.0, 0.5, 2.0 }), 5E-5); + Assert.AreEqual(0.1566553047, Cdf(new[] { -1.0, 0.5, 2.0 }), 5E-5); } /// @@ -364,16 +370,17 @@ public void Test_CDF_4D_Scipy() { 0.0, 0.0, 1.0, 0.0 }, { 0.0, 0.0, 0.0, 1.0 } }; - var mvt = new MultivariateStudentT(4.0, new double[4], shape); + // A fresh instance per point, for the reason given in Test_CDF_3D_Scipy. + double Cdf(double[] point) => new MultivariateStudentT(4.0, new double[4], shape).CDF(point); // Analytic: 1/2^4 by sign symmetry about the origin. Returned exactly. - Assert.AreEqual(0.0625, mvt.CDF(new[] { 0.0, 0.0, 0.0, 0.0 }), 1E-12, + Assert.AreEqual(0.0625, Cdf(new[] { 0.0, 0.0, 0.0, 0.0 }), 1E-12, "The lower-orthant probability at the centre of a symmetric 4-D distribution is exactly 1/16."); // measured agreement 6.04E-06 - Assert.AreEqual(0.4642714854, mvt.CDF(new[] { 1.0, 1.0, 1.0, 1.0 }), 5E-5); + Assert.AreEqual(0.4642714854, Cdf(new[] { 1.0, 1.0, 1.0, 1.0 }), 5E-5); // measured agreement 4.78E-05 - Assert.AreEqual(0.8086925605, mvt.CDF(new[] { 2.0, 2.0, 2.0, 2.0 }), 1E-4); + Assert.AreEqual(0.8086925605, Cdf(new[] { 2.0, 2.0, 2.0, 2.0 }), 1E-4); } /// diff --git a/Test_Numerics/Sampling/MCMC/Test_MCMCTransitionCount.cs b/Test_Numerics/Sampling/MCMC/Test_MCMCTransitionCount.cs index 3542bdd8..6f25ec6b 100644 --- a/Test_Numerics/Sampling/MCMC/Test_MCMCTransitionCount.cs +++ b/Test_Numerics/Sampling/MCMC/Test_MCMCTransitionCount.cs @@ -163,24 +163,45 @@ public void TransitionCount_MatchesTheTransitionsSampleActuallyPerforms() sampler.Sample(); - long observedPerChain = sampler.ChainCallCount[0] * sampler.ThinningInterval; - Assert.AreEqual(sampler.TransitionCount, observedPerChain, - "TransitionCount must equal the transitions Sample() performs on one chain."); + // Compare against the COUNTED transitions directly. Nothing here multiplies by + // ThinningInterval: TransitionCallCount is incremented once per ChainIteration, which is one + // real transition, so if the inner loop in SampleChain ever ran a different number of times + // than ThinningInterval the counts would diverge and this would fail. Multiplying an observed + // SampleChain count by ThinningInterval would instead assume the very thing under test. + Assert.AreEqual(sampler.TransitionCount, sampler.TransitionCallCount[0], + "TransitionCount must equal the transitions Sample() actually performs on one chain."); long observedTotal = 0; for (int i = 0; i < sampler.NumberOfChains; i++) { - Assert.AreEqual(sampler.ChainCallCount[0], sampler.ChainCallCount[i], + Assert.AreEqual(sampler.TransitionCallCount[0], sampler.TransitionCallCount[i], "Every chain is advanced the same number of times."); - observedTotal += sampler.ChainCallCount[i] * sampler.ThinningInterval; + observedTotal += sampler.TransitionCallCount[i]; } Assert.AreEqual(sampler.TotalTransitionCount, observedTotal, "TotalTransitionCount must equal the transitions Sample() performs across all chains."); + + // The recorded-iteration count is a separate fact worth pinning: it is the outer loop, + // Iterations + ceil(OutputLength / NumberOfChains) = 150, and the transitions are that times + // the thinning interval. Asserting both separately locates a future break to one loop or the + // other rather than just reporting a mismatched product. + Assert.AreEqual(150L, sampler.ChainCallCount[0], + "SampleChain must be invoked once per recorded iteration."); + Assert.AreEqual(sampler.ChainCallCount[0] * sampler.ThinningInterval, sampler.TransitionCallCount[0], + "Each recorded iteration must advance the chain exactly ThinningInterval times."); } /// - /// An RWMH sampler that records how many times each chain is advanced. + /// An RWMH sampler that records both how many times each chain is advanced (SampleChain, the + /// outer recorded-iteration loop) and how many transitions actually occur (ChainIteration, + /// the inner thinning loop). /// + /// + /// Counting ChainIteration is what makes the anti-drift assertion real. Counting only + /// SampleChain and multiplying by would assume the + /// inner loop performs exactly that many transitions, which is one of the two things + /// claims. + /// private sealed class CountingRWMH : RWMH { /// @@ -195,9 +216,24 @@ public CountingRWMH(List priors, LogLikelihood target, } /// - /// The number of times each chain has been advanced, indexed by chain. + /// The number of times each chain's recorded iteration ran, indexed by chain. /// - public long[] ChainCallCount { get; } = new long[64]; + public long[] ChainCallCount { get; private set; } = []; + + /// + /// The number of transitions each chain actually performed, indexed by chain. + /// + public long[] TransitionCallCount { get; private set; } = []; + + /// + public override void Sample() + { + // Sized here rather than at construction because NumberOfChains is assigned by the caller + // after the constructor runs. + ChainCallCount = new long[NumberOfChains]; + TransitionCallCount = new long[NumberOfChains]; + base.Sample(); + } /// protected override ParameterSet SampleChain(int index, ParameterSet state) @@ -205,6 +241,13 @@ protected override ParameterSet SampleChain(int index, ParameterSet state) ChainCallCount[index]++; return base.SampleChain(index, state); } + + /// + protected override ParameterSet ChainIteration(int index, ParameterSet state) + { + TransitionCallCount[index]++; + return base.ChainIteration(index, state); + } } } } diff --git a/docs/sampling/mcmc.md b/docs/sampling/mcmc.md index 305132c7..e4b27e93 100644 --- a/docs/sampling/mcmc.md +++ b/docs/sampling/mcmc.md @@ -109,8 +109,10 @@ transitions per chain = (Iterations + ceil(OutputLength / NumberOfChains)) * Thi = 480,000 across the 4 default chains ``` -Every transition costs at least one log-likelihood evaluation, so 480,000 is the evaluation budget of -a default run. Note that `WarmupIterations` is a *subset* of `Iterations`, not an addition to it — +Every transition costs at least one log-likelihood evaluation, so 480,000 is a **floor** on the +evaluation count of a default run, not a budget. A gradient-based sampler such as HMC or NUTS spends +many likelihood and gradient evaluations per transition, and chain initialization adds more on top, +so the real count can be far higher. Note that `WarmupIterations` is a *subset* of `Iterations`, not an addition to it — `ValidateSettings` rejects a warmup longer than half of `Iterations` — so it is already inside these figures and must not be added again. From 15a78e94fcd8ba5dd0f2e9c781d0c3d8fa74d81f Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Mon, 24 Aug 2026 20:47:22 -0600 Subject: [PATCH 079/222] Take the NUTS variance floor scale from measured variances only The relative floor was set from the largest entry of an array holding a window variance for usable coordinates and the prior-scaled fallback for the rest, so a single fallback-taking coordinate put the prior range back into the floor for every other coordinate. With a Uniform(-1000, 1000) prior that floor is 1.111e-7, which silently raises any coordinate whose posterior standard deviation is below 3.3e-4 - an attenuated form of the defect this floor was added alongside. The window scale is now the largest measured variance alone. When no coordinate produced a usable variance there is no window scale and the floor is inert, which is correct because every value is already a fallback. The remarks now state what the floor guarantees and what it does not: it bounds the metric's condition number at 1e12 and stops a numerically degenerate coordinate producing an unbounded mass, but it does not correct a coordinate that merely under-explored. Also pins the default to true in a test, hardens the sufficient-window test so it discriminates against the previous additive blend, and records the small-fit cost in the sampling documentation. --- Numerics/Sampling/MCMC/NUTS.cs | 34 +++-- .../Sampling/MCMC/Test_NUTS_MassMatrix.cs | 125 +++++++++++++++++- docs/sampling/mcmc.md | 2 +- 3 files changed, 143 insertions(+), 18 deletions(-) diff --git a/Numerics/Sampling/MCMC/NUTS.cs b/Numerics/Sampling/MCMC/NUTS.cs index da878220..385690bb 100644 --- a/Numerics/Sampling/MCMC/NUTS.cs +++ b/Numerics/Sampling/MCMC/NUTS.cs @@ -837,12 +837,21 @@ private void AccumulateWelfordStatistics(int chainIndex, double[] sample) /// must contain at least draws: the relative standard error of a /// sample variance is approximately sqrt(2 / (n - 1)), which is still about 47% at ten draws and /// worse below that, so a shorter window is noise rather than an estimate. Second, the variance must be - /// finite and strictly positive. Windows failing either condition take the fallback. A coordinate that - /// barely moved can still return a positive but degenerate variance, so every retained variance is then - /// floored at times the largest variance in the same window. That - /// floor is expressed on the window's own scale rather than on the prior range, so it is invariant to a - /// global rescaling of the target, and it caps the condition number of the diagonal metric at 1e12, - /// well inside double precision. + /// finite and strictly positive. Windows failing either condition take the fallback. With the shipped + /// buffer sizes the first condition is only reachable at a total warmup of twelve transitions or fewer, + /// so in practice it guards the degenerate configuration rather than a routine one. + /// + /// + /// Retained variances are then floored at times the largest + /// measured variance in the same window; fallback values never set that scale, because letting + /// them do so would put the prior range back into the floor for every other coordinate. Stating the + /// guarantee precisely: this bounds the diagonal metric's condition number at 1e12 and prevents a + /// coordinate that is numerically degenerate over the window from producing an unbounded mass. It does + /// not correct a coordinate that merely under-explored — a variance that comes back at 1e-4 to + /// 1e-6 of the truth is four to six orders of magnitude above the floor and passes through untouched, + /// yielding a mass that is too large and a step that is too small, which persists until the next window + /// re-estimates it. A floor tight enough to catch that case would have to encode an expectation about + /// how well the window mixed, which is exactly the prior-scaled assumption this method removes. /// /// private void UpdateMassMatrix(int chainIndex, ParameterSet currentState) @@ -853,7 +862,7 @@ private void UpdateMassMatrix(int chainIndex, ParameterSet currentState) // Estimate the posterior variance of every coordinate from this window, falling back to the // prior-scaled variance only for coordinates whose window estimate is unusable. var estimatedVariance = new double[NumberOfParameters]; - double largestVariance = 0d; + double largestWindowVariance = 0d; for (int j = 0; j < NumberOfParameters; j++) { double variance = _welfordM2[chainIndex][j] / (n - 1); @@ -867,12 +876,15 @@ private void UpdateMassMatrix(int chainIndex, ParameterSet currentState) bool windowIsUsable = n >= MIN_ADAPT_WINDOW_COUNT && Tools.IsFinite(variance) && variance > 0; estimatedVariance[j] = windowIsUsable ? variance : fallbackVariance; - if (estimatedVariance[j] > largestVariance) - largestVariance = estimatedVariance[j]; + // Only measured variances set the window's scale. Letting a fallback set it would put + // the prior range back into the floor for every other coordinate. + if (windowIsUsable && variance > largestWindowVariance) + largestWindowVariance = variance; } - // Bound the metric's condition number against the window's own scale. - double varianceFloor = largestVariance * RELATIVE_VARIANCE_FLOOR; + // Bound the metric's condition number against the window's own scale. When no coordinate + // produced a usable variance there is no such scale, and the floor is inert. + double varianceFloor = largestWindowVariance * RELATIVE_VARIANCE_FLOOR; for (int j = 0; j < NumberOfParameters; j++) { double inverseMass = Math.Max(estimatedVariance[j], varianceFloor); diff --git a/Test_Numerics/Sampling/MCMC/Test_NUTS_MassMatrix.cs b/Test_Numerics/Sampling/MCMC/Test_NUTS_MassMatrix.cs index 66348a5e..e2e47f31 100644 --- a/Test_Numerics/Sampling/MCMC/Test_NUTS_MassMatrix.cs +++ b/Test_Numerics/Sampling/MCMC/Test_NUTS_MassMatrix.cs @@ -4,6 +4,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Distributions; using Numerics.Mathematics.LinearAlgebra; +using Numerics.Mathematics.Optimization; using Numerics.Sampling.MCMC; namespace Sampling.MCMC @@ -281,13 +282,23 @@ public void Test_NUTS_ShortAdaptationWindow_UsesFallbackVariance() /// /// A window holding at least the minimum number of draws must use its own variance rather - /// than the prior-scaled fallback. + /// than the prior-scaled fallback, and must therefore produce an anisotropic metric. /// /// + /// /// With 14 warmup iterations the single adaptation window closes at iteration 12 with ten - /// accumulated draws, which is exactly the minimum. The posterior of this target is many - /// orders of magnitude narrower than the prior, so any use of the window's own variance is - /// unmistakable. + /// accumulated draws, which is exactly the minimum. + /// + /// + /// Ten draws taken early in warmup under an identity metric under-estimate the posterior + /// variances badly in absolute terms, so this test cannot pin the metric to the analytic + /// truth. What it can pin is the property that separates a window estimate from the blended + /// fallback: the additive prior-scaled blend returned the same value on every coordinate to + /// within a part in a thousand, because the prior term dominated it, whereas a window estimate + /// reflects the four decades of scale in this target. The assertions are that the metric is + /// nowhere near the fallback and that it is anisotropic by more than a factor of ten, both of + /// which the pre-fix code fails. + /// /// [TestMethod] public void Test_NUTS_SufficientAdaptationWindow_UsesWindowVariance() @@ -298,11 +309,85 @@ public void Test_NUTS_SufficientAdaptationWindow_UsesWindowVariance() double fallback = (2000d * 2000d) / 36.0; var inverseMass = ReadMetric(sampler, "_inverseMassMatrix"); + + double smallest = double.MaxValue, largest = 0d; for (int j = 0; j < inverseMass.Length; j++) { - Assert.IsTrue(inverseMass[j] > 0d && inverseMass[j] < 1d, - $"Coordinate {j} returned {inverseMass[j]:E4}; a ten-draw window on this target cannot produce a variance of order the prior's {fallback:E4}."); + Assert.IsLessThan(1e-3 * fallback, inverseMass[j], + $"Coordinate {j} returned {inverseMass[j]:E4}, within a thousandth of the prior-scaled fallback {fallback:E4}; the window variance was not used."); + Assert.IsGreaterThan(0d, inverseMass[j], $"Coordinate {j} returned a non-positive inverse mass."); + if (inverseMass[j] < smallest) smallest = inverseMass[j]; + if (inverseMass[j] > largest) largest = inverseMass[j]; } + + Assert.IsGreaterThan(10d, largest / smallest, + $"The metric spans only a factor of {largest / smallest:F3}. A window estimate on a target whose scales span four decades cannot be that flat; the additive prior-scaled blend was."); + } + + /// + /// The relative variance floor must take its scale from the largest measured variance in the + /// window, never from a coordinate that fell back to the prior-scaled value. + /// + /// + /// + /// This is a white-box test of the floor reference scale, driven by writing the Welford + /// accumulators directly and invoking the update, because the situation it guards against is + /// rare enough that provoking it through a real chain would not be reliable. + /// + /// + /// The window is given three coordinates: one whose accumulated sum of squares is exactly + /// zero, so it cannot produce a usable variance and must take the fallback; one whose variance + /// is a genuine 1e-9; and one whose variance is 1.0. If a fallback were allowed to set the + /// window scale, the floor would be 1e-12 times the prior-scaled 1.111e5, which is 1.111e-7, + /// and the 1e-9 coordinate would be raised by two orders of magnitude on a scale derived from + /// the prior - the failure mode this whole change exists to remove. Taking the scale from the + /// measured variances instead puts the floor at 1e-12 and leaves the coordinate alone. + /// + /// + [TestMethod] + public void Test_NUTS_RelativeVarianceFloor_IgnoresFallbackCoordinates() + { + var target = new DiagonalGaussian(3, 1e-3, 1e0); + var sampler = BuildSampler(target, 1000d, adapt: false, warmup: 12, iterations: 100, maxTreeDepth: 4); + sampler.Sample(); + + // Write a window in which coordinate 0 cannot produce a variance and the other two can. + const int windowCount = 100; + double[] windowVariance = { 0d, 1e-9, 1.0 }; + var welfordM2 = GetPrivateField(sampler, "_welfordM2"); + var welfordMean = GetPrivateField(sampler, "_welfordMean"); + var welfordCount = GetPrivateField(sampler, "_welfordCount"); + for (int j = 0; j < windowVariance.Length; j++) + { + welfordM2[0][j] = windowVariance[j] * (windowCount - 1); + welfordMean[0][j] = 0d; + } + welfordCount[0] = windowCount; + + InvokeUpdateMassMatrix(sampler); + + var inverseMass = ReadMetric(sampler, "_inverseMassMatrix"); + double priorFallback = (2000d * 2000d) / 36.0; + + Assert.AreEqual(priorFallback, inverseMass[0], 1e-9 * priorFallback, + "The coordinate with no usable variance should have taken the prior-scaled fallback."); + Assert.AreEqual(1e-9, inverseMass[1], 1e-15, + $"The genuinely small variance was raised to {inverseMass[1]:E4}; the floor took its scale from a fallback rather than from the measured variances."); + Assert.AreEqual(1.0, inverseMass[2], 1e-12, + "The largest measured variance should pass through unchanged."); + } + + /// + /// Mass-matrix adaptation is enabled by default. + /// + [TestMethod] + public void Test_NUTS_AdaptMassMatrix_DefaultsToTrue() + { + var target = new DiagonalGaussian(2, 1e-1, 1e0); + var priors = new List { new Uniform(-10d, 10d), new Uniform(-10d, 10d) }; + var sampler = new NUTS(priors, target.LogLikelihood); + + Assert.IsTrue(sampler.AdaptMassMatrix, "NUTS must adapt the diagonal mass matrix by default."); } /// @@ -360,6 +445,34 @@ public void Test_NUTS_AdaptationDisabled_ReproducesCapturedDraws() } } + /// + /// Reads a private instance field of the sampler. + /// + /// The field type. + /// The sampler. + /// The private field name. + /// The field value. + private static T GetPrivateField(NUTS sampler, string fieldName) + { + var field = typeof(NUTS).GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance); + Assert.IsNotNull(field, "The private field " + fieldName + " was not found on NUTS."); + var value = field!.GetValue(sampler); + Assert.IsInstanceOfType(value, typeof(T), "The private field " + fieldName + " had an unexpected type."); + return (T)value!; + } + + /// + /// Invokes the private mass-matrix update for chain zero at the current state. + /// + /// A sampled sampler, so that its adaptation state is initialized. + private static void InvokeUpdateMassMatrix(NUTS sampler) + { + var method = typeof(NUTS).GetMethod("UpdateMassMatrix", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.IsNotNull(method, "The private method UpdateMassMatrix was not found on NUTS."); + var last = sampler.Output[0][sampler.Output[0].Count - 1]; + method!.Invoke(sampler, new object[] { 0, new ParameterSet((double[])last.Values.Clone(), last.Fitness) }); + } + /// /// Asserts a value matches a captured reference to a relative tolerance of 1e-12. /// diff --git a/docs/sampling/mcmc.md b/docs/sampling/mcmc.md index e4b27e93..e3e2b6fb 100644 --- a/docs/sampling/mcmc.md +++ b/docs/sampling/mcmc.md @@ -571,7 +571,7 @@ After warmup, the step size is fixed to $\exp(\log \bar{\varepsilon})$. - Divergence threshold: if $H - H_0 > 1000$, the trajectory is considered divergent and tree-building stops - NUTS always accepts a candidate from the tree (acceptance is built into the multinomial weighting), so `AcceptCount` increments every iteration - Step size adaptation occurs only during the warmup phase, with step sizes clamped to $[10^{-10}, \, 10^{5}]$ -- `AdaptMassMatrix` defaults to `true`. During warmup the diagonal metric is estimated with Welford's online algorithm over Stan-style doubling windows, and each parameter's estimated posterior variance becomes its inverse mass, so the leapfrog step in that direction scales with the parameter's own posterior width. Without it a single step size has to serve every parameter at once, and on a posterior whose parameters differ in scale the sampler saturates `MaxTreeDepth` on nearly every transition. Set it to `false` to sample with the fixed metric supplied through `Mass` +- `AdaptMassMatrix` defaults to `true`. During warmup the diagonal metric is estimated with Welford's online algorithm over Stan-style doubling windows, and each parameter's estimated posterior variance becomes its inverse mass, so the leapfrog step in that direction scales with the parameter's own posterior width. Without it a single step size has to serve every parameter at once, and on a posterior whose parameters differ in scale the sampler saturates `MaxTreeDepth` on nearly every transition. Set it to `false` to sample with the fixed metric supplied through `Mass`. On a well-conditioned fit with only two or three parameters the metric has little to correct and adaptation costs up to about **38% more leapfrog steps per transition** (measured on the Normal, Logistic and Gumbel reference fits over six seeds); on an ill-conditioned posterior it is worth one to two orders of magnitude the other way ```cs var nuts = new NUTS(priors, logLikelihood); From 61758acc85b0b64af1f5846798d96a5e035924dc Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Tue, 25 Aug 2026 08:09:45 -0600 Subject: [PATCH 080/222] Make the NUTS target acceptance rate settable Raising the dual-averaging acceptance target is the standard response to divergent transitions, and it was unreachable behind a get-only property over a private constant. TargetAcceptanceRate now has a setter, the two adaptation sites read the property instead of the constant, and the value is validated in ValidateCustomSettings alongside the other settings. The default stays 0.80, so existing results are unchanged. --- Numerics/Sampling/MCMC/NUTS.cs | 38 ++++- .../MCMC/Test_NUTS_TargetAcceptanceRate.cs | 148 ++++++++++++++++++ 2 files changed, 181 insertions(+), 5 deletions(-) create mode 100644 Test_Numerics/Sampling/MCMC/Test_NUTS_TargetAcceptanceRate.cs diff --git a/Numerics/Sampling/MCMC/NUTS.cs b/Numerics/Sampling/MCMC/NUTS.cs index 385690bb..63851f06 100644 --- a/Numerics/Sampling/MCMC/NUTS.cs +++ b/Numerics/Sampling/MCMC/NUTS.cs @@ -141,7 +141,9 @@ public NUTS(List priorDistributions, LogLikelihood logL private double[] _previousEnergy = Array.Empty(); private bool[] _hasPreviousEnergy = Array.Empty(); - // Dual averaging hyperparameters (Hoffman & Gelman 2014, Section 3.2) + // Dual averaging hyperparameters (Hoffman & Gelman 2014, Section 3.2). + // DELTA_TARGET is the default of the settable TargetAcceptanceRate, which is what the + // adaptation actually reads. private const double DELTA_TARGET = 0.80; private const double GAMMA = 0.05; private const double T0 = 10.0; @@ -178,9 +180,34 @@ public NUTS(List priorDistributions, LogLikelihood logL public HMC.Gradient GradientFunction { get; } /// - /// The target Metropolis acceptance probability for dual averaging adaptation. Default = 0.80. + /// Gets or sets the target Metropolis acceptance probability that dual averaging adapts the + /// leapfrog step size toward during warmup. Must be strictly between 0 and 1. Default = 0.80. /// - public double TargetAcceptanceRate => DELTA_TARGET; + /// + /// + /// The default of 0.80 is the value recommended by Hoffman and Gelman (2014) and is what Stan's + /// adapt_delta defaults to. Leaving it alone reproduces the sampler's historical behaviour + /// exactly. + /// + /// + /// Raising it toward 0.90 or 0.95 makes dual averaging settle on a shorter step size, which + /// integrates the Hamiltonian more accurately and is the standard response to a run that reports + /// divergent transitions through . The cost is proportional: a + /// shorter step needs more leapfrog steps, and therefore more gradient evaluations, to cover the + /// same trajectory length. A target close to 1 can drive the step size to the lower clamp and + /// exhaust on every transition, so raise it in steps and watch + /// alongside . + /// + /// + /// Persistent divergences that survive a raised target usually indicate a posterior geometry the + /// step size cannot fix on its own, such as a funnel, rather than an adaptation failure. + /// + /// + /// The value is validated when sampling starts, not at assignment, which is the convention the + /// other settings on this class and on follow. + /// + /// + public double TargetAcceptanceRate { get; set; } = DELTA_TARGET; /// /// Gets the mean post-warmup Hamiltonian acceptance probability for each chain. @@ -327,6 +354,7 @@ protected override void ValidateCustomSettings() if (Mass.Length != NumberOfParameters) throw new ArgumentException(nameof(Mass), "The mass vector must be the same length as the number of parameters."); if (_initialStepSize <= 0) throw new ArgumentException("stepSize", "The leapfrog step size must be positive."); if (MaxTreeDepth < 1) throw new ArgumentException(nameof(MaxTreeDepth), "The maximum tree depth must be at least 1."); + if (!Tools.IsFinite(TargetAcceptanceRate) || TargetAcceptanceRate <= 0d || TargetAcceptanceRate >= 1d) throw new ArgumentException(nameof(TargetAcceptanceRate), "The target acceptance rate must be greater than 0 and less than 1."); } /// @@ -677,7 +705,7 @@ protected override ParameterSet ChainIteration(int index, ParameterSet state) // Preserve the original neutral fallback when no subtree contributed. double adaptationAcceptanceProbability = numAlpha > 0 ? averageAcceptanceProbability - : DELTA_TARGET; + : TargetAcceptanceRate; DualAveragingUpdate(index, adaptationAcceptanceProbability); // Accumulate Welford statistics during mass-matrix adaptation windows. @@ -1118,7 +1146,7 @@ private void DualAveragingUpdate(int chainIndex, double avgAcceptProb) // Update running average of the acceptance statistic _chainHBar[chainIndex] = (1.0 - 1.0 / (m + T0)) * _chainHBar[chainIndex] - + (DELTA_TARGET - avgAcceptProb) / (m + T0); + + (TargetAcceptanceRate - avgAcceptProb) / (m + T0); // Compute new log step size double logEps = _chainMu[chainIndex] - Math.Sqrt(m) / GAMMA * _chainHBar[chainIndex]; diff --git a/Test_Numerics/Sampling/MCMC/Test_NUTS_TargetAcceptanceRate.cs b/Test_Numerics/Sampling/MCMC/Test_NUTS_TargetAcceptanceRate.cs new file mode 100644 index 00000000..8444afac --- /dev/null +++ b/Test_Numerics/Sampling/MCMC/Test_NUTS_TargetAcceptanceRate.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Distributions; +using Numerics.Sampling.MCMC; + +namespace Sampling.MCMC +{ + /// + /// Unit tests for the NUTS dual-averaging target acceptance rate. + /// + /// + /// + /// Authors: + /// + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + /// + /// Description: + /// + /// + /// Raising the target acceptance rate is the standard response to divergent transitions, so the + /// value has to be reachable. These tests pin the default at the Hoffman and Gelman value of 0.80, + /// pin the accepted range to the open interval (0, 1), and check that a raised target does what it + /// is raised for: a smaller adapted step size. + /// + /// + /// References: + /// + /// + /// Hoffman, M.D. and Gelman, A. (2014). "The No-U-Turn Sampler: Adaptively Setting Path Lengths + /// in Hamiltonian Monte Carlo." Journal of Machine Learning Research, 15, 1593-1623. + /// + /// + [TestClass] + public class Test_NUTS_TargetAcceptanceRate + { + /// + /// Builds a small, fully deterministic single-chain sampler on a standard normal target. + /// + /// An unsampled sampler. + private static NUTS BuildSampler() + { + var priors = new List { new Uniform(-50d, 50d), new Uniform(-50d, 50d) }; + + static double logLH(double[] x) => -0.5d * (x[0] * x[0] + x[1] * x[1]); + + return new NUTS(priors, logLH, maxTreeDepth: 6) + { + NumberOfChains = 1, + ParallelizeChains = false, + InitialIterations = 1, + ThinningInterval = 1, + WarmupIterations = 60, + Iterations = 150, + OutputLength = 100, + PRNGSeed = 12345 + }; + } + + /// + /// The target acceptance rate defaults to the Hoffman and Gelman value of 0.80. + /// + [TestMethod] + public void Test_NUTS_TargetAcceptanceRate_DefaultsToPointEight() + { + var sampler = BuildSampler(); + Assert.AreEqual(0.80d, sampler.TargetAcceptanceRate, 0d, + "The target acceptance rate default must remain 0.80 so that existing results are unchanged."); + } + + /// + /// The target acceptance rate is settable and reads back exactly what was written. + /// + [TestMethod] + public void Test_NUTS_TargetAcceptanceRate_IsSettable() + { + var sampler = BuildSampler(); + sampler.TargetAcceptanceRate = 0.95d; + Assert.AreEqual(0.95d, sampler.TargetAcceptanceRate, 0d); + } + + /// + /// Values outside the open interval (0, 1), and non-finite values, are rejected when the + /// sampler validates its settings. + /// + /// + /// The sampler validates configuration at the start of rather + /// than at assignment, which is the convention every other setting in this class and in + /// follows. Validation runs before any chain work, so these calls + /// throw immediately. + /// + [TestMethod] + public void Test_NUTS_TargetAcceptanceRate_RejectsValuesOutsideTheUnitInterval() + { + double[] invalid = + { + 0d, 1d, -0.5d, -1e-16d, 1.5d, double.NaN, + double.PositiveInfinity, double.NegativeInfinity + }; + + foreach (double value in invalid) + { + var sampler = BuildSampler(); + sampler.TargetAcceptanceRate = value; + Assert.ThrowsExactly(() => sampler.Sample(), + $"A target acceptance rate of {value:R} should have been rejected."); + } + } + + /// + /// Values strictly inside (0, 1) are accepted. + /// + [TestMethod] + public void Test_NUTS_TargetAcceptanceRate_AcceptsValuesInsideTheUnitInterval() + { + double[] valid = { 1e-12d, 0.6d, 0.8d, 0.95d, 0.99d, 1d - 1e-12d }; + + foreach (double value in valid) + { + var sampler = BuildSampler(); + sampler.TargetAcceptanceRate = value; + sampler.Sample(); + Assert.AreEqual(value, sampler.TargetAcceptanceRate, 0d); + } + } + + /// + /// Raising the target acceptance rate must shorten the adapted step size, which is the whole + /// reason the setting exists. + /// + [TestMethod] + public void Test_NUTS_TargetAcceptanceRate_RaisingTheTargetShortensTheStepSize() + { + var baseline = BuildSampler(); + baseline.Sample(); + + var raised = BuildSampler(); + raised.TargetAcceptanceRate = 0.99d; + raised.Sample(); + + Assert.IsLessThan(baseline.StepSizes[0], raised.StepSizes[0], + $"Raising the target from 0.80 to 0.99 left the step size at {raised.StepSizes[0]:R}, " + + $"which is not shorter than the default run's {baseline.StepSizes[0]:R}."); + } + } +} From a080f0515cca6d985db081a405aa191f0966c96a Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Tue, 25 Aug 2026 08:20:07 -0600 Subject: [PATCH 081/222] Reuse the gradient a NUTS leapfrog step already evaluated Every leapfrog step evaluates the gradient at both ends and returns only the position and momentum, so consecutive leaves of a doubling recompute the gradient at the position the previous leaf just landed on. The step-size heuristic repeats the same work on every trial step, and it now runs after each mass-matrix adaptation window rather than once. With the default finite-difference gradient each of those evaluations costs about 2 * D log-likelihood evaluations. A four-entry per-chain ring buffer now answers a repeat evaluation at an identical position. Positions are compared on their bit patterns, so a hit returns exactly the value a recomputation would have produced and the sign of a zero cannot be substituted. The memo is keyed by chain because chains run under Parallel.For, stores copies of both the position and the gradient, and is allocated fresh in InitializeCustomSettings. The gradient depends on position alone, so an entry stays valid across a metric change. On the test configuration this cuts gradient evaluations from 8,568 to 4,611 with the metric adapting and from 22,280 to 11,809 with a fixed metric, and the recorded draws are unchanged bit for bit. --- Numerics/Sampling/MCMC/NUTS.cs | 151 ++++++- .../Sampling/MCMC/Test_NUTS_GradientReuse.cs | 383 ++++++++++++++++++ 2 files changed, 530 insertions(+), 4 deletions(-) create mode 100644 Test_Numerics/Sampling/MCMC/Test_NUTS_GradientReuse.cs diff --git a/Numerics/Sampling/MCMC/NUTS.cs b/Numerics/Sampling/MCMC/NUTS.cs index 63851f06..46f2c88d 100644 --- a/Numerics/Sampling/MCMC/NUTS.cs +++ b/Numerics/Sampling/MCMC/NUTS.cs @@ -164,6 +164,25 @@ public NUTS(List priorDistributions, LogLikelihood logL private int _termBuffer; private int[] _adaptWindowEnds = null!; + /// + /// The number of recently evaluated positions each chain's gradient memo retains. + /// + /// + /// A doubling extends the trajectory in one direction, so the join a memo has to cover is + /// between two consecutive leaves and only one entry is strictly needed. Four leaves room for + /// the step-size heuristic, which re-enters the same starting position on every trial step, and + /// for the forward and backward endpoints to be resident at the same time, at a cost of + /// 8 × doubles per chain. + /// + private const int GRADIENT_CACHE_SIZE = 4; + + // Per-chain gradient memo: positions, the gradient at each, and which slots hold a value. + // Keyed by chain because Sample() runs chains under Parallel.For. See EvaluateGradient. + private double[][][] _gradientCachePositions = null!; + private double[][][] _gradientCacheValues = null!; + private bool[][] _gradientCacheOccupied = null!; + private int[] _gradientCacheNextSlot = null!; + /// /// The mass vector for the momentum distribution. /// @@ -390,8 +409,24 @@ protected override void InitializeCustomSettings() _massMatrix = new double[N][]; _inverseMassMatrix = new double[N][]; + // Allocate the gradient memo empty. Allocating it here is also what resets it, so a + // re-run cannot serve an entry from the previous run. + _gradientCachePositions = new double[N][][]; + _gradientCacheValues = new double[N][][]; + _gradientCacheOccupied = new bool[N][]; + _gradientCacheNextSlot = new int[N]; + for (int i = 0; i < N; i++) { + _gradientCachePositions[i] = new double[GRADIENT_CACHE_SIZE][]; + _gradientCacheValues[i] = new double[GRADIENT_CACHE_SIZE][]; + _gradientCacheOccupied[i] = new bool[GRADIENT_CACHE_SIZE]; + for (int s = 0; s < GRADIENT_CACHE_SIZE; s++) + { + _gradientCachePositions[i][s] = new double[D]; + _gradientCacheValues[i][s] = new double[D]; + } + // Start with identity mass matrix (or user-provided mass) _massMatrix[i] = new double[D]; _inverseMassMatrix[i] = new double[D]; @@ -560,6 +595,114 @@ private double TrySingleStepLogAcceptance(double[] theta0, double[] r0, double[] } } + /// + /// Evaluates the gradient of the log-likelihood at a position, reusing a value this chain + /// computed at the identical position within the last + /// evaluations. + /// + /// The position to evaluate at. Not retained; a copy is stored. + /// The chain index whose memo is consulted. + /// + /// The gradient. The array is owned by the memo and must be treated as read-only by the caller; + /// it stays valid until this chain records further misses. + /// + /// + /// + /// Why there is anything to reuse. A leapfrog step evaluates the gradient twice, once at + /// its opening half-step and once at the position it lands on. Consecutive leaves of a doubling + /// chain end to end, so leaf k lands on the position leaf k+1 opens from, and + /// without a memo leaf k+1 recomputes what leaf k already produced and threw away. + /// The step-size heuristic re-enters the same starting position on every trial step and repeats + /// the same waste. With the default finite-difference gradient each of those evaluations costs + /// on the order of 2 × log-likelihood evaluations. + /// + /// + /// Why reuse cannot move a draw. Positions are compared bitwise through + /// , never with == and never within a + /// tolerance, so a hit is only possible at the exact point the delegate was called on and + /// returns exactly the value a recomputation would produce. Bitwise comparison also keeps + /// +0 and -0 distinct, which == would not, so a sign of zero cannot be + /// silently substituted. A tolerance-based memo would change results and is not what this is. + /// + /// + /// Why the metric is not part of the key. takes a position + /// and nothing else, is fixed at construction, and the default implementation closes only over + /// the log-likelihood and the prior bounds. The gradient of the log-density is therefore a + /// function of position alone: it does not depend on the mass matrix, the step size, the + /// adaptation window, or the chain. An entry recorded under one metric is exactly as valid after + /// replaces the metric, which matters because that method re-runs + /// at the end of every adaptation window. The chain index is + /// part of the key for thread safety, not for correctness of the value. + /// + /// + /// Assumption. The gradient delegate must be a deterministic function of its argument. + /// That is already required for a seeded run to be reproducible, and the sampler is not usable + /// without it, but it is the one property this memo depends on. + /// + /// + /// Thread safety. Every array is indexed by chain first, and + /// gives each chain index to exactly one + /// iteration at a time, so no two threads touch + /// one chain's slots. The join at the end of each parallel iteration publishes the writes. + /// + /// + /// Copy semantics. Both the position and the gradient are copied into the memo. The + /// caller's position array is mutated in place by , and a + /// caller-supplied gradient delegate is free to return the same every call, + /// so neither may be retained by reference. + /// + /// + /// A gradient whose length does not match the parameter count is returned without being stored, + /// so a malformed delegate still fails where and how it failed before. + /// + /// + private double[] EvaluateGradient(double[] position, int chainIndex) + { + int D = NumberOfParameters; + var positions = _gradientCachePositions[chainIndex]; + var values = _gradientCacheValues[chainIndex]; + var occupied = _gradientCacheOccupied[chainIndex]; + + for (int slot = 0; slot < GRADIENT_CACHE_SIZE; slot++) + { + if (!occupied[slot]) continue; + + var stored = positions[slot]; + bool identical = true; + for (int j = 0; j < D; j++) + { + if (BitConverter.DoubleToInt64Bits(stored[j]) != BitConverter.DoubleToInt64Bits(position[j])) + { + identical = false; + break; + } + } + + if (identical) return values[slot]; + } + + // A miss evaluates the delegate. Anything it throws propagates untouched and nothing is + // recorded, so the failure paths in BuildTree and TrySingleStepLogAcceptance are unchanged. + double[] gradient = GradientFunction(position).Array; + if (gradient.Length != D) return gradient; + + int next = _gradientCacheNextSlot[chainIndex]; + var slotPosition = positions[next]; + var slotValue = values[next]; + + // Clear the slot before overwriting it so a partially written entry can never be matched. + occupied[next] = false; + for (int j = 0; j < D; j++) + { + slotPosition[j] = position[j]; + slotValue[j] = gradient[j]; + } + occupied[next] = true; + _gradientCacheNextSlot[chainIndex] = (next + 1) % GRADIENT_CACHE_SIZE; + + return slotValue; + } + /// /// Performs a single leapfrog step in-place on raw arrays, using the per-chain mass matrix. /// Used by FindReasonableEpsilon to avoid Vector allocations. @@ -575,7 +718,7 @@ private void LeapfrogInPlace(double[] theta, double[] momentum, double epsilon, double[] invMass = _inverseMassMatrix[chainIndex]; // Half-step momentum update - double[] grad = GradientFunction(theta).Array; + double[] grad = EvaluateGradient(theta, chainIndex); for (int j = 0; j < D; j++) momentum[j] += grad[j] * halfEps; @@ -590,7 +733,7 @@ private void LeapfrogInPlace(double[] theta, double[] momentum, double epsilon, } // Half-step momentum update - grad = GradientFunction(theta).Array; + grad = EvaluateGradient(theta, chainIndex); for (int j = 0; j < D; j++) momentum[j] += grad[j] * halfEps; } @@ -1096,7 +1239,7 @@ private static TreeState InvalidTreeState(Vector theta, Vector momentum) double[] invMass = _inverseMassMatrix[chainIndex]; // Half-step momentum update - var grad = GradientFunction(theta.Array); + var grad = new Vector(EvaluateGradient(theta.Array, chainIndex)); var r = momentum + grad * (epsilon * 0.5); // Full-step position update using per-chain inverse mass matrix @@ -1114,7 +1257,7 @@ private static TreeState InvalidTreeState(Vector theta, Vector momentum) } // Half-step momentum update - grad = GradientFunction(q.Array); + grad = new Vector(EvaluateGradient(q.Array, chainIndex)); r = r + grad * (epsilon * 0.5); return (q, r); diff --git a/Test_Numerics/Sampling/MCMC/Test_NUTS_GradientReuse.cs b/Test_Numerics/Sampling/MCMC/Test_NUTS_GradientReuse.cs new file mode 100644 index 00000000..554d4ca0 --- /dev/null +++ b/Test_Numerics/Sampling/MCMC/Test_NUTS_GradientReuse.cs @@ -0,0 +1,383 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Distributions; +using Numerics.Mathematics.LinearAlgebra; +using Numerics.Sampling.MCMC; + +namespace Sampling.MCMC +{ + /// + /// Characterization tests for the NUTS gradient memo. + /// + /// + /// + /// Authors: + /// + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + /// + /// Description: + /// + /// + /// Consecutive leapfrog leaves chain: a leaf ends at a position whose gradient it has just + /// evaluated, and the next leaf opens by evaluating the gradient at that same position. The + /// sampler memoizes those values, which is only defensible if it changes nothing. These tests are + /// the evidence for that claim, so their assertions are bitwise on + /// rather than on a tolerance. An + /// epsilon-based assertion would not separate "identical" from "very close", which is the only + /// distinction being made here. + /// + /// + /// The reference bit patterns were captured from the sampler before the memo was added. They are + /// the same on .NET Framework 4.8.1 and on .NET 8, 9, and 10: this target's gradient is exact and + /// the trajectory arithmetic is IEEE-754 addition, multiplication, and division, so no + /// transcendental last-place difference reaches the recorded draws on this configuration. + /// + /// + /// Both metric settings are covered. With enabled, which is the + /// default, the metric changes at the end of every adaptation window and each change re-runs the + /// step-size heuristic, so the memo is consulted under several different metrics within one run. + /// The gradient of the log-density is a function of position alone and never sees the metric, so an + /// entry stays valid across a metric change; this test is what holds that reasoning to account. + /// + /// + [TestClass] + public class Test_NUTS_GradientReuse + { + /// The number of leading recorded draws compared against the reference, per chain. + private const int GoldenDraws = 20; + + /// The gradient evaluation count of the adapting-metric configuration. + private const int AdaptedMetricGradientCalls = 4611; + + /// The gradient evaluation count of the fixed-metric configuration. + private const int FixedMetricGradientCalls = 11809; + + /// The per-coordinate standard deviations of the target. + /// + /// Powers of two, so that the gradient -x / s^2 is exact and the reference values cannot + /// drift on a division. + /// + private static readonly double[] Sd = { 0.25d, 1d, 2d, 8d }; + + /// + /// The log-density of the zero-mean diagonal Gaussian target, up to an additive constant. + /// + /// The parameter vector. + /// The log-density. + private static double LogLikelihood(double[] x) + { + double sum = 0d; + for (int j = 0; j < Sd.Length; j++) + { + double z = x[j] / Sd[j]; + sum += -0.5d * z * z; + } + return sum; + } + + /// + /// The exact gradient of . + /// + /// The parameter vector. + /// The gradient. + private static Vector Gradient(IList x) + { + var g = new Vector(Sd.Length); + for (int j = 0; j < Sd.Length; j++) + g[j] = -x[j] / (Sd[j] * Sd[j]); + return g; + } + + /// + /// Builds the fixed configuration the reference values were captured from. + /// + /// Whether to adapt the diagonal mass matrix. + /// The gradient delegate. + /// An unsampled sampler. + /// + /// Two chains, run serially so that ordering cannot vary, and a seeded PRNG. The analytic + /// gradient is supplied so that the delegate invocation count is exactly the number of gradient + /// evaluations the sampler asked for. + /// + private static NUTS BuildSampler(bool adapt, HMC.Gradient gradient) + { + var priors = new List(); + for (int j = 0; j < Sd.Length; j++) priors.Add(new Uniform(-50d, 50d)); + + return new NUTS(priors, LogLikelihood, maxTreeDepth: 6, gradientFunction: gradient) + { + NumberOfChains = 2, + ParallelizeChains = false, + InitialIterations = 8, + ThinningInterval = 1, + WarmupIterations = 60, + Iterations = 150, + OutputLength = 100, + PRNGSeed = 12345, + AdaptMassMatrix = adapt + }; + } + + /// + /// Runs the fixed configuration and asserts every compared draw against its reference bit pattern. + /// + /// Whether to adapt the diagonal mass matrix. + /// The reference bit patterns, by chain, then draw, then coordinate. + private static void AssertGoldenDraws(bool adapt, long[] expected) + { + var sampler = BuildSampler(adapt, Gradient); + sampler.Sample(); + + int k = 0; + for (int c = 0; c < sampler.Output.Length; c++) + { + Assert.IsGreaterThanOrEqualTo(GoldenDraws, sampler.Output[c].Count, + $"Chain {c} recorded only {sampler.Output[c].Count} draws; the reference covers {GoldenDraws}."); + + for (int i = 0; i < GoldenDraws; i++) + { + var values = sampler.Output[c][i].Values; + for (int j = 0; j < values.Length; j++, k++) + { + long actual = BitConverter.DoubleToInt64Bits(values[j]); + Assert.AreEqual(expected[k], actual, + $"Chain {c} draw {i} coordinate {j} moved: expected {BitConverter.Int64BitsToDouble(expected[k]):R}, got {values[j]:R}. " + + "The gradient memo must not move a single draw."); + } + } + } + + Assert.AreEqual(expected.Length, k, "The reference length and the compared draw count disagree."); + } + + /// + /// Runs the fixed configuration with a counting gradient delegate. + /// + /// Whether to adapt the diagonal mass matrix. + /// The number of gradient delegate invocations. + private static int CountGradientEvaluations(bool adapt) + { + int count = 0; + var sampler = BuildSampler(adapt, (x) => { count++; return Gradient(x); }); + sampler.Sample(); + return count; + } + + /// + /// With the metric adapting, which is the default, the recorded draws must match the reference + /// bit for bit. + /// + [TestMethod] + public void Test_NUTS_GradientReuse_AdaptedMetric_ReproducesReferenceDrawsBitwise() + { + AssertGoldenDraws(true, AdaptedMetricDraws); + } + + /// + /// With a fixed metric the recorded draws must match the reference bit for bit. + /// + [TestMethod] + public void Test_NUTS_GradientReuse_FixedMetric_ReproducesReferenceDrawsBitwise() + { + AssertGoldenDraws(false, FixedMetricDraws); + } + + /// + /// The memo must remove the redundant gradient evaluation at the join between consecutive + /// leapfrog leaves, and must remove nothing else. + /// + /// + /// + /// Before the memo this configuration evaluated the gradient 8,568 times with the + /// metric adapting and 22,280 times with a fixed metric, against 4611 and + /// 11809 after it: a reduction of 1.86x and 1.89x, close to the 2x ceiling of removing + /// one of the two gradient evaluations every leapfrog step makes. The counts are asserted + /// exactly rather than as an inequality: the companion bitwise tests already pin the trajectory, + /// so with the trajectory fixed the count is decided by the memo policy alone, and an exact + /// assertion catches a silent change in hit rate that a threshold would not. + /// + /// + /// The counts are the same on all four target frameworks, as are the draws. + /// + /// + [TestMethod] + public void Test_NUTS_GradientReuse_ReducesGradientEvaluations() + { + Assert.AreEqual(AdaptedMetricGradientCalls, CountGradientEvaluations(true), + "The adapted-metric gradient evaluation count changed; before the memo it was 8,568."); + Assert.AreEqual(FixedMetricGradientCalls, CountGradientEvaluations(false), + "The fixed-metric gradient evaluation count changed; before the memo it was 22,280."); + } + + /// + /// The memo must serve a repeat of the identical position and must not serve a position that + /// differs only in the sign of a zero. + /// + /// + /// + /// This is a white-box probe of the memo's lookup, driven through reflection because the memo is + /// private and the situation it guards against cannot be provoked reliably through a real chain. + /// It exists because the obvious simplification of the lookup, comparing with == instead + /// of on the bit pattern, would pass every other test in this class: +0 == -0 is true, so + /// an == lookup would answer a query at negative zero with the gradient at positive zero. + /// For this target those two gradients differ, in the sign of their own zero, and the difference + /// would then propagate into the momentum. + /// + /// + /// Negative zero is constructed from its bit pattern rather than written as a literal so that + /// nothing about the constant depends on how the compiler folds a unary minus. + /// + /// + [TestMethod] + public void Test_NUTS_GradientReuse_TreatsNegativeZeroAsADistinctPosition() + { + int count = 0; + var sampler = BuildSampler(false, (x) => { count++; return Gradient(x); }); + sampler.Sample(); + + // Empty chain zero's memo so the probe cannot hit an entry the run left behind. + var occupiedField = typeof(NUTS).GetField("_gradientCacheOccupied", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.IsNotNull(occupiedField, "The private field _gradientCacheOccupied was not found on NUTS."); + var occupied = occupiedField!.GetValue(sampler) as bool[][]; + Assert.IsNotNull(occupied, "The private field _gradientCacheOccupied was not a bool[][]."); + Array.Clear(occupied![0], 0, occupied[0].Length); + + var evaluate = typeof(NUTS).GetMethod("EvaluateGradient", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.IsNotNull(evaluate, "The private method EvaluateGradient was not found on NUTS."); + + double negativeZero = BitConverter.Int64BitsToDouble(long.MinValue); + var atPositiveZero = new double[Sd.Length]; + var atNegativeZero = new double[Sd.Length]; + for (int j = 0; j < Sd.Length; j++) + { + atPositiveZero[j] = 0d; + atNegativeZero[j] = negativeZero; + } + + count = 0; + var first = evaluate!.Invoke(sampler, new object[] { atPositiveZero, 0 }) as double[]; + Assert.IsNotNull(first, "EvaluateGradient did not return a double[]."); + var reference = (double[])first!.Clone(); + Assert.AreEqual(1, count, "The first evaluation at a fresh position must reach the gradient delegate."); + + var repeated = evaluate.Invoke(sampler, new object[] { (double[])atPositiveZero.Clone(), 0 }) as double[]; + Assert.IsNotNull(repeated, "EvaluateGradient did not return a double[]."); + Assert.AreEqual(1, count, "Repeating the identical position must be served from the memo."); + for (int j = 0; j < Sd.Length; j++) + { + Assert.AreEqual(BitConverter.DoubleToInt64Bits(reference[j]), BitConverter.DoubleToInt64Bits(repeated![j]), + $"Coordinate {j} came back from the memo with different bits than the evaluation that filled it."); + } + + var flipped = evaluate.Invoke(sampler, new object[] { atNegativeZero, 0 }) as double[]; + Assert.IsNotNull(flipped, "EvaluateGradient did not return a double[]."); + Assert.AreEqual(2, count, + "Negative zero is a different position from positive zero and must not be answered from the memo."); + for (int j = 0; j < Sd.Length; j++) + { + Assert.AreNotEqual(BitConverter.DoubleToInt64Bits(reference[j]), BitConverter.DoubleToInt64Bits(flipped![j]), + $"Coordinate {j} returned the gradient at positive zero for a query at negative zero."); + } + } + + /// + /// Reference draw bit patterns with the diagonal mass matrix adapting, captured before the + /// gradient memo was added. + /// + private static readonly long[] AdaptedMetricDraws = + { + -4628717957276900768L, -4614344352116551053L, 4603611391276814710L, 4622225145915450648L, + -4628717957276900768L, -4614344352116551053L, 4603611391276814710L, 4622225145915450648L, + 4595421539249117930L, 4609283367556249551L, -4619553774880583316L, 4610874494819852500L, + 4595421539249117930L, 4609283367556249551L, -4619553774880583316L, 4610874494819852500L, + 4595268059699645008L, -4614515372391026388L, -4615358376842143412L, 4618502018394842424L, + -4626965350435671000L, -4617098977443399769L, -4613529980504667989L, -4611552404961281768L, + -4626965350435671000L, -4617098977443399769L, -4613529980504667989L, -4611552404961281768L, + -4625117031024711957L, -4618975755754580538L, 4610911898721537679L, -4619588748106623692L, + -4626833903520120148L, -4618788725822154797L, -4617624839434109446L, -4615653340781801740L, + -4628586846217905589L, -4624617629968939766L, 4607978285003208601L, 4621524210250680202L, + -4628586846217905589L, -4624617629968939766L, 4607978285003208601L, 4621524210250680202L, + -4631048037799758788L, 4598606562225593095L, 4592275416877696720L, -4598889720843471700L, + -4631048037799758788L, 4598606562225593095L, 4592275416877696720L, -4598889720843471700L, + -4630614677190815564L, 4599829856108105510L, 4602035654696767498L, 4622517052814789598L, + -4630614677190815564L, 4599829856108105510L, 4602035654696767498L, 4622517052814789598L, + -4628044555644799119L, -4615544604587728724L, -4605324568804896625L, 4615289631917756594L, + 4586141649926781016L, -4615659434416111236L, -4606765315056840162L, 4619070661431980192L, + -4623994016138285324L, 4604694158858439959L, 4600862861138731824L, 4619894943440118595L, + -4624997371801789776L, 4606985563011054683L, 4609022620809182642L, 4622044230572136596L, + -4624997371801789776L, 4606985563011054683L, 4609022620809182642L, 4622044230572136596L, + 4589218988890646141L, 4601529455578585566L, 4612990821753793139L, 4599039655050837472L, + 4589218988890646141L, 4601529455578585566L, 4612990821753793139L, 4599039655050837472L, + 4591202640630172407L, 4611551731127349298L, -4615612418592602340L, 4618519724767551228L, + 4591202640630172407L, 4611551731127349298L, -4615612418592602340L, 4618519724767551228L, + 4601900831011982165L, -4653844673522834944L, -4609404599743355790L, 4613069599123696405L, + 4594877573097428800L, 4600646389199820926L, -4617865255456648110L, 4623949388794256850L, + -4629536297211435422L, 4596843827571823539L, -4615522644836689135L, 4617146743729294822L, + -4629536297211435422L, 4596843827571823539L, -4615522644836689135L, 4617146743729294822L, + -4664804292599706624L, -4626728832200013664L, 4612284455809246765L, 4621384453701015334L, + 4592195647805789165L, -4626623762681769642L, 4595481563303407552L, -4600832715318815973L, + 4592195647805789165L, -4626623762681769642L, 4595481563303407552L, -4600832715318815973L, + 4592195647805789165L, -4626623762681769642L, 4595481563303407552L, -4600832715318815973L, + 4592195647805789165L, -4626623762681769642L, 4595481563303407552L, -4600832715318815973L, + 4589036769505293890L, 4607652900249041740L, 4612330264484591542L, -4598014163315121310L, + 4589036769505293890L, 4607652900249041740L, 4612330264484591542L, -4598014163315121310L, + -4630619197172725760L, 4607461456558991839L, 4616368273643204144L, 4624443866127377968L, + -4625034992050830473L, -4615566671929947463L, -4615817665372380310L, -4614254007529137496L, + -4633193357778560152L, -4615566018286652080L, 4615300278428133156L, -4602192214965783574L, + -4633193357778560152L, -4615566018286652080L, 4615300278428133156L, -4602192214965783574L, + -4634323884716628752L, 4607777917423490830L, -4611052078354232235L, 4617262316064901896L, + }; + + /// + /// Reference draw bit patterns with a fixed identity metric, captured before the gradient memo + /// was added. + /// + private static readonly long[] FixedMetricDraws = + { + 4594975404019207961L, -4621711899945443789L, -4611952112987838184L, -4611500315287884938L, + -4628256621123045462L, -4619287892620029858L, -4614232458884030478L, -4612488514663140402L, + 4595761952827806092L, 4600751122092123102L, 4613575793801799735L, 4624992171110232310L, + -4633345527287176903L, -4624171562507790115L, -4615839794461924276L, 4626190194575195907L, + -4625189100783473061L, -4618536524957887010L, -4615614065028290685L, 4626263576848944373L, + -4626259148557215207L, 4607595391508306782L, -4613028951595474552L, 4624957502953452018L, + 4600173594495267507L, -4614617300851356384L, -4624189941312186700L, -4606082020433159272L, + 4597716792141818872L, -4614241438875454975L, -4621289566791541011L, -4606369784396057216L, + -4624226452947540604L, -4632705290174055120L, 4600388820663058666L, -4603567199260082839L, + 4595030593496324994L, -4622399403773605971L, -4607493426312042384L, -4603197985821305989L, + -4624299062973987490L, 4596732760167193896L, -4614965948047856994L, -4601755088583506868L, + 4598764634486221846L, -4618308293726385899L, -4611492594737092578L, -4602437470651895100L, + 4598398684193455921L, -4628829198274197006L, 4607299749312890132L, -4602334364034222595L, + -4638029692586604976L, 4601898400980151357L, 4608354046106682907L, -4602296705490575856L, + -4640156845448054636L, -4621005638293633998L, 4605491041205431264L, -4601745596801913269L, + 4580794748882690968L, -4630632412424480978L, 4599148763407829511L, -4601027697149221266L, + 4595370424142256248L, 4589932628438169415L, -4622895218238951981L, 4612827535321258879L, + -4645439079761559424L, -4636650727009762936L, -4619918594546849499L, 4612789608014404110L, + -4645439079761559424L, -4636650727009762936L, -4619918594546849499L, 4612789608014404110L, + -4628794829880472607L, 4579388095185454488L, -4618882751248197878L, 4612846310818148172L, + -4625710289702829098L, 4603182349032855910L, -4621938938232760059L, -4617799282642129463L, + 4583418010629183568L, -4616037850412337266L, 4613423682573598488L, 4606801000288630048L, + -4633885671984424928L, -4613768735541523568L, 4613915903054395787L, 4597687828877945318L, + -4633899563109034347L, 4609344896051078928L, -4609273226815769781L, -4604900278076377306L, + -4628638329354169868L, -4614623471288870178L, -4631850860977198964L, -4602141301970495431L, + -4628638329354169868L, -4614623471288870178L, -4631850860977198964L, -4602141301970495431L, + -4629008656552818505L, -4612834134734929207L, -4627303695876047340L, -4602210861703509402L, + -4641736452535869472L, 4604706186948601715L, -4605196490149347326L, -4603642802112773977L, + -4624276442547037690L, 4607005947237830839L, -4605441975554914160L, -4603435550651477501L, + 4595696255895012454L, 4602594494893305626L, -4609367765923473060L, -4602244045001584135L, + -4634704556634067176L, -4617306856281886408L, -4614470594343065330L, -4601884596191411264L, + -4635705013991812368L, 4585025165220297424L, -4615059973892280391L, -4601753650874562102L, + -4635641498537308012L, -4632169441432132214L, 4611340934453912532L, -4603328932724678622L, + -4635680531729715384L, 4571273438108619616L, -4614340023978301181L, -4604687755738060340L, + -4635680531729715384L, 4571273438108619616L, -4614340023978301181L, -4604687755738060340L, + -4622031799990434282L, 4604340633299106995L, -4614172774610055267L, -4604855512262537533L, + 4596885573681383132L, 4607201543281707569L, -4614327399435879440L, -4605153905120656034L, + -4626105143823512956L, -4620259699054322338L, 4597645397151358219L, -4604402949357508067L, + 4600037919184754832L, -4613908372478531545L, 4608276699040816935L, -4604149254759479244L, + 4560105449497328640L, -4613686306857414974L, 4606789166016571720L, -4603618241203553513L, + }; + } +} From 01c85a119eb577436d4abbb74029f8fbebd4a359 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Tue, 25 Aug 2026 08:29:22 -0600 Subject: [PATCH 082/222] Correct the sample-length count and variance floor scale in documentation --- Numerics/Sampling/MCMC/NUTS.cs | 3 ++- Test_Numerics/Data/Statistics/Test_Statistics.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Numerics/Sampling/MCMC/NUTS.cs b/Numerics/Sampling/MCMC/NUTS.cs index 46f2c88d..3a24026e 100644 --- a/Numerics/Sampling/MCMC/NUTS.cs +++ b/Numerics/Sampling/MCMC/NUTS.cs @@ -974,7 +974,8 @@ private void AccumulateWelfordStatistics(int chainIndex, double[] sample) /// /// The smallest per-coordinate variance retained in an adapted metric, as a fraction of the - /// largest variance in the same window. This caps the diagonal metric's condition number at 1e12. + /// largest measured variance in the same window; fallback values never set that scale. + /// This caps the diagonal metric's condition number at 1e12. /// private const double RELATIVE_VARIANCE_FLOOR = 1e-12; diff --git a/Test_Numerics/Data/Statistics/Test_Statistics.cs b/Test_Numerics/Data/Statistics/Test_Statistics.cs index 2fde8d91..aeb70580 100644 --- a/Test_Numerics/Data/Statistics/Test_Statistics.cs +++ b/Test_Numerics/Data/Statistics/Test_Statistics.cs @@ -376,7 +376,7 @@ public void Test_ComputeLinearMoments_LargeSample() /// This is the non-degenerate companion to . That /// test uses an evenly spaced sample whose τ₃ and τ₄ are analytically exactly zero at every length, so /// it cannot distinguish a sign error, a b₂-only correction or a partial fix from a complete one. This - /// test pins all four moments to non-zero values at two sample lengths. + /// test pins all four moments to non-zero values at three sample lengths. /// /// /// The sample. xᵢ = kᵢ² / 64 with kᵢ = (i · 7919) mod 10007 for i = 1..n. Every value is an exact From 4154eb8380e66c2b4f681e457cf77c10799dd003 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Tue, 25 Aug 2026 08:47:44 -0600 Subject: [PATCH 083/222] Record why the BFGS dfpmin parameter-change exit is not implemented --- .../Mathematics/Optimization/Local/BFGS.cs | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/Numerics/Mathematics/Optimization/Local/BFGS.cs b/Numerics/Mathematics/Optimization/Local/BFGS.cs index c5a73af8..fef05289 100644 --- a/Numerics/Mathematics/Optimization/Local/BFGS.cs +++ b/Numerics/Mathematics/Optimization/Local/BFGS.cs @@ -99,14 +99,25 @@ protected override void Optimize() int D = NumberOfParameters; double EPS = Tools.DoubleMachineEpsilon; // TOLX is declared here to match Numerical Recipes' dfpmin, but it is never compared against - // anything in this method: the outer parameter-change convergence test of dfpmin — exit when the - // largest relative step falls below TOLX — is NOT implemented. The TOLX that is actually used is - // a separate local in LineSearchArmijo, where it sets alamin = TOLX / test; that is the inner - // lnsrch step-size floor and is unrelated to outer convergence. Convergence here is therefore - // decided solely by CheckConvergence's relative-function-change test, so there is no stagnation - // exit: a run that stops improving its parameters while the function value still moves will keep - // iterating to MaxIterations. Do not assume such an exit exists. Adding it is deferred to a - // separate task behind a measurement gate, since it would change which iterate is returned. + // anything in this method: the outer parameter-change convergence test of dfpmin, which exits + // when the largest relative parameter step falls below TOLX, is NOT implemented. The only other + // TOLX in this file is a separate local in LineSearchArmijo, where it sets alamin = TOLX / test; + // that is the inner lnsrch step-size floor and is unrelated to outer convergence. Note that + // LineSearchArmijo is currently unreachable: Optimize calls the strong Wolfe LineSearch instead. + // Convergence here is therefore decided solely by CheckConvergence's relative-function-change + // test, so there is no stagnation exit: a run that stops improving its parameters while the + // function value still moves will keep iterating to MaxIterations. Do not assume such an + // exit exists. + // + // Adding the dfpmin test was measured against the full test suite and rejected. It is a large + // efficiency win in isolation, removing 98.9% of BFGS function evaluations across the suite + // (32.4M to 354k) and turning all 40 MaximumIterationsReached runs into Success with no change + // to any individual BFGS optimum. It was rejected because it changes the point the global + // searches report: MultiStart and MLSL share their evaluation counter with the BFGS runs they + // launch, so the wasted iterations currently act as extra sampling that feeds their best-point + // tracking. With the exit in place Test_MultiStart.Test_Eggholder fails, the returned x moving + // from within 1E-2 of 512 to 512.0564. The exit cannot be added until that dependence is + // addressed separately. double TOLX = 4 * EPS, STPMX = 100.0; bool cancel = false, check = false; From 7ab20449352d13759c3dc33f483f38a9bd541c42 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Tue, 25 Aug 2026 09:02:49 -0600 Subject: [PATCH 084/222] Correct the NUTS gradient memo aliasing contract and guard its copies The returns documentation claimed a memo entry stays valid for four further misses. A hit does not advance the ring, so a hit on the slot the ring is pointing at is overwritten by the very next miss. Both callers consume the array before evaluating again, so nothing was wrong at runtime, but the documented invariant was. EvaluateGradient now records the position before calling the gradient delegate rather than after, so a delegate that writes through its argument cannot leave a mutated position paired with the gradient of the point that was actually evaluated. The slot stays unoccupied across the delegate call, so a throw or a malformed length still leaves nothing matchable. The four call sites now say that the array they receive is memo-owned and read-only. Vector's arithmetic allocates its results, so the current expressions cannot write through it, but its indexer setter is public. Two probes cover the copy-on-store requirement directly: one mutates the caller's position array after storing, one supplies a gradient delegate that returns a single reused Vector. Storing the gradient by reference is invisible to every other test in the suite, because every gradient in the repository allocates per call. Draws are unchanged bit for bit and the gradient counts are unchanged. --- Numerics/Sampling/MCMC/NUTS.cs | 43 +++-- .../Sampling/MCMC/Test_NUTS_GradientReuse.cs | 164 +++++++++++++++--- 2 files changed, 167 insertions(+), 40 deletions(-) diff --git a/Numerics/Sampling/MCMC/NUTS.cs b/Numerics/Sampling/MCMC/NUTS.cs index 3a24026e..7879466e 100644 --- a/Numerics/Sampling/MCMC/NUTS.cs +++ b/Numerics/Sampling/MCMC/NUTS.cs @@ -603,8 +603,10 @@ private double TrySingleStepLogAcceptance(double[] theta0, double[] r0, double[] /// The position to evaluate at. Not retained; a copy is stored. /// The chain index whose memo is consulted. /// - /// The gradient. The array is owned by the memo and must be treated as read-only by the caller; - /// it stays valid until this chain records further misses. + /// The gradient. The array is owned by the memo and must be treated as read-only by the caller. + /// It is valid only until the next miss on this chain: a hit does not advance the ring, so a hit + /// on the slot the ring is currently pointing at is overwritten by the very next miss. Both + /// callers consume the array before evaluating again, which is what makes that safe. /// /// /// @@ -649,7 +651,9 @@ private double TrySingleStepLogAcceptance(double[] theta0, double[] r0, double[] /// Copy semantics. Both the position and the gradient are copied into the memo. The /// caller's position array is mutated in place by , and a /// caller-supplied gradient delegate is free to return the same every call, - /// so neither may be retained by reference. + /// so neither may be retained by reference. The position is copied before the delegate + /// runs, so a delegate that writes through its argument cannot pair a mutated position with the + /// gradient of the point that was actually evaluated. /// /// /// A gradient whose length does not match the parameter count is returned without being stored, @@ -681,22 +685,27 @@ private double[] EvaluateGradient(double[] position, int chainIndex) if (identical) return values[slot]; } - // A miss evaluates the delegate. Anything it throws propagates untouched and nothing is - // recorded, so the failure paths in BuildTree and TrySingleStepLogAcceptance are unchanged. - double[] gradient = GradientFunction(position).Array; - if (gradient.Length != D) return gradient; - + // A miss claims a slot and records the position it is about to evaluate at before calling + // the delegate, so a delegate that writes through its argument cannot leave the memo + // holding a mutated position paired with the gradient of the original point. The slot is + // marked empty for the whole window, so a throw or a malformed length leaves nothing that + // could be matched later. int next = _gradientCacheNextSlot[chainIndex]; var slotPosition = positions[next]; var slotValue = values[next]; - // Clear the slot before overwriting it so a partially written entry can never be matched. occupied[next] = false; for (int j = 0; j < D; j++) - { slotPosition[j] = position[j]; + + // Anything the delegate throws propagates untouched, so the failure paths in BuildTree and + // TrySingleStepLogAcceptance are unchanged. + double[] gradient = GradientFunction(position).Array; + if (gradient.Length != D) return gradient; + + for (int j = 0; j < D; j++) slotValue[j] = gradient[j]; - } + occupied[next] = true; _gradientCacheNextSlot[chainIndex] = (next + 1) % GRADIENT_CACHE_SIZE; @@ -717,7 +726,8 @@ private void LeapfrogInPlace(double[] theta, double[] momentum, double epsilon, double halfEps = epsilon * 0.5; double[] invMass = _inverseMassMatrix[chainIndex]; - // Half-step momentum update + // Half-step momentum update. grad is the memo's own buffer and must be read only; write + // through it and this chain's entry is silently corrupted. double[] grad = EvaluateGradient(theta, chainIndex); for (int j = 0; j < D; j++) momentum[j] += grad[j] * halfEps; @@ -732,7 +742,7 @@ private void LeapfrogInPlace(double[] theta, double[] momentum, double epsilon, theta[j] = _upperBounds[j] - Tools.DoubleMachineEpsilon; } - // Half-step momentum update + // Half-step momentum update. As above, grad is memo-owned and must be read only. grad = EvaluateGradient(theta, chainIndex); for (int j = 0; j < D; j++) momentum[j] += grad[j] * halfEps; @@ -1239,7 +1249,10 @@ private static TreeState InvalidTreeState(Vector theta, Vector momentum) int D = NumberOfParameters; double[] invMass = _inverseMassMatrix[chainIndex]; - // Half-step momentum update + // Half-step momentum update. new Vector(double[]) wraps without copying, so grad is a live + // view onto the memo's own buffer and must be read only. Vector's arithmetic operators all + // allocate their result, so the expression below cannot write through it, but Vector's + // indexer setter is public: do not accumulate, clamp, or negate in place through grad. var grad = new Vector(EvaluateGradient(theta.Array, chainIndex)); var r = momentum + grad * (epsilon * 0.5); @@ -1257,7 +1270,7 @@ private static TreeState InvalidTreeState(Vector theta, Vector momentum) q[j] = PriorDistributions[j].Maximum - Tools.DoubleMachineEpsilon; } - // Half-step momentum update + // Half-step momentum update. As above, grad wraps the memo's buffer and must be read only. grad = new Vector(EvaluateGradient(q.Array, chainIndex)); r = r + grad * (epsilon * 0.5); diff --git a/Test_Numerics/Sampling/MCMC/Test_NUTS_GradientReuse.cs b/Test_Numerics/Sampling/MCMC/Test_NUTS_GradientReuse.cs index 554d4ca0..5acf999e 100644 --- a/Test_Numerics/Sampling/MCMC/Test_NUTS_GradientReuse.cs +++ b/Test_Numerics/Sampling/MCMC/Test_NUTS_GradientReuse.cs @@ -208,9 +208,13 @@ public void Test_NUTS_GradientReuse_FixedMetric_ReproducesReferenceDrawsBitwise( public void Test_NUTS_GradientReuse_ReducesGradientEvaluations() { Assert.AreEqual(AdaptedMetricGradientCalls, CountGradientEvaluations(true), - "The adapted-metric gradient evaluation count changed; before the memo it was 8,568."); + "The adapted-metric gradient evaluation count changed; before the memo it was 8,568. " + + "Check the companion bitwise tests first: if they also moved the trajectory changed and " + + "this count followed it, which is a different finding from a change in memo hit rate."); Assert.AreEqual(FixedMetricGradientCalls, CountGradientEvaluations(false), - "The fixed-metric gradient evaluation count changed; before the memo it was 22,280."); + "The fixed-metric gradient evaluation count changed; before the memo it was 22,280. " + + "Check the companion bitwise tests first: if they also moved the trajectory changed and " + + "this count followed it, which is a different finding from a change in memo hit rate."); } /// @@ -238,16 +242,7 @@ public void Test_NUTS_GradientReuse_TreatsNegativeZeroAsADistinctPosition() int count = 0; var sampler = BuildSampler(false, (x) => { count++; return Gradient(x); }); sampler.Sample(); - - // Empty chain zero's memo so the probe cannot hit an entry the run left behind. - var occupiedField = typeof(NUTS).GetField("_gradientCacheOccupied", BindingFlags.NonPublic | BindingFlags.Instance); - Assert.IsNotNull(occupiedField, "The private field _gradientCacheOccupied was not found on NUTS."); - var occupied = occupiedField!.GetValue(sampler) as bool[][]; - Assert.IsNotNull(occupied, "The private field _gradientCacheOccupied was not a bool[][]."); - Array.Clear(occupied![0], 0, occupied[0].Length); - - var evaluate = typeof(NUTS).GetMethod("EvaluateGradient", BindingFlags.NonPublic | BindingFlags.Instance); - Assert.IsNotNull(evaluate, "The private method EvaluateGradient was not found on NUTS."); + ClearMemo(sampler); double negativeZero = BitConverter.Int64BitsToDouble(long.MinValue); var atPositiveZero = new double[Sd.Length]; @@ -259,31 +254,150 @@ public void Test_NUTS_GradientReuse_TreatsNegativeZeroAsADistinctPosition() } count = 0; - var first = evaluate!.Invoke(sampler, new object[] { atPositiveZero, 0 }) as double[]; - Assert.IsNotNull(first, "EvaluateGradient did not return a double[]."); - var reference = (double[])first!.Clone(); + var reference = (double[])InvokeEvaluateGradient(sampler, atPositiveZero).Clone(); Assert.AreEqual(1, count, "The first evaluation at a fresh position must reach the gradient delegate."); - var repeated = evaluate.Invoke(sampler, new object[] { (double[])atPositiveZero.Clone(), 0 }) as double[]; - Assert.IsNotNull(repeated, "EvaluateGradient did not return a double[]."); + var repeated = InvokeEvaluateGradient(sampler, (double[])atPositiveZero.Clone()); Assert.AreEqual(1, count, "Repeating the identical position must be served from the memo."); - for (int j = 0; j < Sd.Length; j++) - { - Assert.AreEqual(BitConverter.DoubleToInt64Bits(reference[j]), BitConverter.DoubleToInt64Bits(repeated![j]), - $"Coordinate {j} came back from the memo with different bits than the evaluation that filled it."); - } + AssertSameBits(reference, repeated, + "came back from the memo with different bits than the evaluation that filled it"); - var flipped = evaluate.Invoke(sampler, new object[] { atNegativeZero, 0 }) as double[]; - Assert.IsNotNull(flipped, "EvaluateGradient did not return a double[]."); + var flipped = InvokeEvaluateGradient(sampler, atNegativeZero); Assert.AreEqual(2, count, "Negative zero is a different position from positive zero and must not be answered from the memo."); for (int j = 0; j < Sd.Length; j++) { - Assert.AreNotEqual(BitConverter.DoubleToInt64Bits(reference[j]), BitConverter.DoubleToInt64Bits(flipped![j]), + Assert.AreNotEqual(BitConverter.DoubleToInt64Bits(reference[j]), BitConverter.DoubleToInt64Bits(flipped[j]), $"Coordinate {j} returned the gradient at positive zero for a query at negative zero."); } } + /// + /// The memo must store a copy of the queried position, not a reference to the caller's array. + /// + /// + /// The requirement is real and not hypothetical: 's in-place leapfrog mutates + /// the very array it passed in, one statement after the opening half-step. A memo holding that + /// array by reference would find its key rewritten under it, so the entry would stop matching + /// the point it was computed at and start matching a point it was not. This probe reproduces + /// that directly — evaluate at a position, mutate the caller's array, and check that the entry + /// still answers the original position and does not answer the mutated one. + /// + [TestMethod] + public void Test_NUTS_GradientReuse_StoresACopyOfTheQueriedPosition() + { + int count = 0; + var sampler = BuildSampler(false, (x) => { count++; return Gradient(x); }); + sampler.Sample(); + ClearMemo(sampler); + + var probe = new double[] { 0.5d, -1.5d, 2.25d, -4d }; + var original = (double[])probe.Clone(); + + count = 0; + var reference = (double[])InvokeEvaluateGradient(sampler, probe).Clone(); + Assert.AreEqual(1, count, "The first evaluation at a fresh position must reach the gradient delegate."); + + // Mutate the caller's array in place, exactly as LeapfrogInPlace does to its position. + for (int j = 0; j < probe.Length; j++) probe[j] += 1d; + + var atOriginal = InvokeEvaluateGradient(sampler, (double[])original.Clone()); + Assert.AreEqual(1, count, + "The memo stopped recognising the position it was filled at after the caller's array was mutated; " + + "it is holding a reference to that array rather than a copy."); + AssertSameBits(reference, atOriginal, "was not returned unchanged from the memo"); + + InvokeEvaluateGradient(sampler, probe); + Assert.AreEqual(2, count, + "The memo answered a position it never evaluated at; its stored key followed the caller's mutation."); + } + + /// + /// The memo must store a copy of the gradient the delegate returned, not a reference to the + /// it came back in. + /// + /// + /// is a caller-supplied delegate and nothing obliges it to + /// allocate a fresh per call; returning one reused buffer is a reasonable + /// thing for a performance-minded implementation to do. A memo holding that buffer by reference + /// would have every entry aliased to the same array, so each entry would return the most recent + /// gradient rather than its own. The delegate here does exactly that, and the probe evaluates at + /// two distinct positions before re-querying the first. + /// + [TestMethod] + public void Test_NUTS_GradientReuse_StoresACopyOfTheReturnedGradient() + { + int count = 0; + var reused = new Vector(Sd.Length); + var sampler = BuildSampler(false, (x) => + { + count++; + for (int j = 0; j < Sd.Length; j++) reused[j] = -x[j] / (Sd[j] * Sd[j]); + return reused; + }); + sampler.Sample(); + ClearMemo(sampler); + + var first = new double[] { 0.5d, -1.5d, 2.25d, -4d }; + var second = new double[] { 1.5d, -0.5d, 3.25d, -3d }; + + count = 0; + var atFirst = (double[])InvokeEvaluateGradient(sampler, first).Clone(); + Assert.AreEqual(1, count, "The first evaluation at a fresh position must reach the gradient delegate."); + + // This overwrites the delegate's one buffer with the gradient at a different position. + InvokeEvaluateGradient(sampler, second); + Assert.AreEqual(2, count, "A different position must reach the gradient delegate."); + + var repeated = InvokeEvaluateGradient(sampler, (double[])first.Clone()); + Assert.AreEqual(2, count, "Repeating the first position must be served from the memo."); + AssertSameBits(atFirst, repeated, + "came back as the gradient at a later position; the memo is aliased to the delegate's reused buffer"); + } + + /// + /// Empties chain zero's gradient memo so a probe cannot hit an entry a sampling run left behind. + /// + /// A sampled sampler, so that its memo is allocated. + private static void ClearMemo(NUTS sampler) + { + var field = typeof(NUTS).GetField("_gradientCacheOccupied", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.IsNotNull(field, "The private field _gradientCacheOccupied was not found on NUTS."); + var occupied = field!.GetValue(sampler) as bool[][]; + Assert.IsNotNull(occupied, "The private field _gradientCacheOccupied was not a bool[][]."); + Array.Clear(occupied![0], 0, occupied[0].Length); + } + + /// + /// Calls the sampler's private gradient memo for chain zero. + /// + /// A sampled sampler. + /// The position to evaluate at. + /// The gradient the memo returned. This array is owned by the memo. + private static double[] InvokeEvaluateGradient(NUTS sampler, double[] position) + { + var method = typeof(NUTS).GetMethod("EvaluateGradient", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.IsNotNull(method, "The private method EvaluateGradient was not found on NUTS."); + var result = method!.Invoke(sampler, new object[] { position, 0 }) as double[]; + Assert.IsNotNull(result, "EvaluateGradient did not return a double[]."); + return result!; + } + + /// + /// Asserts two gradients agree on every bit of every coordinate. + /// + /// The reference gradient. + /// The gradient to check. + /// A description of the failure, completing "Coordinate {j} ...". + private static void AssertSameBits(double[] expected, double[] actual, string what) + { + for (int j = 0; j < expected.Length; j++) + { + Assert.AreEqual(BitConverter.DoubleToInt64Bits(expected[j]), BitConverter.DoubleToInt64Bits(actual[j]), + $"Coordinate {j} {what}."); + } + } + /// /// Reference draw bit patterns with the diagonal mass matrix adapting, captured before the /// gradient memo was added. From 4b78bce54b664c9525fe6fb8957ee36afca9a5fc Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Tue, 25 Aug 2026 09:16:03 -0600 Subject: [PATCH 085/222] Correct the inverted ArgumentException argument order at 51 call sites ArgumentException takes (message, paramName) while ArgumentOutOfRangeException takes (paramName, message). Fifty-one sites passed nameof(...) first, placing the identifier in Message and the sentence in ParamName. The message and parameter name text is unchanged; only the argument order moves. Add a source-scanning regression test that fails when the inverted shape reappears, plus ParamName assertions on five corrected sites. --- Numerics/Data/Interpolation/Polynomial.cs | 2 +- .../Interpolation/Support/Interpolater.cs | 10 +- Numerics/Data/Statistics/Histogram.cs | 2 +- Numerics/Data/Time Series/TimeSeries.cs | 4 +- .../Linear Algebra/Support/Vector.cs | 8 +- .../Constrained/AugmentedLagrange.cs | 2 +- Numerics/Sampling/MCMC/ARWMH.cs | 4 +- Numerics/Sampling/MCMC/Base/MCMCSampler.cs | 14 +- Numerics/Sampling/MCMC/DEMCz.cs | 8 +- Numerics/Sampling/MCMC/DEMCzs.cs | 10 +- Numerics/Sampling/MCMC/HMC.cs | 6 +- Numerics/Sampling/MCMC/NUTS.cs | 6 +- Numerics/Sampling/MCMC/RWMH.cs | 6 +- Numerics/Sampling/MCMC/SNIS.cs | 14 +- Numerics/Utilities/ExtensionMethods.cs | 6 +- .../Utilities/Test_ArgumentExceptionOrder.cs | 363 ++++++++++++++++++ 16 files changed, 414 insertions(+), 51 deletions(-) create mode 100644 Test_Numerics/Utilities/Test_ArgumentExceptionOrder.cs diff --git a/Numerics/Data/Interpolation/Polynomial.cs b/Numerics/Data/Interpolation/Polynomial.cs index ba14759a..171bdc99 100644 --- a/Numerics/Data/Interpolation/Polynomial.cs +++ b/Numerics/Data/Interpolation/Polynomial.cs @@ -42,7 +42,7 @@ public class Polynomial : Interpolater /// The sort order of the x-values, either ascending or descending. Default = Ascending. public Polynomial(int order, IList xValues, IList yValues, SortOrder sortOrder = SortOrder.Ascending) : base(xValues, yValues, sortOrder) { - if (order >= Count) throw new ArgumentException(nameof(order), "The order must be less than the length of the x value list."); + if (order >= Count) throw new ArgumentException("The order must be less than the length of the x value list.", nameof(order)); Order = order; } diff --git a/Numerics/Data/Interpolation/Support/Interpolater.cs b/Numerics/Data/Interpolation/Support/Interpolater.cs index eb499cb9..a18c4758 100644 --- a/Numerics/Data/Interpolation/Support/Interpolater.cs +++ b/Numerics/Data/Interpolation/Support/Interpolater.cs @@ -33,13 +33,13 @@ public Interpolater(IList xValues, IList yValues, SortOrder sort Count = xValues.Count; // Validate - if (yValues.Count != Count) throw new ArgumentException(nameof(xValues), "The x and y lists must be the same length."); - if (Count < 2) throw new ArgumentException(nameof(xValues), "The x list is too small. It must have at least 2 values."); + if (yValues.Count != Count) throw new ArgumentException("The x and y lists must be the same length.", nameof(xValues)); + if (Count < 2) throw new ArgumentException("The x list is too small. It must have at least 2 values.", nameof(xValues)); for (int i = 1; i < xValues.Count; ++i) { - if (xValues[i] == xValues[i - 1]) throw new ArgumentException(nameof(xValues), "All x values should be unique."); - if (sortOrder == SortOrder.Ascending && xValues[i] < xValues[i - 1]) throw new ArgumentException(nameof(xValues), "The x values are not in ascending order."); - if (sortOrder == SortOrder.Descending && xValues[i] > xValues[i - 1]) throw new ArgumentException(nameof(xValues), "The x values are not in descending order."); + if (xValues[i] == xValues[i - 1]) throw new ArgumentException("All x values should be unique.", nameof(xValues)); + if (sortOrder == SortOrder.Ascending && xValues[i] < xValues[i - 1]) throw new ArgumentException("The x values are not in ascending order.", nameof(xValues)); + if (sortOrder == SortOrder.Descending && xValues[i] > xValues[i - 1]) throw new ArgumentException("The x values are not in descending order.", nameof(xValues)); } this.XValues = xValues; this.YValues = yValues; diff --git a/Numerics/Data/Statistics/Histogram.cs b/Numerics/Data/Statistics/Histogram.cs index 52aa12b8..a274ed6b 100644 --- a/Numerics/Data/Statistics/Histogram.cs +++ b/Numerics/Data/Statistics/Histogram.cs @@ -94,7 +94,7 @@ public int CompareTo(Bin? other) if (other is null) return 1; if (UpperBound > other.LowerBound && LowerBound < other.LowerBound) { - throw new ArgumentException(nameof(other), "The bins cannot be overlapping."); + throw new ArgumentException("The bins cannot be overlapping.", nameof(other)); } if (UpperBound.Equals(other.UpperBound) && LowerBound.Equals(other.LowerBound)) { diff --git a/Numerics/Data/Time Series/TimeSeries.cs b/Numerics/Data/Time Series/TimeSeries.cs index b739b413..d6e9c4d9 100644 --- a/Numerics/Data/Time Series/TimeSeries.cs +++ b/Numerics/Data/Time Series/TimeSeries.cs @@ -953,7 +953,7 @@ private bool CheckIfMinStepsExceeded(DateTime startTime, DateTime endTime, int m public TimeSeries MovingAverage(int period, int? minValidCount = null) { if (period >= Count) - throw new ArgumentException(nameof(period), "The period must be less than the length of the time-series."); + throw new ArgumentException("The period must be less than the length of the time-series.", nameof(period)); int minCount = minValidCount ?? period; if (minCount < 1 || minCount > period) throw new ArgumentOutOfRangeException(nameof(minValidCount), "minValidCount must be between 1 and period."); @@ -996,7 +996,7 @@ public TimeSeries MovingAverage(int period, int? minValidCount = null) public TimeSeries MovingSum(int period, int? minValidCount = null) { if (period >= Count) - throw new ArgumentException(nameof(period), "The period must be less than the length of the time-series."); + throw new ArgumentException("The period must be less than the length of the time-series.", nameof(period)); int minCount = minValidCount ?? period; if (minCount < 1 || minCount > period) throw new ArgumentOutOfRangeException(nameof(minValidCount), "minValidCount must be between 1 and period."); diff --git a/Numerics/Mathematics/Linear Algebra/Support/Vector.cs b/Numerics/Mathematics/Linear Algebra/Support/Vector.cs index 6981b992..1ab21fc8 100644 --- a/Numerics/Mathematics/Linear Algebra/Support/Vector.cs +++ b/Numerics/Mathematics/Linear Algebra/Support/Vector.cs @@ -162,7 +162,7 @@ public double NormSquared() /// Right-side vector. public static double Distance(Vector A, Vector B) { - if (A.Length != B.Length) throw new ArgumentException(nameof(A.Length), "The vectors must be the same length."); + if (A.Length != B.Length) throw new ArgumentException("The vectors must be the same length.", nameof(A.Length)); double d = 0; for (int i = 0; i < A.Length; i++) { @@ -179,7 +179,7 @@ public static double Distance(Vector A, Vector B) /// Right-side vector. public static double DotProduct(Vector A, Vector B) { - if (A.Length != B.Length) throw new ArgumentException(nameof(A.Length), "The vectors must be the same length."); + if (A.Length != B.Length) throw new ArgumentException("The vectors must be the same length.", nameof(A.Length)); double sum = 0; for (int i = 0; i < A.Length; i++) sum += A[i] * B[i]; @@ -279,7 +279,7 @@ public Vector Multiply(Matrix matrix) /// The right-side vector. public Vector Multiply(Vector vector) { - if (Length != vector.Length) throw new ArgumentException(nameof(Length), "The vectors must be the same length."); + if (Length != vector.Length) throw new ArgumentException("The vectors must be the same length.", nameof(Length)); var result = new Vector(Length); for (int i = 0; i < Length; i++) result[i] = _vector[i] * vector[i]; @@ -292,7 +292,7 @@ public Vector Multiply(Vector vector) /// The right-side array. public Vector Multiply(double[] vector) { - if (Length != vector.Length) throw new ArgumentException(nameof(Length), "The vectors must be the same length."); + if (Length != vector.Length) throw new ArgumentException("The vectors must be the same length.", nameof(Length)); var result = new Vector(Length); for (int i = 0; i < Length; i++) result[i] = _vector[i] * vector[i]; diff --git a/Numerics/Mathematics/Optimization/Constrained/AugmentedLagrange.cs b/Numerics/Mathematics/Optimization/Constrained/AugmentedLagrange.cs index 0c31168f..6227fc26 100644 --- a/Numerics/Mathematics/Optimization/Constrained/AugmentedLagrange.cs +++ b/Numerics/Mathematics/Optimization/Constrained/AugmentedLagrange.cs @@ -50,7 +50,7 @@ public AugmentedLagrange(Func objectiveFunction, Optimizer opt _nu = new double[_constraints.Where((x) => x.Type == ConstraintType.GreaterThanOrEqualTo).ToArray().Length]; // Set up objective functions and optimizer - if (optimizer.GetType() == typeof(AugmentedLagrange)) throw new ArgumentException(nameof(optimizer), "The inner optimizer cannot also be an Augmented Lagrange optimizer."); + if (optimizer.GetType() == typeof(AugmentedLagrange)) throw new ArgumentException("The inner optimizer cannot also be an Augmented Lagrange optimizer.", nameof(optimizer)); _primaryObjectiveFunction = objectiveFunction; this.Optimizer = optimizer; this.Optimizer.ObjectiveFunction = augmentedLagrangianFunction; diff --git a/Numerics/Sampling/MCMC/ARWMH.cs b/Numerics/Sampling/MCMC/ARWMH.cs index e8608d26..15c4ab2a 100644 --- a/Numerics/Sampling/MCMC/ARWMH.cs +++ b/Numerics/Sampling/MCMC/ARWMH.cs @@ -81,8 +81,8 @@ public Matrix[] ProposalSigma /// protected override void ValidateCustomSettings() { - if (Scale <= 0) throw new ArgumentException(nameof(Scale), "The scale parameter must greater than 0."); - if (Beta < 0 || Beta > 1) throw new ArgumentException(nameof(Beta), "Beta must be between 0 and 1."); + if (Scale <= 0) throw new ArgumentException("The scale parameter must greater than 0.", nameof(Scale)); + if (Beta < 0 || Beta > 1) throw new ArgumentException("Beta must be between 0 and 1.", nameof(Beta)); } /// diff --git a/Numerics/Sampling/MCMC/Base/MCMCSampler.cs b/Numerics/Sampling/MCMC/Base/MCMCSampler.cs index 67140c96..d6055179 100644 --- a/Numerics/Sampling/MCMC/Base/MCMCSampler.cs +++ b/Numerics/Sampling/MCMC/Base/MCMCSampler.cs @@ -459,13 +459,13 @@ public double[] AcceptanceRates /// protected virtual void ValidateSettings() { - if (NumberOfChains < 1) throw new ArgumentException(nameof(NumberOfChains), "There must be at least 1 chain."); - if (Iterations < 100) throw new ArgumentException(nameof(Iterations), "The number of iterations cannot be less than 100."); - if (WarmupIterations < 1) throw new ArgumentException(nameof(WarmupIterations), "The number of warm up iterations cannot be less than 1."); - if (WarmupIterations > (int)(0.5 * Iterations)) throw new ArgumentException(nameof(WarmupIterations), "The number of warm up iterations cannot be greater than half the number of iterations."); - if (ThinningInterval < 1) throw new ArgumentException(nameof(ThinningInterval), "The thinning interval cannot be less than 1."); - if (InitialIterations < NumberOfChains) throw new ArgumentException(nameof(InitialIterations), "The initial population cannot be less than the number of chains."); - if (OutputLength < 100) throw new ArgumentException(nameof(OutputLength), "The output length must be at least 100."); + if (NumberOfChains < 1) throw new ArgumentException("There must be at least 1 chain.", nameof(NumberOfChains)); + if (Iterations < 100) throw new ArgumentException("The number of iterations cannot be less than 100.", nameof(Iterations)); + if (WarmupIterations < 1) throw new ArgumentException("The number of warm up iterations cannot be less than 1.", nameof(WarmupIterations)); + if (WarmupIterations > (int)(0.5 * Iterations)) throw new ArgumentException("The number of warm up iterations cannot be greater than half the number of iterations.", nameof(WarmupIterations)); + if (ThinningInterval < 1) throw new ArgumentException("The thinning interval cannot be less than 1.", nameof(ThinningInterval)); + if (InitialIterations < NumberOfChains) throw new ArgumentException("The initial population cannot be less than the number of chains.", nameof(InitialIterations)); + if (OutputLength < 100) throw new ArgumentException("The output length must be at least 100.", nameof(OutputLength)); ValidateCustomSettings(); } diff --git a/Numerics/Sampling/MCMC/DEMCz.cs b/Numerics/Sampling/MCMC/DEMCz.cs index a5d798a7..a3e3b190 100644 --- a/Numerics/Sampling/MCMC/DEMCz.cs +++ b/Numerics/Sampling/MCMC/DEMCz.cs @@ -79,10 +79,10 @@ public double Noise /// protected override void ValidateCustomSettings() { - if (NumberOfChains < 3) throw new ArgumentException(nameof(NumberOfChains), "There must be at least 3 chains."); - if (Jump <= 0 || Jump >= 2) throw new ArgumentException(nameof(Jump), "The jump parameter must be between 0 and 2."); - if (JumpThreshold < 0 || JumpThreshold >= 1) throw new ArgumentException(nameof(JumpThreshold), "The jump threshold must be between 0 and 1."); - if (Noise < 0) throw new ArgumentException(nameof(Noise), "The noise parameter must be greater than 0."); + if (NumberOfChains < 3) throw new ArgumentException("There must be at least 3 chains.", nameof(NumberOfChains)); + if (Jump <= 0 || Jump >= 2) throw new ArgumentException("The jump parameter must be between 0 and 2.", nameof(Jump)); + if (JumpThreshold < 0 || JumpThreshold >= 1) throw new ArgumentException("The jump threshold must be between 0 and 1.", nameof(JumpThreshold)); + if (Noise < 0) throw new ArgumentException("The noise parameter must be greater than 0.", nameof(Noise)); } diff --git a/Numerics/Sampling/MCMC/DEMCzs.cs b/Numerics/Sampling/MCMC/DEMCzs.cs index 993d314d..ce3484ad 100644 --- a/Numerics/Sampling/MCMC/DEMCzs.cs +++ b/Numerics/Sampling/MCMC/DEMCzs.cs @@ -85,11 +85,11 @@ public double Noise /// protected override void ValidateCustomSettings() { - if (NumberOfChains < 3) throw new ArgumentException(nameof(NumberOfChains), "There must be at least 3 chains."); - if (Jump <= 0 || Jump >= 2) throw new ArgumentException(nameof(Jump), "The jump parameter must be between 0 and 2."); - if (JumpThreshold < 0 || JumpThreshold > 1) throw new ArgumentException(nameof(JumpThreshold), "The jump threshold must be between 0 and 1."); - if (SnookerThreshold < 0 || SnookerThreshold > 0.5) throw new ArgumentException(nameof(SnookerThreshold), "The snooker threshold must be between 0 and 0.5."); - if (Noise < 0) throw new ArgumentException(nameof(Noise), "The noise parameter must be greater than 0."); + if (NumberOfChains < 3) throw new ArgumentException("There must be at least 3 chains.", nameof(NumberOfChains)); + if (Jump <= 0 || Jump >= 2) throw new ArgumentException("The jump parameter must be between 0 and 2.", nameof(Jump)); + if (JumpThreshold < 0 || JumpThreshold > 1) throw new ArgumentException("The jump threshold must be between 0 and 1.", nameof(JumpThreshold)); + if (SnookerThreshold < 0 || SnookerThreshold > 0.5) throw new ArgumentException("The snooker threshold must be between 0 and 0.5.", nameof(SnookerThreshold)); + if (Noise < 0) throw new ArgumentException("The noise parameter must be greater than 0.", nameof(Noise)); } /// diff --git a/Numerics/Sampling/MCMC/HMC.cs b/Numerics/Sampling/MCMC/HMC.cs index 31b624ee..4c16e314 100644 --- a/Numerics/Sampling/MCMC/HMC.cs +++ b/Numerics/Sampling/MCMC/HMC.cs @@ -158,9 +158,9 @@ public int Steps /// protected override void ValidateCustomSettings() { - if (Mass.Length != NumberOfParameters) throw new ArgumentException(nameof(Mass), "The mass vector must be the same length as the number of parameters."); - if (StepSize < 0) throw new ArgumentException(nameof(StepSize), "The leapfrog step size must be positive."); - if (Steps < 1) throw new ArgumentException(nameof(Steps), "The number of leapfrog steps must be at least one."); + if (Mass.Length != NumberOfParameters) throw new ArgumentException("The mass vector must be the same length as the number of parameters.", nameof(Mass)); + if (StepSize < 0) throw new ArgumentException("The leapfrog step size must be positive.", nameof(StepSize)); + if (Steps < 1) throw new ArgumentException("The number of leapfrog steps must be at least one.", nameof(Steps)); } /// diff --git a/Numerics/Sampling/MCMC/NUTS.cs b/Numerics/Sampling/MCMC/NUTS.cs index 7879466e..632f23f6 100644 --- a/Numerics/Sampling/MCMC/NUTS.cs +++ b/Numerics/Sampling/MCMC/NUTS.cs @@ -370,10 +370,10 @@ private double[] ComputeDiagnosticMeans(double[] sums) /// protected override void ValidateCustomSettings() { - if (Mass.Length != NumberOfParameters) throw new ArgumentException(nameof(Mass), "The mass vector must be the same length as the number of parameters."); + if (Mass.Length != NumberOfParameters) throw new ArgumentException("The mass vector must be the same length as the number of parameters.", nameof(Mass)); if (_initialStepSize <= 0) throw new ArgumentException("stepSize", "The leapfrog step size must be positive."); - if (MaxTreeDepth < 1) throw new ArgumentException(nameof(MaxTreeDepth), "The maximum tree depth must be at least 1."); - if (!Tools.IsFinite(TargetAcceptanceRate) || TargetAcceptanceRate <= 0d || TargetAcceptanceRate >= 1d) throw new ArgumentException(nameof(TargetAcceptanceRate), "The target acceptance rate must be greater than 0 and less than 1."); + if (MaxTreeDepth < 1) throw new ArgumentException("The maximum tree depth must be at least 1.", nameof(MaxTreeDepth)); + if (!Tools.IsFinite(TargetAcceptanceRate) || TargetAcceptanceRate <= 0d || TargetAcceptanceRate >= 1d) throw new ArgumentException("The target acceptance rate must be greater than 0 and less than 1.", nameof(TargetAcceptanceRate)); } /// diff --git a/Numerics/Sampling/MCMC/RWMH.cs b/Numerics/Sampling/MCMC/RWMH.cs index 6f590bab..a3f64016 100644 --- a/Numerics/Sampling/MCMC/RWMH.cs +++ b/Numerics/Sampling/MCMC/RWMH.cs @@ -49,9 +49,9 @@ public RWMH(List priorDistributions, LogLikelihood logL /// protected override void ValidateCustomSettings() { - if (ProposalSigma == null) throw new ArgumentException(nameof(ProposalSigma), "The proposal covariance matrix cannot be null."); - if (ProposalSigma.NumberOfRows != ProposalSigma.NumberOfColumns) throw new ArgumentException(nameof(ProposalSigma), "The proposal covariance matrix must be square."); - if (ProposalSigma.NumberOfRows != NumberOfParameters) throw new ArgumentException(nameof(ProposalSigma), "The proposal covariance matrix must have the same number of rows and columns as the number of parameters."); + if (ProposalSigma == null) throw new ArgumentException("The proposal covariance matrix cannot be null.", nameof(ProposalSigma)); + if (ProposalSigma.NumberOfRows != ProposalSigma.NumberOfColumns) throw new ArgumentException("The proposal covariance matrix must be square.", nameof(ProposalSigma)); + if (ProposalSigma.NumberOfRows != NumberOfParameters) throw new ArgumentException("The proposal covariance matrix must have the same number of rows and columns as the number of parameters.", nameof(ProposalSigma)); } /// diff --git a/Numerics/Sampling/MCMC/SNIS.cs b/Numerics/Sampling/MCMC/SNIS.cs index e378c533..e1bad703 100644 --- a/Numerics/Sampling/MCMC/SNIS.cs +++ b/Numerics/Sampling/MCMC/SNIS.cs @@ -69,14 +69,14 @@ protected override ParameterSet ChainIteration(int index, ParameterSet state) /// protected override void ValidateSettings() { - if (NumberOfChains != 1) throw new ArgumentException(nameof(InitialIterations), "There can only be 1 chain with this method."); - if (OutputLength < 100) throw new ArgumentException(nameof(OutputLength), "The output length must be at least 100."); - if (Iterations < OutputLength) throw new ArgumentException(nameof(Iterations), "The number of iterations cannot be less than the output length."); - if (WarmupIterations != 0) throw new ArgumentException(nameof(WarmupIterations), "There are no warmup iterations with this method."); - if (ThinningInterval != 1) throw new ArgumentException(nameof(ThinningInterval), "The thinning interval must be 1 for this method."); - if (InitialIterations != 1) throw new ArgumentException(nameof(InitialIterations), "The initial population must be 1 for this method."); + if (NumberOfChains != 1) throw new ArgumentException("There can only be 1 chain with this method.", nameof(InitialIterations)); + if (OutputLength < 100) throw new ArgumentException("The output length must be at least 100.", nameof(OutputLength)); + if (Iterations < OutputLength) throw new ArgumentException("The number of iterations cannot be less than the output length.", nameof(Iterations)); + if (WarmupIterations != 0) throw new ArgumentException("There are no warmup iterations with this method.", nameof(WarmupIterations)); + if (ThinningInterval != 1) throw new ArgumentException("The thinning interval must be 1 for this method.", nameof(ThinningInterval)); + if (InitialIterations != 1) throw new ArgumentException("The initial population must be 1 for this method.", nameof(InitialIterations)); if (mvn != null && mvn.ParametersValid == false) - throw new ArgumentException(nameof(MultivariateNormal), "The multivariate Normal importance distribution is invalid."); + throw new ArgumentException("The multivariate Normal importance distribution is invalid.", nameof(MultivariateNormal)); } /// diff --git a/Numerics/Utilities/ExtensionMethods.cs b/Numerics/Utilities/ExtensionMethods.cs index 042557f2..49d107b8 100644 --- a/Numerics/Utilities/ExtensionMethods.cs +++ b/Numerics/Utilities/ExtensionMethods.cs @@ -192,7 +192,7 @@ public static double[] Map(this double[] array, Func func) /// The array after addition. public static double[] Add(this double[] array, double[] values) { - if (array.Length != values.Length) throw new ArgumentException(nameof(array), "The arrays must be the same length."); + if (array.Length != values.Length) throw new ArgumentException("The arrays must be the same length.", nameof(array)); var result = new double[array.Length]; for (int i = 0; i < array.Length; i++) result[i] = array[i] + values[i]; @@ -207,7 +207,7 @@ public static double[] Add(this double[] array, double[] values) /// The array after subtraction. public static double[] Subtract(this double[] array, double[] values) { - if (array.Length != values.Length) throw new ArgumentException(nameof(array), "The arrays must be the same length."); + if (array.Length != values.Length) throw new ArgumentException("The arrays must be the same length.", nameof(array)); var result = new double[array.Length]; for (int i = 0; i < array.Length; i++) result[i] = array[i] - values[i]; @@ -250,7 +250,7 @@ public static double[] Divide(this double[] array, double scalar) /// The array after performing the dot product. public static double DotProduct(this double[] array, double[] values) { - if (array.Length != values.Length) throw new ArgumentException(nameof(array), "The arrays must be the same length."); + if (array.Length != values.Length) throw new ArgumentException("The arrays must be the same length.", nameof(array)); double result = 0.0; for (int i = 0; i < array.Length; i++) result += array[i] * values[i]; diff --git a/Test_Numerics/Utilities/Test_ArgumentExceptionOrder.cs b/Test_Numerics/Utilities/Test_ArgumentExceptionOrder.cs new file mode 100644 index 00000000..8e5655aa --- /dev/null +++ b/Test_Numerics/Utilities/Test_ArgumentExceptionOrder.cs @@ -0,0 +1,363 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics; +using Numerics.Data; +using Numerics.Mathematics.LinearAlgebra; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; + +namespace Utilities +{ + /// + /// Guards the argument order used when constructing in the Numerics library. + /// + /// + /// + /// Authors: + /// + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + /// + /// takes (message, paramName) while + /// takes (paramName, message). The two are easy to + /// confuse, and the inverted form compiles cleanly because both parameters are strings. When inverted, + /// the identifier lands in and the sentence lands in + /// , which makes diagnostics misleading and breaks any caller + /// that keys on . The defect propagated through the library by + /// copy-paste, so this class scans the library sources for the inverted shape rather than pinning a + /// handful of individual call sites. + /// + /// + [TestClass] + public class Test_ArgumentExceptionOrder + { + /// + /// Scans every Numerics source file and fails if any new ArgumentException(...) passes + /// nameof(...) as the first argument. + /// + /// + /// The scan masks comments, string literals and character literals before parsing so that text + /// inside documentation or literals cannot produce a match, and so that parentheses and commas + /// inside literals cannot confuse the argument split. Only two-argument constructions are + /// considered; the single-argument overload takes a message and is always correct. + /// + [TestMethod] + public void Test_NoInvertedArgumentExceptionArguments() + { + string sourceRoot = LocateNumericsSourceRoot(); + if (sourceRoot == null) Assert.Inconclusive("The Numerics source tree was not found next to the compiled test assembly, so the source scan could not run."); + + var offenders = new List(); + foreach (string file in EnumerateSourceFiles(sourceRoot)) + { + string text = File.ReadAllText(file); + string masked = MaskCommentsAndLiterals(text); + foreach (int start in FindConstructions(masked, "ArgumentException")) + { + int open = masked.IndexOf('(', start); + if (open < 0) continue; + int close = FindMatchingParenthesis(masked, open); + if (close < 0) continue; + + var arguments = SplitTopLevelArguments(masked.Substring(open + 1, close - open - 1)); + if (arguments.Count < 2) continue; + if (arguments[1].Trim().Length == 0) continue; + if (!arguments[0].TrimStart().StartsWith("nameof", StringComparison.Ordinal)) continue; + + int line = masked.Take(start).Count(c => c == '\n') + 1; + offenders.Add(file.Substring(sourceRoot.Length).TrimStart(Path.DirectorySeparatorChar) + ":" + line); + } + } + + Assert.IsEmpty(offenders, + "ArgumentException takes (message, paramName). These sites pass nameof(...) first, which puts the identifier in Message and the sentence in ParamName: " + + string.Join(", ", offenders)); + } + + /// + /// Verifies the corrected argument order on a representative site in . + /// + [TestMethod] + public void Test_ExtensionMethods_ReportsParameterName() + { + var exception = AssertThrows(() => new double[] { 1d, 2d }.Add(new double[] { 1d })); + Assert.AreEqual("array", exception.ParamName); + StringAssert.Contains(exception.Message, "The arrays must be the same length."); + } + + /// + /// Verifies the corrected argument order on a representative site in . + /// + [TestMethod] + public void Test_Vector_ReportsParameterName() + { + var a = new Vector(new[] { 1d, 2d }); + var b = new Vector(new[] { 1d }); + var exception = AssertThrows(() => Vector.DotProduct(a, b)); + Assert.AreEqual("Length", exception.ParamName); + StringAssert.Contains(exception.Message, "The vectors must be the same length."); + } + + /// + /// Verifies the corrected argument order on a representative site in . + /// + [TestMethod] + public void Test_Interpolater_ReportsParameterName() + { + var exception = AssertThrows(() => new Linear(new[] { 1d, 2d, 3d }, new[] { 1d, 2d })); + Assert.AreEqual("xValues", exception.ParamName); + StringAssert.Contains(exception.Message, "The x and y lists must be the same length."); + } + + /// + /// Verifies the corrected argument order on a representative site in . + /// + [TestMethod] + public void Test_Polynomial_ReportsParameterName() + { + var exception = AssertThrows(() => new Polynomial(3, new[] { 1d, 2d, 3d }, new[] { 1d, 2d, 3d })); + Assert.AreEqual("order", exception.ParamName); + StringAssert.Contains(exception.Message, "The order must be less than the length of the x value list."); + } + + /// + /// Verifies the corrected argument order on a representative site in . + /// + [TestMethod] + public void Test_TimeSeries_ReportsParameterName() + { + var series = new TimeSeries(TimeInterval.OneDay, new DateTime(2000, 1, 1), new[] { 1d, 2d, 3d }); + var exception = AssertThrows(() => series.MovingAverage(3)); + Assert.AreEqual("period", exception.ParamName); + StringAssert.Contains(exception.Message, "The period must be less than the length of the time-series."); + } + + #region Helpers + + /// + /// Invokes an action and returns the exception of the expected type, failing the test otherwise. + /// + /// The expected exception type. + /// The action expected to throw. + /// The thrown exception. + private static T AssertThrows(Action action) where T : Exception + { + try + { + action(); + } + catch (T expected) + { + return expected; + } + catch (Exception unexpected) + { + throw new AssertFailedException("Expected " + typeof(T).Name + " but caught " + unexpected.GetType().Name + ".", unexpected); + } + + throw new AssertFailedException("Expected " + typeof(T).Name + " but no exception was thrown."); + } + + /// + /// Locates the Numerics library source directory relative to this source file. + /// + /// Supplied by the compiler; the full path of this source file. + /// The Numerics project directory, or when the source tree is unavailable. + private static string LocateNumericsSourceRoot([CallerFilePath] string callerFilePath = "") + { + if (string.IsNullOrEmpty(callerFilePath)) return null; + + var directory = new DirectoryInfo(Path.GetDirectoryName(callerFilePath)); + while (directory != null) + { + string candidate = Path.Combine(directory.FullName, "Numerics"); + if (File.Exists(Path.Combine(candidate, "Numerics.csproj"))) return candidate; + directory = directory.Parent; + } + + return null; + } + + /// + /// Enumerates the C# sources of the library, skipping build output directories. + /// + /// The Numerics project directory. + /// The full paths of the library sources. + private static IEnumerable EnumerateSourceFiles(string sourceRoot) + { + string separator = Path.DirectorySeparatorChar.ToString(); + return Directory.GetFiles(sourceRoot, "*.cs", SearchOption.AllDirectories) + .Where(f => !f.Contains(separator + "bin" + separator) && !f.Contains(separator + "obj" + separator)) + .OrderBy(f => f, StringComparer.Ordinal); + } + + /// + /// Replaces the contents of comments, string literals and character literals with spaces. + /// + /// The source text. + /// Text of identical length whose comment and literal contents cannot be mistaken for code. + /// + /// Delimiters are preserved so that an empty literal remains distinguishable from a missing argument, + /// and the length is preserved so that offsets and line numbers still refer to the original text. + /// + private static string MaskCommentsAndLiterals(string text) + { + var masked = new StringBuilder(text); + int i = 0; + while (i < text.Length) + { + char c = text[i]; + + if (c == '/' && i + 1 < text.Length && text[i + 1] == '/') + { + while (i < text.Length && text[i] != '\n') + { + if (text[i] != '\r') masked[i] = ' '; + i++; + } + continue; + } + + if (c == '/' && i + 1 < text.Length && text[i + 1] == '*') + { + masked[i] = ' '; + masked[i + 1] = ' '; + i += 2; + while (i < text.Length && !(text[i] == '*' && i + 1 < text.Length && text[i + 1] == '/')) + { + if (text[i] != '\n' && text[i] != '\r') masked[i] = ' '; + i++; + } + if (i < text.Length) + { + masked[i] = ' '; + masked[i + 1] = ' '; + i += 2; + } + continue; + } + + if (c == '@' && i + 1 < text.Length && text[i + 1] == '"') + { + i += 2; + while (i < text.Length) + { + if (text[i] == '"') + { + if (i + 1 < text.Length && text[i + 1] == '"') + { + masked[i] = ' '; + masked[i + 1] = ' '; + i += 2; + continue; + } + i++; + break; + } + if (text[i] != '\n' && text[i] != '\r') masked[i] = ' '; + i++; + } + continue; + } + + if (c == '"' || c == '\'') + { + char quote = c; + i++; + while (i < text.Length && text[i] != quote) + { + if (text[i] == '\\' && i + 1 < text.Length) + { + masked[i] = ' '; + masked[i + 1] = ' '; + i += 2; + continue; + } + if (text[i] != '\n' && text[i] != '\r') masked[i] = ' '; + i++; + } + i++; + continue; + } + + i++; + } + + return masked.ToString(); + } + + /// + /// Finds the start offset of every new {typeName} construction in masked source text. + /// + /// Masked source text. + /// The exception type name to search for. + /// The offsets at which each construction begins. + private static IEnumerable FindConstructions(string masked, string typeName) + { + string token = "new " + typeName; + int index = 0; + while ((index = masked.IndexOf(token, index, StringComparison.Ordinal)) >= 0) + { + int after = index + token.Length; + while (after < masked.Length && char.IsWhiteSpace(masked[after])) after++; + if (after < masked.Length && masked[after] == '(') yield return index; + index += token.Length; + } + } + + /// + /// Finds the offset of the parenthesis matching the one at . + /// + /// Masked source text. + /// The offset of the opening parenthesis. + /// The offset of the closing parenthesis, or -1 when unbalanced. + private static int FindMatchingParenthesis(string masked, int open) + { + int depth = 0; + for (int i = open; i < masked.Length; i++) + { + if (masked[i] == '(') depth++; + else if (masked[i] == ')') + { + depth--; + if (depth == 0) return i; + } + } + return -1; + } + + /// + /// Splits an argument list on its top-level commas. + /// + /// The text between the parentheses of a construction. + /// The individual arguments, in order. + private static List SplitTopLevelArguments(string arguments) + { + var parts = new List(); + var current = new StringBuilder(); + int depth = 0; + + foreach (char c in arguments) + { + if (c == '(' || c == '[') depth++; + else if (c == ')' || c == ']') depth--; + else if (c == ',' && depth == 0) + { + parts.Add(current.ToString()); + current.Clear(); + continue; + } + current.Append(c); + } + + parts.Add(current.ToString()); + return parts; + } + + #endregion + } +} From 0d499659104979d18233c622c232220947cbae38 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Tue, 25 Aug 2026 09:35:50 -0600 Subject: [PATCH 086/222] Pass the parameter bounds to the finite-difference gradient in BFGS, ADAM and GradientDescent With no bounds, ClampInPlace is inert and AvailableLeft/AvailableRight return positive infinity, so a gradient taken on a bound perturbs outside the feasible region. MultiStart and MLSL build their child BFGS over the parent's Evaluate delegate, so those probes were scored by the parent and could be reported as the parent's solution. MLSL returned 512.00513 for a bound of 512 on the Eggholder benchmark; it now returns 512. Matches the form already used by NUTS and HMC. Bounds are only consulted when no analytic gradient is supplied, so callers that pass one are unaffected. Add bounds probe tests for all three solvers and feasible-region assertions to the MultiStart and MLSL Eggholder tests. --- .../Mathematics/Optimization/Local/ADAM.cs | 2 +- .../Mathematics/Optimization/Local/BFGS.cs | 8 ++-- .../Optimization/Local/GradientDescent.cs | 2 +- .../Optimization/Global/Test_MLSL.cs | 10 +++++ .../Optimization/Global/Test_MultiStart.cs | 10 +++++ .../Optimization/Local/Test_Adam.cs | 37 +++++++++++++++++++ .../Optimization/Local/Test_BFGS.cs | 37 +++++++++++++++++++ .../Local/Test_GradientDescent.cs | 37 +++++++++++++++++++ 8 files changed, 137 insertions(+), 6 deletions(-) diff --git a/Numerics/Mathematics/Optimization/Local/ADAM.cs b/Numerics/Mathematics/Optimization/Local/ADAM.cs index 88d89a0d..7ad52295 100644 --- a/Numerics/Mathematics/Optimization/Local/ADAM.cs +++ b/Numerics/Mathematics/Optimization/Local/ADAM.cs @@ -123,7 +123,7 @@ protected override void Optimize() while (Iterations < MaxIterations) { // Get gradient with respect to objective function - g = Gradient != null ? Gradient(p) : NumericalDerivative.Gradient((x) => Evaluate(x, ref cancel), p); + g = Gradient != null ? Gradient(p) : NumericalDerivative.Gradient((x) => Evaluate(x, ref cancel), p, LowerBounds, UpperBounds); if (cancel) return; // Update parameters diff --git a/Numerics/Mathematics/Optimization/Local/BFGS.cs b/Numerics/Mathematics/Optimization/Local/BFGS.cs index fef05289..a7c73f43 100644 --- a/Numerics/Mathematics/Optimization/Local/BFGS.cs +++ b/Numerics/Mathematics/Optimization/Local/BFGS.cs @@ -127,7 +127,7 @@ protected override void Optimize() // Calculate the starting function value and gradient, and initialize the inverse Hessian to the unit matrix. double fp = Evaluate(p, ref cancel); - var g = Gradient != null ? Gradient(p) : NumericalDerivative.Gradient(x => Evaluate(x, ref cancel), p); + var g = Gradient != null ? Gradient(p) : NumericalDerivative.Gradient(x => Evaluate(x, ref cancel), p, LowerBounds, UpperBounds); var dg = new double[D]; var hdg = new double[D]; var xi = new double[D]; @@ -170,7 +170,7 @@ protected override void Optimize() // Save the old gradient, and get the new gradient. for (int i = 0; i < D; i++) dg[i] = g[i]; - g = Gradient != null ? Gradient(p) : NumericalDerivative.Gradient((x) => Evaluate(x, ref cancel), p); + g = Gradient != null ? Gradient(p) : NumericalDerivative.Gradient((x) => Evaluate(x, ref cancel), p, LowerBounds, UpperBounds); if (cancel) return; // Compute difference of gradients. @@ -357,7 +357,7 @@ private void LineSearch(double[] x0, double f0, double[] g0, double[] p, double[ } bool cancelFlag = cancel; - g = Gradient != null ? Gradient(xTemp) : NumericalDerivative.Gradient(x => Evaluate(x, ref cancelFlag), xTemp); + g = Gradient != null ? Gradient(xTemp) : NumericalDerivative.Gradient(x => Evaluate(x, ref cancelFlag), xTemp, LowerBounds, UpperBounds); cancel = cancelFlag; if (cancel) return; @@ -420,7 +420,7 @@ private void Zoom(double[] x0, double f0, double slope0, double[] p, double alph else { bool cancelFlag = cancel; - g = Gradient != null ? Gradient(xTemp) : NumericalDerivative.Gradient(x => Evaluate(x, ref cancelFlag), xTemp); + g = Gradient != null ? Gradient(xTemp) : NumericalDerivative.Gradient(x => Evaluate(x, ref cancelFlag), xTemp, LowerBounds, UpperBounds); cancel = cancelFlag; if (cancel) return; diff --git a/Numerics/Mathematics/Optimization/Local/GradientDescent.cs b/Numerics/Mathematics/Optimization/Local/GradientDescent.cs index c87ab1ad..c7c3a6c4 100644 --- a/Numerics/Mathematics/Optimization/Local/GradientDescent.cs +++ b/Numerics/Mathematics/Optimization/Local/GradientDescent.cs @@ -111,7 +111,7 @@ protected override void Optimize() while (Iterations < MaxIterations) { // Get gradient with respect to objective function - g = Gradient != null ? Gradient(p0) : NumericalDerivative.Gradient((x) => Evaluate(x, ref cancel), p0); + g = Gradient != null ? Gradient(p0) : NumericalDerivative.Gradient((x) => Evaluate(x, ref cancel), p0, LowerBounds, UpperBounds); if (cancel) return; // Update parameters diff --git a/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs b/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs index 31a59faa..31dd8bb9 100644 --- a/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs +++ b/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs @@ -308,6 +308,16 @@ public void Test_Eggholder() var validY = 404.2319d; Assert.AreEqual(x, validX, 1E-1); Assert.AreEqual(y, validY, 1E-1); + + // The optimum sits on the upper bound, so the local solver's finite-difference gradient is + // taken at a point on that bound. The local solver scores its probes through this solver's + // objective delegate, so an unclamped perturbation can be reported as the solution. Guard the + // declared feasible region explicitly rather than relying on the tolerances above. + for (int i = 0; i < solution.Length; i++) + { + Assert.IsGreaterThanOrEqualTo(lower[i], solution[i], "Parameter " + i + " is below its lower bound."); + Assert.IsLessThanOrEqualTo(upper[i], solution[i], "Parameter " + i + " is above its upper bound."); + } } /// diff --git a/Test_Numerics/Mathematics/Optimization/Global/Test_MultiStart.cs b/Test_Numerics/Mathematics/Optimization/Global/Test_MultiStart.cs index f02bd6f4..67945879 100644 --- a/Test_Numerics/Mathematics/Optimization/Global/Test_MultiStart.cs +++ b/Test_Numerics/Mathematics/Optimization/Global/Test_MultiStart.cs @@ -306,6 +306,16 @@ public void Test_Eggholder() var validY = 404.2319d; Assert.AreEqual(x, validX, 1E-2); Assert.AreEqual(y, validY, 1E-1); + + // The optimum sits on the upper bound, so the local solver's finite-difference gradient is + // taken at a point on that bound. The local solver scores its probes through this solver's + // objective delegate, so an unclamped perturbation can be reported as the solution. Guard the + // declared feasible region explicitly rather than relying on the tolerances above. + for (int i = 0; i < solution.Length; i++) + { + Assert.IsGreaterThanOrEqualTo(lower[i], solution[i], "Parameter " + i + " is below its lower bound."); + Assert.IsLessThanOrEqualTo(upper[i], solution[i], "Parameter " + i + " is above its upper bound."); + } } /// diff --git a/Test_Numerics/Mathematics/Optimization/Local/Test_Adam.cs b/Test_Numerics/Mathematics/Optimization/Local/Test_Adam.cs index 0a6e3ce7..7c4dbf81 100644 --- a/Test_Numerics/Mathematics/Optimization/Local/Test_Adam.cs +++ b/Test_Numerics/Mathematics/Optimization/Local/Test_Adam.cs @@ -167,5 +167,42 @@ public void Test_McCormick() Assert.AreEqual(y, validY, 1E-2); } + /// + /// The finite-difference gradient must not evaluate the objective outside the declared bounds. + /// + /// + /// + /// The unconstrained minimum of this objective is at (2, -2), so the solver parks on the corner + /// (1, 0) and every subsequent gradient is taken at a point on both bounds. A perturbation that is + /// not clamped evaluates the objective where it may be undefined, and the resulting point can + /// displace the incumbent. + /// + /// + /// The end-of-run Hessian pass in is a separate finite-difference + /// path that is not given the bounds, so it is switched off here to keep this test scoped to the + /// gradient. + /// + /// + [TestMethod] + public void Test_GradientDoesNotProbeOutsideBounds() + { + var initial = new double[] { 0.5d, 0.5d }; + var lower = new double[] { 0d, 0d }; + var upper = new double[] { 1d, 1d }; + var violations = new List(); + + var solver = new ADAM(x => + { + for (int i = 0; i < x.Length; i++) + if (x[i] < lower[i] || x[i] > upper[i]) violations.Add("p" + i + "=" + x[i]); + return Math.Pow(x[0] - 2d, 2d) + Math.Pow(x[1] + 2d, 2d); + }, 2, initial, lower, upper) { ComputeHessian = false }; + solver.Minimize(); + + Assert.IsEmpty(violations, "The objective was evaluated outside the declared bounds: " + string.Join(", ", violations)); + Assert.AreEqual(1d, solver.BestParameterSet.Values[0], 1E-6); + Assert.AreEqual(0d, solver.BestParameterSet.Values[1], 1E-6); + } + } } diff --git a/Test_Numerics/Mathematics/Optimization/Local/Test_BFGS.cs b/Test_Numerics/Mathematics/Optimization/Local/Test_BFGS.cs index e12546a1..e77d07cd 100644 --- a/Test_Numerics/Mathematics/Optimization/Local/Test_BFGS.cs +++ b/Test_Numerics/Mathematics/Optimization/Local/Test_BFGS.cs @@ -188,5 +188,42 @@ public void Test_Beale() Assert.AreEqual(y, validY, 1E-4); } + /// + /// The finite-difference gradient must not evaluate the objective outside the declared bounds. + /// + /// + /// + /// The unconstrained minimum of this objective is at (2, -2), so the solver parks on the corner + /// (1, 0) and every subsequent gradient is taken at a point on both bounds. A perturbation that is + /// not clamped evaluates the objective where it may be undefined, and the resulting point can + /// displace the incumbent. + /// + /// + /// The end-of-run Hessian pass in is a separate finite-difference + /// path that is not given the bounds, so it is switched off here to keep this test scoped to the + /// gradient. + /// + /// + [TestMethod] + public void Test_GradientDoesNotProbeOutsideBounds() + { + var initial = new double[] { 0.5d, 0.5d }; + var lower = new double[] { 0d, 0d }; + var upper = new double[] { 1d, 1d }; + var violations = new List(); + + var solver = new BFGS(x => + { + for (int i = 0; i < x.Length; i++) + if (x[i] < lower[i] || x[i] > upper[i]) violations.Add("p" + i + "=" + x[i]); + return Math.Pow(x[0] - 2d, 2d) + Math.Pow(x[1] + 2d, 2d); + }, 2, initial, lower, upper) { ComputeHessian = false }; + solver.Minimize(); + + Assert.IsEmpty(violations, "The objective was evaluated outside the declared bounds: " + string.Join(", ", violations)); + Assert.AreEqual(1d, solver.BestParameterSet.Values[0], 1E-6); + Assert.AreEqual(0d, solver.BestParameterSet.Values[1], 1E-6); + } + } } diff --git a/Test_Numerics/Mathematics/Optimization/Local/Test_GradientDescent.cs b/Test_Numerics/Mathematics/Optimization/Local/Test_GradientDescent.cs index a97f15e5..f5160fca 100644 --- a/Test_Numerics/Mathematics/Optimization/Local/Test_GradientDescent.cs +++ b/Test_Numerics/Mathematics/Optimization/Local/Test_GradientDescent.cs @@ -170,5 +170,42 @@ public void Test_McCormick() Assert.AreEqual(x, validX, 1E-2); Assert.AreEqual(y, validY, 1E-2); } + + /// + /// The finite-difference gradient must not evaluate the objective outside the declared bounds. + /// + /// + /// + /// The unconstrained minimum of this objective is at (2, -2), so the solver parks on the corner + /// (1, 0) and every subsequent gradient is taken at a point on both bounds. A perturbation that is + /// not clamped evaluates the objective where it may be undefined, and the resulting point can + /// displace the incumbent. + /// + /// + /// The end-of-run Hessian pass in is a separate finite-difference + /// path that is not given the bounds, so it is switched off here to keep this test scoped to the + /// gradient. + /// + /// + [TestMethod] + public void Test_GradientDoesNotProbeOutsideBounds() + { + var initial = new double[] { 0.5d, 0.5d }; + var lower = new double[] { 0d, 0d }; + var upper = new double[] { 1d, 1d }; + var violations = new List(); + + var solver = new GradientDescent(x => + { + for (int i = 0; i < x.Length; i++) + if (x[i] < lower[i] || x[i] > upper[i]) violations.Add("p" + i + "=" + x[i]); + return Math.Pow(x[0] - 2d, 2d) + Math.Pow(x[1] + 2d, 2d); + }, 2, initial, lower, upper) { ComputeHessian = false }; + solver.Minimize(); + + Assert.IsEmpty(violations, "The objective was evaluated outside the declared bounds: " + string.Join(", ", violations)); + Assert.AreEqual(1d, solver.BestParameterSet.Values[0], 1E-6); + Assert.AreEqual(0d, solver.BestParameterSet.Values[1], 1E-6); + } } } From ed51f14811d0004dd872c9e5dfc0e2463cb0b7ad Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Tue, 25 Aug 2026 11:39:34 -0600 Subject: [PATCH 087/222] Pass the parameter bounds to the end-of-run Hessian in Optimizer Optimizer.Minimize and Optimizer.Maximize differenced the objective about the reported solution without the bounds, so a solution on a bound was probed outside the feasible region. Because MultiStart and MLSL build their local solvers over the parent's Evaluate delegate, and leave ComputeHessian at its default, an infeasible Hessian probe could be recorded as the parent's BestParameterSet. Add protected virtual ParameterLowerBounds/ParameterUpperBounds to Optimizer, returning null for an unbounded problem, and override them in the eleven solvers that declare bounds. The distinct name avoids hiding the public LowerBounds/UpperBounds properties, so no public signature changes. Restore the MultiStart and MLSL bounds regression tests that were withdrawn because of this defect, re-enable the Hessian pass in the three gradient probe tests, and add direct coverage of the Hessian pass over both Minimize and Maximize. --- .../Global/DifferentialEvolution.cs | 6 + .../Mathematics/Optimization/Global/MLSL.cs | 6 + .../Optimization/Global/MultiStart.cs | 6 + .../Optimization/Global/ParticleSwarm.cs | 6 + .../Global/ShuffledComplexEvolution.cs | 6 + .../Optimization/Global/SimulatedAnnealing.cs | 6 + .../Mathematics/Optimization/Local/ADAM.cs | 6 + .../Mathematics/Optimization/Local/BFGS.cs | 6 + .../Optimization/Local/GradientDescent.cs | 6 + .../Optimization/Local/NelderMead.cs | 6 + .../Mathematics/Optimization/Local/Powell.cs | 6 + .../Optimization/Support/Optimizer.cs | 31 ++- .../Optimization/Global/Test_MLSL.cs | 48 +++++ .../Optimization/Global/Test_MultiStart.cs | 53 +++++ .../Optimization/Local/Test_Adam.cs | 8 +- .../Optimization/Local/Test_BFGS.cs | 8 +- .../Local/Test_GradientDescent.cs | 8 +- .../Support/Test_OptimizerHessianBounds.cs | 190 ++++++++++++++++++ 18 files changed, 398 insertions(+), 14 deletions(-) create mode 100644 Test_Numerics/Mathematics/Optimization/Support/Test_OptimizerHessianBounds.cs diff --git a/Numerics/Mathematics/Optimization/Global/DifferentialEvolution.cs b/Numerics/Mathematics/Optimization/Global/DifferentialEvolution.cs index 5e0be11d..d0e682cc 100644 --- a/Numerics/Mathematics/Optimization/Global/DifferentialEvolution.cs +++ b/Numerics/Mathematics/Optimization/Global/DifferentialEvolution.cs @@ -77,6 +77,12 @@ public DifferentialEvolution(Func objectiveFunction, int numbe /// public double[] UpperBounds { get; private set; } + /// + protected override double[]? ParameterLowerBounds => LowerBounds; + + /// + protected override double[]? ParameterUpperBounds => UpperBounds; + /// /// The total population size. Default = 10 * D (Storn & Price, 1997). /// diff --git a/Numerics/Mathematics/Optimization/Global/MLSL.cs b/Numerics/Mathematics/Optimization/Global/MLSL.cs index eb8216c9..d6066075 100644 --- a/Numerics/Mathematics/Optimization/Global/MLSL.cs +++ b/Numerics/Mathematics/Optimization/Global/MLSL.cs @@ -102,6 +102,12 @@ public MLSL(Func objectiveFunction, int numberOfParameters, IL /// public double[] UpperBounds { get; private set; } = null!; + /// + protected override double[]? ParameterLowerBounds => LowerBounds; + + /// + protected override double[]? ParameterUpperBounds => UpperBounds; + /// /// The pseudo random number generator (PRNG) seed. /// diff --git a/Numerics/Mathematics/Optimization/Global/MultiStart.cs b/Numerics/Mathematics/Optimization/Global/MultiStart.cs index b6c1fa35..7284893c 100644 --- a/Numerics/Mathematics/Optimization/Global/MultiStart.cs +++ b/Numerics/Mathematics/Optimization/Global/MultiStart.cs @@ -103,6 +103,12 @@ public MultiStart(Func objectiveFunction, int numberOfParamete /// public double[] UpperBounds { get; private set; } + /// + protected override double[]? ParameterLowerBounds => LowerBounds; + + /// + protected override double[]? ParameterUpperBounds => UpperBounds; + /// /// The pseudo random number generator (PRNG) seed. /// diff --git a/Numerics/Mathematics/Optimization/Global/ParticleSwarm.cs b/Numerics/Mathematics/Optimization/Global/ParticleSwarm.cs index ace43134..d2270cfa 100644 --- a/Numerics/Mathematics/Optimization/Global/ParticleSwarm.cs +++ b/Numerics/Mathematics/Optimization/Global/ParticleSwarm.cs @@ -72,6 +72,12 @@ public ParticleSwarm(Func objectiveFunction, int numberOfParam /// public double[] UpperBounds { get; private set; } = null!; + /// + protected override double[]? ParameterLowerBounds => LowerBounds; + + /// + protected override double[]? ParameterUpperBounds => UpperBounds; + /// /// The total population size. Default = 30. /// diff --git a/Numerics/Mathematics/Optimization/Global/ShuffledComplexEvolution.cs b/Numerics/Mathematics/Optimization/Global/ShuffledComplexEvolution.cs index 17e2ede6..49f082a0 100644 --- a/Numerics/Mathematics/Optimization/Global/ShuffledComplexEvolution.cs +++ b/Numerics/Mathematics/Optimization/Global/ShuffledComplexEvolution.cs @@ -94,6 +94,12 @@ public ShuffledComplexEvolution(Func objectiveFunction, int nu /// public double[] UpperBounds { get; private set; } + /// + protected override double[]? ParameterLowerBounds => LowerBounds; + + /// + protected override double[]? ParameterUpperBounds => UpperBounds; + /// /// The pseudo random number generator (PRNG) seed. /// diff --git a/Numerics/Mathematics/Optimization/Global/SimulatedAnnealing.cs b/Numerics/Mathematics/Optimization/Global/SimulatedAnnealing.cs index 1e894ad9..7bee0669 100644 --- a/Numerics/Mathematics/Optimization/Global/SimulatedAnnealing.cs +++ b/Numerics/Mathematics/Optimization/Global/SimulatedAnnealing.cs @@ -75,6 +75,12 @@ public SimulatedAnnealing(Func objectiveFunction, int numberOf /// public double[] UpperBounds { get; private set; } + /// + protected override double[]? ParameterLowerBounds => LowerBounds; + + /// + protected override double[]? ParameterUpperBounds => UpperBounds; + /// /// The pseudo random number generator (PRNG) seed. /// diff --git a/Numerics/Mathematics/Optimization/Local/ADAM.cs b/Numerics/Mathematics/Optimization/Local/ADAM.cs index 7ad52295..e39c0c06 100644 --- a/Numerics/Mathematics/Optimization/Local/ADAM.cs +++ b/Numerics/Mathematics/Optimization/Local/ADAM.cs @@ -87,6 +87,12 @@ public ADAM(Func objectiveFunction, int numberOfParameters, /// public double[] UpperBounds { get; private set; } + /// + protected override double[]? ParameterLowerBounds => LowerBounds; + + /// + protected override double[]? ParameterUpperBounds => UpperBounds; + /// /// Gets and sets the step size, or learning rate. Default = 0.001. /// diff --git a/Numerics/Mathematics/Optimization/Local/BFGS.cs b/Numerics/Mathematics/Optimization/Local/BFGS.cs index a7c73f43..c268ad66 100644 --- a/Numerics/Mathematics/Optimization/Local/BFGS.cs +++ b/Numerics/Mathematics/Optimization/Local/BFGS.cs @@ -88,6 +88,12 @@ public BFGS(Func objectiveFunction, int numberOfParameters, /// public double[] UpperBounds { get; private set; } + /// + protected override double[]? ParameterLowerBounds => LowerBounds; + + /// + protected override double[]? ParameterUpperBounds => UpperBounds; + /// /// The function for evaluating the gradient of the objective function. /// diff --git a/Numerics/Mathematics/Optimization/Local/GradientDescent.cs b/Numerics/Mathematics/Optimization/Local/GradientDescent.cs index c7c3a6c4..42f267a2 100644 --- a/Numerics/Mathematics/Optimization/Local/GradientDescent.cs +++ b/Numerics/Mathematics/Optimization/Local/GradientDescent.cs @@ -88,6 +88,12 @@ public GradientDescent(Func objectiveFunction, int numberOfPar /// public double[] UpperBounds { get; private set; } + /// + protected override double[]? ParameterLowerBounds => LowerBounds; + + /// + protected override double[]? ParameterUpperBounds => UpperBounds; + /// /// Gets and sets the step size, or learning rate. Default = 0.001. /// diff --git a/Numerics/Mathematics/Optimization/Local/NelderMead.cs b/Numerics/Mathematics/Optimization/Local/NelderMead.cs index 0c50d043..5c9f4341 100644 --- a/Numerics/Mathematics/Optimization/Local/NelderMead.cs +++ b/Numerics/Mathematics/Optimization/Local/NelderMead.cs @@ -93,6 +93,12 @@ public NelderMead(Func objectiveFunction, int numberOfParamete /// public double[] UpperBounds { get; private set; } + /// + protected override double[]? ParameterLowerBounds => LowerBounds; + + /// + protected override double[]? ParameterUpperBounds => UpperBounds; + /// /// The relative size of the initial simplex. Typical values range from 0.01 to 0.1. /// diff --git a/Numerics/Mathematics/Optimization/Local/Powell.cs b/Numerics/Mathematics/Optimization/Local/Powell.cs index ef694dba..ad55bb81 100644 --- a/Numerics/Mathematics/Optimization/Local/Powell.cs +++ b/Numerics/Mathematics/Optimization/Local/Powell.cs @@ -82,6 +82,12 @@ public Powell(Func objectiveFunction, int numberOfParameters, /// public double[] UpperBounds { get; private set; } + /// + protected override double[]? ParameterLowerBounds => LowerBounds; + + /// + protected override double[]? ParameterUpperBounds => UpperBounds; + /// protected override void Optimize() { diff --git a/Numerics/Mathematics/Optimization/Support/Optimizer.cs b/Numerics/Mathematics/Optimization/Support/Optimizer.cs index 794eb517..af50cf8f 100644 --- a/Numerics/Mathematics/Optimization/Support/Optimizer.cs +++ b/Numerics/Mathematics/Optimization/Support/Optimizer.cs @@ -89,6 +89,33 @@ public Func ObjectiveFunction /// protected int functionScale = 1; + /// + /// The inclusive lower bounds of the parameter space, or null when the search is unbounded. + /// + /// + /// + /// The base class returns null, which is correct for an unconstrained problem. A derived class that + /// constrains its search to a box overrides this to expose those bounds, so that the finite-difference + /// Hessian computed at the end of a successful or keeps + /// its perturbations inside the feasible region. + /// + /// + /// This is deliberately separate from the public bounds properties declared by the individual solvers. + /// Those properties are declared independently on each derived class, so a member of the same name on + /// this base class would be hidden by every one of them. The array returned here must have one entry + /// per parameter, in the same order as . + /// + /// + protected virtual double[]? ParameterLowerBounds => null; + + /// + /// The inclusive upper bounds of the parameter space, or null when the search is unbounded. + /// + /// + /// See for the rationale and the ordering contract. + /// + protected virtual double[]? ParameterUpperBounds => null; + #endregion @@ -168,7 +195,7 @@ public virtual void Minimize() Optimize(); if (Status == OptimizationStatus.Success && ComputeHessian) { - Hessian = new Matrix(NumericalDerivative.Hessian((x) => { return ObjectiveFunction(x); }, BestParameterSet.Values)); + Hessian = new Matrix(NumericalDerivative.Hessian((x) => { return ObjectiveFunction(x); }, BestParameterSet.Values, ParameterLowerBounds!, ParameterUpperBounds!)); } } catch (ArgumentException ex) @@ -196,7 +223,7 @@ public virtual void Maximize() Optimize(); if (Status == OptimizationStatus.Success && ComputeHessian) { - Hessian = new Matrix(NumericalDerivative.Hessian((x) => { return ObjectiveFunction(x); }, BestParameterSet.Values)); + Hessian = new Matrix(NumericalDerivative.Hessian((x) => { return ObjectiveFunction(x); }, BestParameterSet.Values, ParameterLowerBounds!, ParameterUpperBounds!)); } } catch (ArgumentException ex) diff --git a/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs b/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs index 31dd8bb9..a7ceb5b5 100644 --- a/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs +++ b/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs @@ -463,5 +463,53 @@ public void Test_SampledPointsKeepSameListInstance() Assert.HasCount(1, instances); Assert.IsTrue(ReferenceEquals(instances[0], solver.SampledPoints), "The sampled point list instance must not be replaced during a run."); } + + /// + /// Test that the reported solution lies inside the declared bounds when the unconstrained optimum + /// lies outside them. + /// + /// + /// + /// The objective is a smooth quadratic centred at (20, 20) restricted to the unit square, so the + /// constrained solution is the corner (1, 1) and every finite difference taken there steps outside + /// the feasible region unless it is clamped. + /// + /// + /// The local solvers are built over this solver's own objective delegate, so their probes are scored + /// through and can be recorded as the incumbent. Both finite + /// difference paths matter: the local gradient and the local solver's end-of-run Hessian. The + /// objective falls monotonically toward the centre, so any unclamped probe scores better than the + /// corner and would be reported as the solution. + /// + /// + /// is deliberately left at its default of true. Switching it + /// off would hide the Hessian path, which is the half of this guard that a bounded gradient alone + /// does not cover. + /// + /// + [TestMethod] + public void Test_SolutionStaysWithinBounds() + { + var initial = new double[] { 0.5d, 0.5d }; + var lower = new double[] { 0d, 0d }; + var upper = new double[] { 1d, 1d }; + var solver = new MLSL(x => Math.Pow(x[0] - 20d, 2d) + Math.Pow(x[1] - 20d, 2d), 2, initial, lower, upper) + { + ReportFailure = false + }; + solver.Minimize(); + + var solution = solver.BestParameterSet.Values; + Assert.IsNotNull(solution); + for (int i = 0; i < solution.Length; i++) + { + Assert.IsGreaterThanOrEqualTo(lower[i], solution[i], "Parameter " + i + " is below its lower bound."); + Assert.IsLessThanOrEqualTo(upper[i], solution[i], "Parameter " + i + " is above its upper bound."); + } + + // The constrained minimum is the corner nearest the unconstrained optimum. + Assert.AreEqual(1d, solution[0], 1E-6); + Assert.AreEqual(1d, solution[1], 1E-6); + } } } diff --git a/Test_Numerics/Mathematics/Optimization/Global/Test_MultiStart.cs b/Test_Numerics/Mathematics/Optimization/Global/Test_MultiStart.cs index 67945879..ed931a74 100644 --- a/Test_Numerics/Mathematics/Optimization/Global/Test_MultiStart.cs +++ b/Test_Numerics/Mathematics/Optimization/Global/Test_MultiStart.cs @@ -342,5 +342,58 @@ public void Test_TP2() bool match2 = Math.Abs(x - validY) < 1E-4 && Math.Abs(y - validX) < 1E-4; Assert.IsTrue(match1 || match2); } + + /// + /// Test that the reported solution lies inside the declared bounds when the unconstrained optimum + /// lies outside them. + /// + /// + /// + /// The objective is a smooth quadratic centred at (20, 20) restricted to the unit square, so the + /// constrained solution is the corner (1, 1) and every finite difference taken there steps outside + /// the feasible region unless it is clamped. + /// + /// + /// The local solvers are built over this solver's own objective delegate, so their probes are scored + /// through and can be recorded as the incumbent. Both finite + /// difference paths matter: the local gradient and the local solver's end-of-run Hessian. The + /// objective falls monotonically toward the centre, so any unclamped probe scores better than the + /// corner and would be reported as the solution. + /// + /// + /// is deliberately left at its default of true. Switching it + /// off would hide the Hessian path, which is the half of this guard that a bounded gradient alone + /// does not cover. + /// + /// + [TestMethod] + public void Test_SolutionStaysWithinBounds() + { + var initial = new double[] { 0.5d, 0.5d }; + var lower = new double[] { 0d, 0d }; + var upper = new double[] { 1d, 1d }; + var solver = new MultiStart(x => Math.Pow(x[0] - 20d, 2d) + Math.Pow(x[1] - 20d, 2d), 2, initial, lower, upper) + { + ReportFailure = false, + // Every local start converges on the same corner, so a handful of starts exercises the + // guarded path as thoroughly as the default hundred and keeps the test cheap. + MaxIterations = 10, + // Bookkeeping only, and it dominates the runtime of this test. + RecordTraces = false + }; + solver.Minimize(); + + var solution = solver.BestParameterSet.Values; + Assert.IsNotNull(solution); + for (int i = 0; i < solution.Length; i++) + { + Assert.IsGreaterThanOrEqualTo(lower[i], solution[i], "Parameter " + i + " is below its lower bound."); + Assert.IsLessThanOrEqualTo(upper[i], solution[i], "Parameter " + i + " is above its upper bound."); + } + + // The constrained minimum is the corner nearest the unconstrained optimum. + Assert.AreEqual(1d, solution[0], 1E-6); + Assert.AreEqual(1d, solution[1], 1E-6); + } } } diff --git a/Test_Numerics/Mathematics/Optimization/Local/Test_Adam.cs b/Test_Numerics/Mathematics/Optimization/Local/Test_Adam.cs index 7c4dbf81..4ec6a858 100644 --- a/Test_Numerics/Mathematics/Optimization/Local/Test_Adam.cs +++ b/Test_Numerics/Mathematics/Optimization/Local/Test_Adam.cs @@ -178,9 +178,9 @@ public void Test_McCormick() /// displace the incumbent. /// /// - /// The end-of-run Hessian pass in is a separate finite-difference - /// path that is not given the bounds, so it is switched off here to keep this test scoped to the - /// gradient. + /// The end-of-run Hessian pass in is a second finite-difference + /// path over the same objective at the same corner. It is left enabled, at its default, so this + /// test covers both paths rather than only the gradient. /// /// [TestMethod] @@ -196,7 +196,7 @@ public void Test_GradientDoesNotProbeOutsideBounds() for (int i = 0; i < x.Length; i++) if (x[i] < lower[i] || x[i] > upper[i]) violations.Add("p" + i + "=" + x[i]); return Math.Pow(x[0] - 2d, 2d) + Math.Pow(x[1] + 2d, 2d); - }, 2, initial, lower, upper) { ComputeHessian = false }; + }, 2, initial, lower, upper); solver.Minimize(); Assert.IsEmpty(violations, "The objective was evaluated outside the declared bounds: " + string.Join(", ", violations)); diff --git a/Test_Numerics/Mathematics/Optimization/Local/Test_BFGS.cs b/Test_Numerics/Mathematics/Optimization/Local/Test_BFGS.cs index e77d07cd..20afb27f 100644 --- a/Test_Numerics/Mathematics/Optimization/Local/Test_BFGS.cs +++ b/Test_Numerics/Mathematics/Optimization/Local/Test_BFGS.cs @@ -199,9 +199,9 @@ public void Test_Beale() /// displace the incumbent. /// /// - /// The end-of-run Hessian pass in is a separate finite-difference - /// path that is not given the bounds, so it is switched off here to keep this test scoped to the - /// gradient. + /// The end-of-run Hessian pass in is a second finite-difference + /// path over the same objective at the same corner. It is left enabled, at its default, so this + /// test covers both paths rather than only the gradient. /// /// [TestMethod] @@ -217,7 +217,7 @@ public void Test_GradientDoesNotProbeOutsideBounds() for (int i = 0; i < x.Length; i++) if (x[i] < lower[i] || x[i] > upper[i]) violations.Add("p" + i + "=" + x[i]); return Math.Pow(x[0] - 2d, 2d) + Math.Pow(x[1] + 2d, 2d); - }, 2, initial, lower, upper) { ComputeHessian = false }; + }, 2, initial, lower, upper); solver.Minimize(); Assert.IsEmpty(violations, "The objective was evaluated outside the declared bounds: " + string.Join(", ", violations)); diff --git a/Test_Numerics/Mathematics/Optimization/Local/Test_GradientDescent.cs b/Test_Numerics/Mathematics/Optimization/Local/Test_GradientDescent.cs index f5160fca..40272d6c 100644 --- a/Test_Numerics/Mathematics/Optimization/Local/Test_GradientDescent.cs +++ b/Test_Numerics/Mathematics/Optimization/Local/Test_GradientDescent.cs @@ -182,9 +182,9 @@ public void Test_McCormick() /// displace the incumbent. /// /// - /// The end-of-run Hessian pass in is a separate finite-difference - /// path that is not given the bounds, so it is switched off here to keep this test scoped to the - /// gradient. + /// The end-of-run Hessian pass in is a second finite-difference + /// path over the same objective at the same corner. It is left enabled, at its default, so this + /// test covers both paths rather than only the gradient. /// /// [TestMethod] @@ -200,7 +200,7 @@ public void Test_GradientDoesNotProbeOutsideBounds() for (int i = 0; i < x.Length; i++) if (x[i] < lower[i] || x[i] > upper[i]) violations.Add("p" + i + "=" + x[i]); return Math.Pow(x[0] - 2d, 2d) + Math.Pow(x[1] + 2d, 2d); - }, 2, initial, lower, upper) { ComputeHessian = false }; + }, 2, initial, lower, upper); solver.Minimize(); Assert.IsEmpty(violations, "The objective was evaluated outside the declared bounds: " + string.Join(", ", violations)); diff --git a/Test_Numerics/Mathematics/Optimization/Support/Test_OptimizerHessianBounds.cs b/Test_Numerics/Mathematics/Optimization/Support/Test_OptimizerHessianBounds.cs new file mode 100644 index 00000000..b2075e7f --- /dev/null +++ b/Test_Numerics/Mathematics/Optimization/Support/Test_OptimizerHessianBounds.cs @@ -0,0 +1,190 @@ +using System; +using System.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Mathematics.Optimization; + +namespace Mathematics.Optimization +{ + /// + /// Unit tests for the end-of-run finite-difference Hessian computed by . + /// + /// + /// + /// Authors: + /// + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + /// + /// A successful or differences the + /// objective function about the reported solution. When the solution lies on a bound, an unclamped + /// perturbation evaluates the objective outside the declared feasible region, where it may be + /// undefined. These tests isolate that pass: the solver below performs no search of its own, so every + /// objective evaluation after the first is a Hessian probe. + /// + /// + [TestClass] + public class Test_OptimizerHessianBounds + { + /// + /// An optimizer that reports a caller-supplied solution and performs no search. + /// + /// + /// This exists so the end-of-run Hessian pass can be observed on its own. A real solver interleaves + /// its own finite-difference gradients and its own iterate repair with the Hessian pass, which makes + /// it impossible to attribute an out-of-bounds evaluation to one path or the other. + /// + private sealed class FixedSolutionOptimizer : Optimizer + { + private readonly double[] _solution; + + /// + /// Construct a solver that reports as a successful result. + /// + /// The objective function to evaluate. + /// The parameter set to report. + /// The inclusive lower bounds, or null for an unbounded problem. + /// The inclusive upper bounds, or null for an unbounded problem. + public FixedSolutionOptimizer(Func objectiveFunction, double[] solution, double[] lowerBounds, double[] upperBounds) + : base(objectiveFunction, solution.Length) + { + _solution = solution; + LowerBounds = lowerBounds; + UpperBounds = upperBounds; + } + + /// + /// An array of lower bounds (inclusive) of the interval containing the optimal point. + /// + public double[] LowerBounds { get; } + + /// + /// An array of upper bounds (inclusive) of the interval containing the optimal point. + /// + public double[] UpperBounds { get; } + + /// + protected override double[] ParameterLowerBounds => LowerBounds; + + /// + protected override double[] ParameterUpperBounds => UpperBounds; + + /// + protected override void Optimize() + { + bool cancel = false; + Evaluate((double[])_solution.Clone(), ref cancel); + UpdateStatus(OptimizationStatus.Success); + } + } + + /// + /// A separable quadratic whose exact Hessian is the constant diagonal matrix diag(2a, 2b). + /// + private static Func Quadratic(double a, double b, List violations, double[] lower, double[] upper) + { + return x => + { + if (violations != null) + { + for (int i = 0; i < x.Length; i++) + { + if (x[i] < lower[i] || x[i] > upper[i]) + violations.Add("p" + i + "=" + x[i].ToString("R")); + } + } + return a * x[0] * x[0] + b * x[1] * x[1]; + }; + } + + /// + /// Verifies that the end-of-run Hessian never evaluates the objective outside the declared bounds + /// when the reported solution sits on a corner of the feasible region. + /// + /// + /// Both parameters are on a bound, so an unclamped central difference steps outside in every + /// coordinate and in all four off-diagonal stencil directions. + /// + [TestMethod] + public void Test_HessianDoesNotProbeOutsideBounds() + { + var lower = new double[] { 0d, 0d }; + var upper = new double[] { 1d, 1d }; + var violations = new List(); + var solver = new FixedSolutionOptimizer(Quadratic(3d, 5d, violations, lower, upper), new double[] { 1d, 0d }, lower, upper); + + solver.Minimize(); + + Assert.AreEqual(OptimizationStatus.Success, solver.Status); + Assert.IsNotNull(solver.Hessian, "The Hessian was not computed."); + Assert.IsEmpty(violations, "The objective was evaluated outside the declared bounds: " + string.Join(", ", violations)); + } + + /// + /// Verifies that clamping the Hessian probes into the feasible region still recovers the exact + /// curvature of a quadratic, so the guard does not cost accuracy. + /// + /// + /// A one-sided second difference is exact for a quadratic up to floating point rounding, so the + /// tolerance here only absorbs cancellation in the difference quotient. + /// + [TestMethod] + public void Test_BoundedHessianRecoversQuadraticCurvature() + { + var lower = new double[] { 0d, 0d }; + var upper = new double[] { 1d, 1d }; + var solver = new FixedSolutionOptimizer(Quadratic(3d, 5d, null, lower, upper), new double[] { 1d, 0d }, lower, upper); + + solver.Minimize(); + + Assert.IsNotNull(solver.Hessian); + Assert.AreEqual(6d, solver.Hessian[0, 0], 1E-4); + Assert.AreEqual(10d, solver.Hessian[1, 1], 1E-4); + Assert.AreEqual(0d, solver.Hessian[0, 1], 1E-4); + Assert.AreEqual(0d, solver.Hessian[1, 0], 1E-4); + } + + /// + /// Verifies that an optimizer which declares no bounds still computes its Hessian. + /// + /// + /// returns null bounds by default, which is the correct description of an + /// unconstrained problem. The finite-difference routine treats null as unlimited room, so the + /// unbounded result is a plain central difference. + /// + [TestMethod] + public void Test_UnboundedOptimizerComputesHessian() + { + var solver = new FixedSolutionOptimizer(x => 3d * x[0] * x[0] + 5d * x[1] * x[1], new double[] { 0.5d, 0.25d }, null, null); + + solver.Minimize(); + + Assert.AreEqual(OptimizationStatus.Success, solver.Status); + Assert.IsNotNull(solver.Hessian); + Assert.AreEqual(6d, solver.Hessian[0, 0], 1E-4); + Assert.AreEqual(10d, solver.Hessian[1, 1], 1E-4); + } + + /// + /// Verifies that the maximization path guards its Hessian probes as well as the minimization path. + /// + /// + /// carries its own copy of the end-of-run Hessian block, so it + /// needs its own coverage; the two are easy to change out of step. + /// + [TestMethod] + public void Test_MaximizeHessianDoesNotProbeOutsideBounds() + { + var lower = new double[] { 0d, 0d }; + var upper = new double[] { 1d, 1d }; + var violations = new List(); + var solver = new FixedSolutionOptimizer(Quadratic(-3d, -5d, violations, lower, upper), new double[] { 1d, 1d }, lower, upper); + + solver.Maximize(); + + Assert.AreEqual(OptimizationStatus.Success, solver.Status); + Assert.IsNotNull(solver.Hessian, "The Hessian was not computed."); + Assert.IsEmpty(violations, "The objective was evaluated outside the declared bounds: " + string.Join(", ", violations)); + } + } +} From edca9a1e25ea3b5180994510a0b93e34a2fe5981 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Tue, 25 Aug 2026 14:14:29 -0600 Subject: [PATCH 088/222] Correct the last inverted ArgumentException site and widen the guard NUTS.cs:374 swapped ArgumentException(message, paramName) to (paramName, message), the one site the 51-site sweep in 4b78bce missed because its first argument was a plain string literal rather than a nameof(...) expression. Widen Test_ArgumentExceptionOrder's scanner to also flag a two-argument ArgumentException whose first argument is a quoted bare identifier (e.g. "stepSize"), which is the shape that hid this site. Also: - Anchor the nameof check on the whole trimmed argument instead of its prefix, so a message built as nameof(Foo) + " is invalid" is no longer a false positive. - Match "new ArgumentException(" with a regex instead of a literal token, so extra whitespace, a line break, or a namespace-qualified type name are still found. - Require char literals to have a well-formed single-character or escape-sequence body before treating a quote as their close, so a stray apostrophe (e.g. in a preprocessor directive) cannot mask everything after it. - Fail hard instead of Assert.Inconclusive when the source tree cannot be located, since a guard that silently stands down defeats its purpose. Add a representative-site test for NUTS mirroring the pattern already used for the other fixed sites in this file. --- Numerics/Sampling/MCMC/NUTS.cs | 2 +- .../Utilities/Test_ArgumentExceptionOrder.cs | 171 ++++++++++++++++-- 2 files changed, 155 insertions(+), 18 deletions(-) diff --git a/Numerics/Sampling/MCMC/NUTS.cs b/Numerics/Sampling/MCMC/NUTS.cs index 632f23f6..7a805fb4 100644 --- a/Numerics/Sampling/MCMC/NUTS.cs +++ b/Numerics/Sampling/MCMC/NUTS.cs @@ -371,7 +371,7 @@ private double[] ComputeDiagnosticMeans(double[] sums) protected override void ValidateCustomSettings() { if (Mass.Length != NumberOfParameters) throw new ArgumentException("The mass vector must be the same length as the number of parameters.", nameof(Mass)); - if (_initialStepSize <= 0) throw new ArgumentException("stepSize", "The leapfrog step size must be positive."); + if (_initialStepSize <= 0) throw new ArgumentException("The leapfrog step size must be positive.", "stepSize"); if (MaxTreeDepth < 1) throw new ArgumentException("The maximum tree depth must be at least 1.", nameof(MaxTreeDepth)); if (!Tools.IsFinite(TargetAcceptanceRate) || TargetAcceptanceRate <= 0d || TargetAcceptanceRate >= 1d) throw new ArgumentException("The target acceptance rate must be greater than 0 and less than 1.", nameof(TargetAcceptanceRate)); } diff --git a/Test_Numerics/Utilities/Test_ArgumentExceptionOrder.cs b/Test_Numerics/Utilities/Test_ArgumentExceptionOrder.cs index 8e5655aa..c53b615f 100644 --- a/Test_Numerics/Utilities/Test_ArgumentExceptionOrder.cs +++ b/Test_Numerics/Utilities/Test_ArgumentExceptionOrder.cs @@ -1,13 +1,16 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics; using Numerics.Data; +using Numerics.Distributions; using Numerics.Mathematics.LinearAlgebra; +using Numerics.Sampling.MCMC; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Text; +using System.Text.RegularExpressions; namespace Utilities { @@ -36,8 +39,9 @@ namespace Utilities public class Test_ArgumentExceptionOrder { /// - /// Scans every Numerics source file and fails if any new ArgumentException(...) passes - /// nameof(...) as the first argument. + /// Scans every Numerics source file and fails if any new ArgumentException(...) has its + /// arguments inverted: either nameof(...) passed as the first argument, or a quoted bare + /// identifier (for example "stepSize") passed as the first argument. /// /// /// The scan masks comments, string literals and character literals before parsing so that text @@ -49,7 +53,10 @@ public class Test_ArgumentExceptionOrder public void Test_NoInvertedArgumentExceptionArguments() { string sourceRoot = LocateNumericsSourceRoot(); - if (sourceRoot == null) Assert.Inconclusive("The Numerics source tree was not found next to the compiled test assembly, so the source scan could not run."); + if (sourceRoot == null) + Assert.Fail("The Numerics source tree was not found next to the compiled test assembly, so this guard could not scan the library sources. " + + "This fails hard rather than reporting Inconclusive: a guard that silently stands down when its own lookup breaks (for example, because the " + + "checkout was renamed or moved) could let an inverted ArgumentException ship undetected, which defeats its purpose."); var offenders = new List(); foreach (string file in EnumerateSourceFiles(sourceRoot)) @@ -66,7 +73,14 @@ public void Test_NoInvertedArgumentExceptionArguments() var arguments = SplitTopLevelArguments(masked.Substring(open + 1, close - open - 1)); if (arguments.Count < 2) continue; if (arguments[1].Trim().Length == 0) continue; - if (!arguments[0].TrimStart().StartsWith("nameof", StringComparison.Ordinal)) continue; + + // The first split argument always starts at the opening parenthesis, in both the masked + // and the original text (masking preserves length and delimiters), so this slice recovers + // the un-masked source for the first argument alone. + string firstArgumentOriginal = text.Substring(open + 1, arguments[0].Length); + + bool inverted = IsNameofExpression(arguments[0]) || IsBareIdentifierLiteral(firstArgumentOriginal); + if (!inverted) continue; int line = masked.Take(start).Count(c => c == '\n') + 1; offenders.Add(file.Substring(sourceRoot.Length).TrimStart(Path.DirectorySeparatorChar) + ":" + line); @@ -74,10 +88,54 @@ public void Test_NoInvertedArgumentExceptionArguments() } Assert.IsEmpty(offenders, - "ArgumentException takes (message, paramName). These sites pass nameof(...) first, which puts the identifier in Message and the sentence in ParamName: " + "ArgumentException takes (message, paramName). These sites pass the parameter name first, which puts it in Message and the sentence in ParamName: " + string.Join(", ", offenders)); } + /// + /// Determines whether an argument, taken in its entirety once trimmed, is a single nameof(...) + /// expression. + /// + /// A masked, comma-split constructor argument. + /// when the whole trimmed argument is exactly one nameof(...) call. + /// + /// Anchoring on the whole argument, rather than only its prefix, avoids flagging a message built as + /// nameof(Foo) + " is invalid": that argument starts with nameof but, once the trailing + /// concatenation is accounted for, is not itself a nameof expression. + /// + private static bool IsNameofExpression(string argument) + { + string trimmed = argument.Trim(); + if (!trimmed.StartsWith("nameof", StringComparison.Ordinal)) return false; + + int i = "nameof".Length; + while (i < trimmed.Length && char.IsWhiteSpace(trimmed[i])) i++; + if (i >= trimmed.Length || trimmed[i] != '(') return false; + + int close = FindMatchingParenthesis(trimmed, i); + return close == trimmed.Length - 1; + } + + /// + /// Determines whether an argument's original (unmasked) source text is a quoted, bare identifier, + /// such as "stepSize". + /// + /// The corresponding argument text taken from the unmasked source. + /// when the argument is a string literal containing a single identifier + /// with no spaces or sentence punctuation. + /// + /// A genuine message is a sentence: it has spaces and terminal punctuation. A quoted bare identifier in + /// the message slot is almost certainly a parameter name that was written as a string literal instead + /// of passed as nameof(...), or otherwise placed in the wrong argument position. This mirrors + /// the shape of new ArgumentException("stepSize", "The leapfrog step size must be positive."), + /// which the nameof-prefix check alone cannot see because its first argument is not a + /// nameof expression at all. + /// + private static bool IsBareIdentifierLiteral(string originalArgument) + { + return Regex.IsMatch(originalArgument.Trim(), "^\"[A-Za-z_][A-Za-z0-9_]*\"$", RegexOptions.None); + } + /// /// Verifies the corrected argument order on a representative site in . /// @@ -136,6 +194,23 @@ public void Test_TimeSeries_ReportsParameterName() StringAssert.Contains(exception.Message, "The period must be less than the length of the time-series."); } + /// + /// Verifies the corrected argument order on the stepSize validation site in , + /// which the nameof-prefix scan could not see because its first argument was the string literal + /// "stepSize" rather than a nameof(...) expression. + /// + [TestMethod] + public void Test_NUTS_ReportsParameterName() + { + var priors = new List { new Uniform(-50d, 50d), new Uniform(-50d, 50d) }; + static double logLH(double[] x) => -0.5d * (x[0] * x[0] + x[1] * x[1]); + var sampler = new NUTS(priors, logLH, stepSize: 0d); + + var exception = AssertThrows(() => sampler.Sample()); + Assert.AreEqual("stepSize", exception.ParamName); + StringAssert.Contains(exception.Message, "The leapfrog step size must be positive."); + } + #region Helpers /// @@ -264,11 +339,10 @@ private static string MaskCommentsAndLiterals(string text) continue; } - if (c == '"' || c == '\'') + if (c == '"') { - char quote = c; i++; - while (i < text.Length && text[i] != quote) + while (i < text.Length && text[i] != '"') { if (text[i] == '\\' && i + 1 < text.Length) { @@ -284,29 +358,92 @@ private static string MaskCommentsAndLiterals(string text) continue; } + if (c == '\'') + { + int closeQuote = FindCharLiteralClose(text, i); + if (closeQuote < 0) + { + // Not a valid char literal (for example, an apostrophe inside a preprocessor directive + // or other non-literal text). Leave it as ordinary text rather than masking everything + // up to the next unrelated quote character. + i++; + continue; + } + + for (int k = i + 1; k < closeQuote; k++) + { + if (text[k] != '\n' && text[k] != '\r') masked[k] = ' '; + } + i = closeQuote + 1; + continue; + } + i++; } return masked.ToString(); } + /// + /// Finds the offset of the closing quote of the char literal that opens at , + /// if the text at that position is actually a well-formed char literal. + /// + /// The source text. + /// The offset of the opening '. + /// The offset of the matching closing ', or -1 when the content is not a valid char + /// literal (most commonly a stray apostrophe that is not part of any literal). + /// + /// A well-formed char literal contains exactly one ordinary character, or one backslash escape + /// (\n, \t, \\, \', and so on, or a \xH..H, \uHHHH, or + /// \UHHHHHHHH hexadecimal escape), and never spans a line. Requiring this exact shape, rather + /// than treating any later ' as the terminator, keeps an unrelated apostrophe (for example, one + /// inside a preprocessor directive, which is not itself masked as a comment or string) from being + /// mistaken for the start of a char literal and masking everything up to the next unrelated quote. + /// + private static int FindCharLiteralClose(string text, int openQuote) + { + int i = openQuote + 1; + if (i >= text.Length || text[i] == '\n' || text[i] == '\r') return -1; + + if (text[i] == '\\') + { + i++; + if (i >= text.Length || text[i] == '\n' || text[i] == '\r') return -1; + char escape = text[i]; + i++; + + int maxDigits = escape == 'U' ? 8 : escape == 'u' || escape == 'x' ? 4 : 0; + int digits = 0; + while (digits < maxDigits && i < text.Length && Uri.IsHexDigit(text[i])) + { + i++; + digits++; + } + } + else + { + i++; + } + + return i < text.Length && text[i] == '\'' ? i : -1; + } + /// /// Finds the start offset of every new {typeName} construction in masked source text. /// /// Masked source text. /// The exception type name to search for. /// The offsets at which each construction begins. + /// + /// Matches on a regular expression rather than the literal token "new " + typeName so that + /// unusual but legal spacing (extra spaces, a line break between new and the type) and a + /// namespace-qualified type name (for example System.ArgumentException) are still found. + /// private static IEnumerable FindConstructions(string masked, string typeName) { - string token = "new " + typeName; - int index = 0; - while ((index = masked.IndexOf(token, index, StringComparison.Ordinal)) >= 0) - { - int after = index + token.Length; - while (after < masked.Length && char.IsWhiteSpace(masked[after])) after++; - if (after < masked.Length && masked[after] == '(') yield return index; - index += token.Length; - } + string pattern = @"\bnew\s+(?:[A-Za-z_][A-Za-z0-9_]*\.)*" + Regex.Escape(typeName) + @"\s*\("; + foreach (Match match in Regex.Matches(masked, pattern)) + yield return match.Index; } /// From 48b80ac154b096071d054fb4e3e0cd84154df35e Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Tue, 25 Aug 2026 14:26:03 -0600 Subject: [PATCH 089/222] Stop MultiStart and MLSL from mutating their own InitialValues MultiStart.Optimize() re-pointed its working array at the InitialValues property on the first iteration instead of copying into it. Every later uniform draw and the local solver's bounds repair then wrote straight through that alias into the property's own array, so after a run InitialValues no longer matched what the constructor had recorded. MLSL had the same exposure at two sites: it stored InitialValues by reference into a ParameterSet, and handed it directly to the local solver's in-place bounds repair. MLSL does not have MultiStart's random-draw overwrite, so this did not move any value in the existing test suite (the constructor already requires InitialValues to be in-bounds, so the repair is a no-op, and BFGS, the default local method, copies its own initial point rather than mutating the array handed to it) but the aliasing is still a genuine ownership defect and is fixed the same way. Fix both by operating on an owned copy: MultiStart copies into its already-allocated scratch array instead of re-pointing at the property; MLSL clones InitialValues once per run and uses that clone for the initial evaluation, its ParameterSet, and the local optimizer. The repair logic in GetLocalOptimizer is unchanged, and this does not touch MLSL.cs:308 or ShuffledComplexEvolution.cs, where the same ownership problem exists but is out of scope for this change. A before/after capture across every existing MultiStart and MLSL test scenario (best parameters, fitness, Iterations, FunctionEvaluations, Status) is bit-identical; only the InitialValues readback changed. Add a test per class confirming InitialValues still equals what was passed to the constructor after a run, and that the caller's own array is unmodified. --- .../Mathematics/Optimization/Global/MLSL.cs | 10 ++++++-- .../Optimization/Global/MultiStart.cs | 6 ++++- .../Optimization/Global/Test_MLSL.cs | 24 +++++++++++++++++++ .../Optimization/Global/Test_MultiStart.cs | 24 +++++++++++++++++++ 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/Numerics/Mathematics/Optimization/Global/MLSL.cs b/Numerics/Mathematics/Optimization/Global/MLSL.cs index d6066075..91fbf953 100644 --- a/Numerics/Mathematics/Optimization/Global/MLSL.cs +++ b/Numerics/Mathematics/Optimization/Global/MLSL.cs @@ -211,10 +211,16 @@ protected override void Optimize() { // On the first iteration, add the user-defined initial starting points // This can often be very close to the true minimum - SampledPoints.Add(new SamplePoint { ParameterSet = new ParameterSet(InitialValues, Evaluate(InitialValues, ref cancel)), Minimized = true }); + // + // Evaluate and minimize from a copy of InitialValues rather than the array itself. + // ParameterSet stores its array by reference, and GetLocalOptimizer repairs its + // argument in place, so operating directly on InitialValues would let either path + // silently corrupt the public InitialValues array. + var initial = InitialValues.ToArray(); + SampledPoints.Add(new SamplePoint { ParameterSet = new ParameterSet(initial, Evaluate(initial, ref cancel)), Minimized = true }); // Perform local minimizations from initial values - solver = GetLocalOptimizer(InitialValues, LocalRelativeTolerance, LocalAbsoluteTolerance, ref cancel); + solver = GetLocalOptimizer(initial, LocalRelativeTolerance, LocalAbsoluteTolerance, ref cancel); solver.Minimize(); if (cancel) return; diff --git a/Numerics/Mathematics/Optimization/Global/MultiStart.cs b/Numerics/Mathematics/Optimization/Global/MultiStart.cs index 7284893c..352eb733 100644 --- a/Numerics/Mathematics/Optimization/Global/MultiStart.cs +++ b/Numerics/Mathematics/Optimization/Global/MultiStart.cs @@ -155,7 +155,11 @@ protected override void Optimize() { if (Iterations == 0) { - values = InitialValues; + // Copy into the already-allocated array rather than re-pointing values at + // InitialValues. Re-pointing would alias the public InitialValues array, and the + // uniform draws below and the bounds repair inside GetLocalOptimizer both write + // through values in place, which would silently corrupt InitialValues. + Array.Copy(InitialValues, values, D); } else { diff --git a/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs b/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs index a7ceb5b5..641cb091 100644 --- a/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs +++ b/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs @@ -511,5 +511,29 @@ public void Test_SolutionStaysWithinBounds() Assert.AreEqual(1d, solution[0], 1E-6); Assert.AreEqual(1d, solution[1], 1E-6); } + + /// + /// Test that the public property still equals what was passed to + /// the constructor after a run, and that the caller's own array is left untouched. + /// + /// + /// On the first iteration, itself was stored by reference into a + /// and handed straight to the local solver's in-place bounds repair, so + /// either path could silently corrupt the public property's own array. This pins both the public + /// array and the caller's own array against that regression. + /// + [TestMethod] + public void Test_InitialValuesAreNotMutatedByARun() + { + var initial = new double[] { 0.5d, 0.5d }; + var callerSnapshot = (double[])initial.Clone(); + var lower = new double[] { 0d, 0d }; + var upper = new double[] { 1d, 1d }; + var solver = new MLSL(TestFunctions.Booth, 2, initial, lower, upper); + solver.Minimize(); + + CollectionAssert.AreEqual(callerSnapshot, initial, "The caller's own array must not be modified by a run."); + CollectionAssert.AreEqual(callerSnapshot, solver.InitialValues, "InitialValues must still equal what was passed to the constructor after a run."); + } } } diff --git a/Test_Numerics/Mathematics/Optimization/Global/Test_MultiStart.cs b/Test_Numerics/Mathematics/Optimization/Global/Test_MultiStart.cs index ed931a74..299dcd82 100644 --- a/Test_Numerics/Mathematics/Optimization/Global/Test_MultiStart.cs +++ b/Test_Numerics/Mathematics/Optimization/Global/Test_MultiStart.cs @@ -395,5 +395,29 @@ public void Test_SolutionStaysWithinBounds() Assert.AreEqual(1d, solution[0], 1E-6); Assert.AreEqual(1d, solution[1], 1E-6); } + + /// + /// Test that the public property still equals what was + /// passed to the constructor after a run, and that the caller's own array is left untouched. + /// + /// + /// On the first iteration the solver's working array was re-pointed at + /// itself rather than copied into, so every later uniform draw and the local solver's bounds repair + /// wrote straight into the public property's own array. This pins both the public + /// array and the caller's own array against that regression. + /// + [TestMethod] + public void Test_InitialValuesAreNotMutatedByARun() + { + var initial = new double[] { 0.5d, 0.5d }; + var callerSnapshot = (double[])initial.Clone(); + var lower = new double[] { 0d, 0d }; + var upper = new double[] { 1d, 1d }; + var solver = new MultiStart(TestFunctions.Booth, 2, initial, lower, upper); + solver.Minimize(); + + CollectionAssert.AreEqual(callerSnapshot, initial, "The caller's own array must not be modified by a run."); + CollectionAssert.AreEqual(callerSnapshot, solver.InitialValues, "InitialValues must still equal what was passed to the constructor after a run."); + } } } From 7f3d0812f9293aebca0f05414913a0c5658ee7a9 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Tue, 25 Aug 2026 15:15:19 -0600 Subject: [PATCH 090/222] Confine the Powell search to its declared feasible region The line search is now restricted to the feasible step interval, the set of step lengths that keep every coordinate between its bounds. The step length is clamped to that interval before the trial point is formed and the point is repaired, the first bracketing step is chosen to land inside the interval so the bracketing direction test is made on distinct points, and the accepted step is clamped the same way. The zero step is evaluated first and kept unless the search strictly improves on it, which keeps each line search non-increasing. The extrapolated point is scored only when the reflection producing it is feasible; an infeasible extrapolation counts as no improvement. --- .../Mathematics/Optimization/Local/Powell.cs | 204 +++++++++++++++++- 1 file changed, 193 insertions(+), 11 deletions(-) diff --git a/Numerics/Mathematics/Optimization/Local/Powell.cs b/Numerics/Mathematics/Optimization/Local/Powell.cs index ad55bb81..42a103cd 100644 --- a/Numerics/Mathematics/Optimization/Local/Powell.cs +++ b/Numerics/Mathematics/Optimization/Local/Powell.cs @@ -15,9 +15,24 @@ namespace Numerics.Mathematics.Optimization /// /// Description: /// This method minimizes the function by bi-directionally searching along search vectors via Brent's method. The minima of - /// these search vectors are recorded and used to create new search vectors, as the current ones are deleted after use. + /// these search vectors are recorded and used to create new search vectors, as the current ones are deleted after use. /// The algorithm iterates until no significant improvement is made. /// + /// + /// Feasible region: + /// The search is confined to the box defined by and , and the objective + /// function is never evaluated outside it. Each line search is restricted to the feasible step interval, which is the set + /// of step lengths along the search direction that keep every coordinate between its bounds. That interval always contains + /// zero because the current iterate is feasible, so restricting the search never excludes the current point. The + /// extrapolated point used by the direction-set update is scored only when the reflection that produces it is itself + /// feasible; an infeasible extrapolation is treated as no improvement and the direction set is left alone for that + /// iteration. Projecting the reflection back onto the box instead was measured and rejected, because the projected point + /// sits on a face where the objective can be far smaller than at the reflection, which makes the acceptance test fire on a + /// point the test was not derived for and admits direction replacements that slow convergence badly on smooth problems. + /// These restrictions matter most when this class is the local solver inside a global optimizer, because the objective + /// function is then the global solver's own evaluation routine and any point it scores can be recorded as the reported + /// solution. + /// /// References: /// /// @@ -137,14 +152,24 @@ protected override void Optimize() } // Construct the extrapolated point and save the average direction moved. // Save the old starting point. + // The reflection through the current point readily leaves the box even when both points are + // inside it, and this point is scored through the objective function, so it has to be kept + // feasible. It is skipped rather than projected back: projection moves the point onto a face + // where the objective can be much smaller than at the reflection, which turns the acceptance + // test below into a test on a different point and admits direction replacements the test was + // never meant to admit. See the remarks on this class. + bool extrapolationIsFeasible = true; for (j = 0; j < D; j++) { ptt[j] = 2.0 * p[j] - pt[j]; + if (ptt[j] < LowerBounds[j] || ptt[j] > UpperBounds[j]) extrapolationIsFeasible = false; xi[j] = p[j] - pt[j]; pt[j] = p[j]; } - // Function evaluated at the extrapolated point - fptt = Evaluate(ptt, ref cancel); + // Function evaluated at the extrapolated point. An infeasible extrapolation is treated as no + // improvement, which is the same outcome the acceptance test reaches for a point that does + // not beat the value at the start of this iteration. + fptt = extrapolationIsFeasible ? Evaluate(ptt, ref cancel) : double.PositiveInfinity; if (cancel == true) return; if (fptt < fp) { @@ -171,33 +196,106 @@ protected override void Optimize() } /// - /// Auxiliary line minimization routine. + /// Auxiliary line minimization routine. /// - /// The initial point. - /// The initial direction. + /// The initial point. Updated in place to the minimizing point along the direction. + /// The initial direction. Updated in place to the vector displacement actually taken. /// Determines if the solver should be canceled. + /// + /// The fitness at the returned point, or when the solver was canceled. + /// + /// + /// + /// The search is restricted to the feasible step interval returned by + /// . That interval always contains zero, so restricting the + /// search never excludes the current point. Three guards enforce the restriction, because + /// expands geometrically and overwrites its own bounds, so + /// constructing the search over a narrower interval would not by itself confine it. + /// + /// + /// + /// The step length is clamped to the feasible interval before the trial point is formed, and each + /// coordinate of that point is then repaired. The objective seen by is + /// therefore the restriction of the function to the feasible segment, extended as a constant past + /// each end. No trial point can lie outside the box however far the bracket expands, and the + /// constant tails stop the bracketing search within a step or two of leaving the interval instead + /// of letting it run away. + /// + /// + /// The first bracketing step comes from and always lands inside the + /// feasible interval, so the single comparison that chooses the bracketing direction is made + /// between two genuinely different points rather than on a constant tail. + /// + /// + /// The accepted step length is clamped to the feasible interval before the displacement is applied, + /// so a bracket that ran past an endpoint cannot enlarge the recorded displacement or put an + /// infeasible direction into the direction set. Clamping the accepted step exactly matches the + /// clamping inside the objective, so the returned fitness is the value of the objective at the + /// returned point. + /// + /// + /// + /// The zero step is evaluated before the search and is kept unless the search strictly improves on + /// it, so this routine is non-increasing. The enclosing algorithm relies on that: it identifies the + /// direction of largest decrease by index, and that index is only defined when some direction did + /// decrease. + /// + /// + /// When the feasible interval collapses to the single point zero, the line search cannot move. That + /// happens when the direction is identically zero and when the iterate sits on a bound with the + /// direction pointing out of the box in every constrained coordinate. In that case the value at the + /// current point is returned and the direction is left unchanged, so a blocked direction is retained + /// in the direction set and can be retried from a later iterate. + /// + /// private double LineMinimization(double[] startPoint, double[] direction, ref bool cancel) { - // Line-minimization routine, Given an n-dimensional point p[0..n-1] and an n-dimension + // Line-minimization routine, Given an n-dimensional point p[0..n-1] and an n-dimension // direction xi[0..n-1], moves and resets p to where the function of functor func(p) takes on // a minimum along the direction xi from p, and replaces xi by the actual vector displacement // that p was moved. Also returns the value of func at the return location p. This is actually - // all accomplished by calling the Brent minimize routine. + // all accomplished by calling the Brent minimize routine. int D = NumberOfParameters; bool c = cancel; + GetFeasibleStepInterval(startPoint, direction, out double alphaMin, out double alphaMax); + + // The value at the current point, which is the zero step. Brent starts from the middle of the + // interval it is given rather than from an endpoint, so it need not evaluate the zero step at + // all. Taking it here makes it the fallback below, which is what keeps this routine from ever + // returning a point worse than the one it was given. + double zeroStep = Evaluate(startPoint, ref c); + cancel = c; + if (cancel) return double.NaN; + + // The interval always contains zero, so this is the degenerate case in which no step is + // feasible in either sense. Report the value at the current point instead of searching. + if (alphaMin == 0d && alphaMax == 0d) return zeroStep; + double func(double alpha) { + double step = alpha < alphaMin ? alphaMin : (alpha > alphaMax ? alphaMax : alpha); var x = new double[D]; for (int i = 0; i < D; i++) - x[i] = startPoint[i] + alpha * direction[i]; + x[i] = RepairParameter(startPoint[i] + step * direction[i], LowerBounds[i], UpperBounds[i]); return Evaluate(x, ref c); } + var brent = new BrentSearch(func, 0d, 1d) { RelativeTolerance = RelativeTolerance, AbsoluteTolerance = AbsoluteTolerance }; - brent.Bracket(0.1); + brent.Bracket(GetBracketingStep(alphaMin, alphaMax)); brent.Minimize(); cancel = c; if (cancel) return double.NaN; double xmin = brent.BestParameterSet.Values[0]; + // Keep the accepted step inside the feasible interval, matching the clamping in func above. + xmin = xmin < alphaMin ? alphaMin : (xmin > alphaMax ? alphaMax : xmin); + double fmin = brent.BestParameterSet.Fitness; + // Fall back on the zero step unless the search strictly improved on it. Written this way so a + // search that returned NaN keeps the current point rather than moving to it. + if (!(fmin < zeroStep)) + { + xmin = 0d; + fmin = zeroStep; + } for (int j = 0; j < NumberOfParameters; j++) { direction[j] *= xmin; @@ -205,7 +303,91 @@ double func(double alpha) // Make sure the parameter is within bounds startPoint[j] = RepairParameter(startPoint[j], LowerBounds[j], UpperBounds[j]); } - return brent.BestParameterSet.Fitness; + return fmin; + } + + /// + /// Returns the first bracketing step for a line search over the given feasible step interval. + /// + /// The most negative feasible step length. Must not be positive. + /// The most positive feasible step length. Must not be negative. + /// A nonzero step length that lies inside the feasible interval. + /// + /// + /// compares the objective at the origin against the objective one + /// step away, and only that single comparison decides which way it then expands. The objective handed + /// to it by is constant outside the feasible interval, so a first step + /// that lands outside carries no information: the comparison sees two equal values, the search never + /// turns around, and the whole feasible side can be missed. Returning a step that lies inside the + /// interval is what prevents that. + /// + /// + /// The default magnitude is the same 0.1 the routine has always used, and it is kept whenever the + /// interval is wide enough to hold it in either sense, so an interior iterate brackets exactly as + /// before. Only when the interval is narrower than the default in both senses is the magnitude + /// reduced, to half of the wider side, which is inside the interval and nonzero because the caller + /// has already handled the interval that collapses to a point. + /// + /// + private static double GetBracketingStep(double alphaMin, double alphaMax) + { + const double defaultStep = 0.1; + if (alphaMax >= defaultStep) return defaultStep; + if (alphaMin <= -defaultStep) return -defaultStep; + return alphaMax >= -alphaMin ? 0.5 * alphaMax : 0.5 * alphaMin; + } + + /// + /// Returns the interval of step lengths along a direction that keeps every coordinate within its bounds. + /// + /// The feasible point the step is taken from. + /// The search direction. + /// When this method returns, the most negative feasible step length. + /// When this method returns, the most positive feasible step length. + /// + /// + /// For each coordinate the two ratios (UpperBounds[i] - startPoint[i]) / direction[i] and + /// (LowerBounds[i] - startPoint[i]) / direction[i] are the step lengths that put that coordinate + /// exactly on a bound. The larger of the two is an upper limit and the smaller is a lower limit when the + /// component is positive, and the roles swap when it is negative. A zero component imposes no limit, and + /// an infinite bound produces an infinite limit, which is also no limit. + /// + /// + /// A direction with no nonzero component cannot move the point, so the interval is reported as the single + /// point zero rather than as the whole line. Because is feasible the interval + /// always contains zero; that is enforced explicitly at the end so that a rounding error in one of the + /// ratios, or a non-finite ratio, cannot produce an interval that excludes the current point. + /// + /// + private void GetFeasibleStepInterval(double[] startPoint, double[] direction, out double alphaMin, out double alphaMax) + { + alphaMin = double.NegativeInfinity; + alphaMax = double.PositiveInfinity; + bool moves = false; + for (int i = 0; i < NumberOfParameters; i++) + { + double d = direction[i]; + if (d == 0d) continue; + moves = true; + double toUpper = (UpperBounds[i] - startPoint[i]) / d; + double toLower = (LowerBounds[i] - startPoint[i]) / d; + double high = d > 0d ? toUpper : toLower; + double low = d > 0d ? toLower : toUpper; + if (high < alphaMax) alphaMax = high; + if (low > alphaMin) alphaMin = low; + } + + if (!moves) + { + alphaMin = 0d; + alphaMax = 0d; + return; + } + + // The start point is feasible, so zero is always a feasible step. These comparisons are written + // so that a NaN limit collapses that side of the interval to zero rather than propagating. + if (!(alphaMax > 0d)) alphaMax = 0d; + if (!(alphaMin < 0d)) alphaMin = 0d; } } From 8a1d833e8e23e029a660dcaf3df785fcf16af0ef Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Tue, 25 Aug 2026 15:22:14 -0600 Subject: [PATCH 091/222] Cover the Powell feasible region and every local method Powell gains coverage for the two places it used to leave the box, the constrained corner reported with its own fitness, a zero width box, an iterate on a bound with the minimum outside it, and infinite bounds. MultiStart and MLSL gain a data driven pass over every LocalMethod. The three methods those solvers build are asserted feasible and consistent with both Polish settings, and the two they do not build are pinned to the exception they raise, so the enumeration is covered end to end. The stored fitness convention under maximization is pinned for each method as well. --- .../Optimization/Global/Test_MLSL.cs | 127 +++++++++++ .../Optimization/Global/Test_MultiStart.cs | 129 ++++++++++++ .../Optimization/Local/Test_Powell.cs | 199 +++++++++++++++++- 3 files changed, 454 insertions(+), 1 deletion(-) diff --git a/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs b/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs index 641cb091..2af59a3e 100644 --- a/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs +++ b/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs @@ -535,5 +535,132 @@ public void Test_InitialValuesAreNotMutatedByARun() CollectionAssert.AreEqual(callerSnapshot, initial, "The caller's own array must not be modified by a run."); CollectionAssert.AreEqual(callerSnapshot, solver.InitialValues, "InitialValues must still equal what was passed to the constructor after a run."); } + + /// + /// A quadratic whose unconstrained minimum lies far outside the unit square used by the local method + /// tests below, so that the constrained solution is the corner (1, 1) with value 722. + /// + /// The point to evaluate. + /// The value of the quadratic at . + private static double CornerQuadratic(double[] x) + { + return (x[0] - 20d) * (x[0] - 20d) + (x[1] - 20d) * (x[1] - 20d); + } + + /// + /// Test that every local method this solver supports returns a point inside the declared bounds, and + /// that the reported fitness is the objective function evaluated at that same point. + /// + /// The local search method to drive. + /// Whether a final local search polishes the best sampled point. + /// + /// + /// The local solvers are built over this solver's own objective delegate, so their probes are scored + /// through and any of them can be recorded as the incumbent. The + /// objective here falls monotonically toward its unconstrained minimum outside the box, so any probe + /// that leaves the box scores better than the constrained corner and would be reported. + /// + /// + /// Both settings of are covered because the two used to fail differently. + /// Without polishing, the reported point was the infeasible probe itself. With polishing, the + /// reported point was repaired back to the corner while the fitness recorded at the infeasible probe + /// was kept, which produced a feasible-looking point carrying a fitness from somewhere else. + /// + /// + /// The fitness comparison is exact rather than approximate. stores + /// the point and the value it computed there in the same operation, so the two can only disagree if + /// something later replaced one of them. + /// + /// + [TestMethod] + [DataRow(LocalMethod.BFGS, true)] + [DataRow(LocalMethod.BFGS, false)] + [DataRow(LocalMethod.NelderMead, true)] + [DataRow(LocalMethod.NelderMead, false)] + [DataRow(LocalMethod.Powell, true)] + [DataRow(LocalMethod.Powell, false)] + public void Test_SupportedLocalMethodReturnsAFeasibleAndConsistentSolution(LocalMethod method, bool polish) + { + var initial = new double[] { 0.5d, 0.5d }; + var lower = new double[] { 0d, 0d }; + var upper = new double[] { 1d, 1d }; + var solver = new MLSL(CornerQuadratic, 2, initial, lower, upper, method) + { + ReportFailure = false, + RecordTraces = false, + MaxIterations = 10, + Polish = polish + }; + solver.Minimize(); + + var solution = solver.BestParameterSet.Values; + Assert.IsNotNull(solution); + for (int i = 0; i < solution.Length; i++) + { + Assert.IsGreaterThanOrEqualTo(lower[i], solution[i], "Parameter " + i + " is below its lower bound."); + Assert.IsLessThanOrEqualTo(upper[i], solution[i], "Parameter " + i + " is above its upper bound."); + } + + // The constrained minimum is the corner nearest the unconstrained optimum. + Assert.AreEqual(1d, solution[0], 1E-6); + Assert.AreEqual(1d, solution[1], 1E-6); + Assert.AreEqual(CornerQuadratic(solution), solver.BestParameterSet.Fitness, "The reported fitness must be the objective function evaluated at the reported point."); + } + + /// + /// Test that the local methods this solver does not build report that rather than returning a point. + /// + /// The unsupported local search method. + /// + /// also names the Adam and gradient descent algorithms, which this solver + /// has never constructed. This pins the behaviour so that the coverage above is known to be complete + /// for the enumeration as it stands, and so that adding support later is a deliberate change rather + /// than a silent one. + /// + [TestMethod] + [DataRow(LocalMethod.ADAM)] + [DataRow(LocalMethod.GradientDescent)] + public void Test_UnsupportedLocalMethodIsReported(LocalMethod method) + { + var initial = new double[] { 0.5d, 0.5d }; + var lower = new double[] { 0d, 0d }; + var upper = new double[] { 1d, 1d }; + var solver = new MLSL(CornerQuadratic, 2, initial, lower, upper, method) { RecordTraces = false, MaxIterations = 10 }; + + Assert.ThrowsExactly(() => solver.Minimize()); + } + + /// + /// Test that maximization stores the negated objective at the reported point. + /// + /// The local search method to drive. + /// + /// scales the objective by minus one while maximizing and stores + /// that scaled value as the fitness. The local solvers are minimized over this solver's evaluation + /// routine, which has already applied the scale, so the convention is the same for every method: the + /// stored fitness is the negated objective at the stored point. + /// + [TestMethod] + [DataRow(LocalMethod.BFGS)] + [DataRow(LocalMethod.NelderMead)] + [DataRow(LocalMethod.Powell)] + public void Test_MaximizeStoresTheNegatedObjective(LocalMethod method) + { + var initial = new double[] { 0.5d, 0.5d }; + var lower = new double[] { 0d, 0d }; + var upper = new double[] { 1d, 1d }; + double peak(double[] x) => 5d - (x[0] - 0.3d) * (x[0] - 0.3d) - (x[1] - 0.4d) * (x[1] - 0.4d); + var solver = new MLSL(peak, 2, initial, lower, upper, method) { ReportFailure = false, RecordTraces = false, MaxIterations = 10, ComputeHessian = false }; + solver.Maximize(); + + var solution = solver.BestParameterSet.Values; + Assert.IsNotNull(solution); + // The location tolerance is loose because the point of this test is the sign convention on the + // stored fitness, not the accuracy of any one local method. The simplex method in particular + // stops on its own simplex size rather than on a gradient. + Assert.AreEqual(0.3d, solution[0], 1E-3); + Assert.AreEqual(0.4d, solution[1], 1E-3); + Assert.AreEqual(-peak(solution), solver.BestParameterSet.Fitness, "While maximizing, the stored fitness is the negated objective at the stored point."); + } } } diff --git a/Test_Numerics/Mathematics/Optimization/Global/Test_MultiStart.cs b/Test_Numerics/Mathematics/Optimization/Global/Test_MultiStart.cs index 299dcd82..896fa8d6 100644 --- a/Test_Numerics/Mathematics/Optimization/Global/Test_MultiStart.cs +++ b/Test_Numerics/Mathematics/Optimization/Global/Test_MultiStart.cs @@ -419,5 +419,134 @@ public void Test_InitialValuesAreNotMutatedByARun() CollectionAssert.AreEqual(callerSnapshot, initial, "The caller's own array must not be modified by a run."); CollectionAssert.AreEqual(callerSnapshot, solver.InitialValues, "InitialValues must still equal what was passed to the constructor after a run."); } + + /// + /// A quadratic whose unconstrained minimum lies far outside the unit square used by the local method + /// tests below, so that the constrained solution is the corner (1, 1) with value 722. + /// + /// The point to evaluate. + /// The value of the quadratic at . + private static double CornerQuadratic(double[] x) + { + return (x[0] - 20d) * (x[0] - 20d) + (x[1] - 20d) * (x[1] - 20d); + } + + /// + /// Test that every local method this solver supports returns a point inside the declared bounds, and + /// that the reported fitness is the objective function evaluated at that same point. + /// + /// The local search method to drive. + /// Whether a final local search polishes the best member. + /// + /// + /// The local solvers are built over this solver's own objective delegate, so their probes are scored + /// through and any of them can be recorded as the incumbent. The + /// objective here falls monotonically toward its unconstrained minimum outside the box, so any probe + /// that leaves the box scores better than the constrained corner and would be reported. + /// + /// + /// Both settings of are covered because the two used to fail + /// differently. Without polishing, the reported point was the infeasible probe itself. With polishing, + /// the reported point was repaired back to the corner while the fitness recorded at the infeasible + /// probe was kept, which produced a feasible-looking point carrying a fitness from somewhere else. + /// + /// + /// The fitness comparison is exact rather than approximate. stores + /// the point and the value it computed there in the same operation, so the two can only disagree if + /// something later replaced one of them. + /// + /// + [TestMethod] + [DataRow(LocalMethod.BFGS, true)] + [DataRow(LocalMethod.BFGS, false)] + [DataRow(LocalMethod.NelderMead, true)] + [DataRow(LocalMethod.NelderMead, false)] + [DataRow(LocalMethod.Powell, true)] + [DataRow(LocalMethod.Powell, false)] + public void Test_SupportedLocalMethodReturnsAFeasibleAndConsistentSolution(LocalMethod method, bool polish) + { + var initial = new double[] { 0.5d, 0.5d }; + var lower = new double[] { 0d, 0d }; + var upper = new double[] { 1d, 1d }; + var solver = new MultiStart(CornerQuadratic, 2, initial, lower, upper, method) + { + ReportFailure = false, + RecordTraces = false, + // Every local start converges on the same corner, so a handful of starts covers the guarded + // path as thoroughly as the default hundred and keeps the test cheap. + MaxIterations = 10, + Polish = polish + }; + solver.Minimize(); + + var solution = solver.BestParameterSet.Values; + Assert.IsNotNull(solution); + for (int i = 0; i < solution.Length; i++) + { + Assert.IsGreaterThanOrEqualTo(lower[i], solution[i], "Parameter " + i + " is below its lower bound."); + Assert.IsLessThanOrEqualTo(upper[i], solution[i], "Parameter " + i + " is above its upper bound."); + } + + // The constrained minimum is the corner nearest the unconstrained optimum. + Assert.AreEqual(1d, solution[0], 1E-6); + Assert.AreEqual(1d, solution[1], 1E-6); + Assert.AreEqual(CornerQuadratic(solution), solver.BestParameterSet.Fitness, "The reported fitness must be the objective function evaluated at the reported point."); + } + + /// + /// Test that the local methods this solver does not build report that rather than returning a point. + /// + /// The unsupported local search method. + /// + /// also names the Adam and gradient descent algorithms, which this solver + /// has never constructed. This pins the behaviour so that the coverage above is known to be complete + /// for the enumeration as it stands, and so that adding support later is a deliberate change rather + /// than a silent one. + /// + [TestMethod] + [DataRow(LocalMethod.ADAM)] + [DataRow(LocalMethod.GradientDescent)] + public void Test_UnsupportedLocalMethodIsReported(LocalMethod method) + { + var initial = new double[] { 0.5d, 0.5d }; + var lower = new double[] { 0d, 0d }; + var upper = new double[] { 1d, 1d }; + var solver = new MultiStart(CornerQuadratic, 2, initial, lower, upper, method) { RecordTraces = false, MaxIterations = 10 }; + + Assert.ThrowsExactly(() => solver.Minimize()); + } + + /// + /// Test that maximization stores the negated objective at the reported point. + /// + /// The local search method to drive. + /// + /// scales the objective by minus one while maximizing and stores + /// that scaled value as the fitness. The local solvers are minimized over this solver's evaluation + /// routine, which has already applied the scale, so the convention is the same for every method: the + /// stored fitness is the negated objective at the stored point. + /// + [TestMethod] + [DataRow(LocalMethod.BFGS)] + [DataRow(LocalMethod.NelderMead)] + [DataRow(LocalMethod.Powell)] + public void Test_MaximizeStoresTheNegatedObjective(LocalMethod method) + { + var initial = new double[] { 0.5d, 0.5d }; + var lower = new double[] { 0d, 0d }; + var upper = new double[] { 1d, 1d }; + double peak(double[] x) => 5d - (x[0] - 0.3d) * (x[0] - 0.3d) - (x[1] - 0.4d) * (x[1] - 0.4d); + var solver = new MultiStart(peak, 2, initial, lower, upper, method) { ReportFailure = false, RecordTraces = false, MaxIterations = 10, ComputeHessian = false }; + solver.Maximize(); + + var solution = solver.BestParameterSet.Values; + Assert.IsNotNull(solution); + // The location tolerance is loose because the point of this test is the sign convention on the + // stored fitness, not the accuracy of any one local method. The simplex method in particular + // stops on its own simplex size rather than on a gradient. + Assert.AreEqual(0.3d, solution[0], 1E-3); + Assert.AreEqual(0.4d, solution[1], 1E-3); + Assert.AreEqual(-peak(solution), solver.BestParameterSet.Fitness, "While maximizing, the stored fitness is the negated objective at the stored point."); + } } } diff --git a/Test_Numerics/Mathematics/Optimization/Local/Test_Powell.cs b/Test_Numerics/Mathematics/Optimization/Local/Test_Powell.cs index b598290f..725dcfa9 100644 --- a/Test_Numerics/Mathematics/Optimization/Local/Test_Powell.cs +++ b/Test_Numerics/Mathematics/Optimization/Local/Test_Powell.cs @@ -1,4 +1,5 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Mathematics.Optimization; namespace Mathematics.Optimization @@ -211,5 +212,201 @@ public void Test_DistantLineMinimumUsesGeometricBracket() Assert.IsLessThan(200, solver.FunctionEvaluations, $"Expected fewer than 200 objective evaluations, but observed {solver.FunctionEvaluations}."); } + /// + /// The quadratic used by the feasible-region tests below. Its unconstrained minimum is the point + /// (20, 20), which is far outside the unit square those tests declare, so the constrained solution + /// is the corner (1, 1) and the objective falls monotonically toward the centre from there. + /// + /// The point to evaluate. + /// The value of the quadratic at . + private static double CornerQuadratic(double[] x) + { + return (x[0] - 20d) * (x[0] - 20d) + (x[1] - 20d) * (x[1] - 20d); + } + + /// + /// Test that no point outside the declared bounds is ever passed to the objective function. + /// + /// + /// + /// This covers both places the solver used to leave the box. The line search used to hand the step + /// length straight to a bracketing routine that expands geometrically without regard for the bounds, + /// and the extrapolated point built for the direction-set update is a reflection through the current + /// point, which leaves the box readily even when both points forming it are inside. + /// + /// + /// Both matter because this class is used as the local solver inside and + /// , where the objective function is the global solver's own evaluation routine and + /// any point it scores can be recorded as the reported solution. + /// + /// + /// The unconstrained minimum lies outside the box in the direction of the corner (1, 1), so an + /// unrestricted line search runs straight out of the box and the reflection through a corner-bound + /// iterate lands outside it as well. The iteration count is asserted so that a future change that + /// converges before the extrapolation is ever built cannot leave that second path untested. + /// + /// + [TestMethod] + public void Test_NoPointOutsideTheBoundsIsEvaluated() + { + var initial = new double[] { 0.5d, 0.5d }; + var lower = new double[] { 0d, 0d }; + var upper = new double[] { 1d, 1d }; + var offending = new List(); + + double recording(double[] x) + { + for (int i = 0; i < x.Length; i++) + { + if (x[i] < lower[i] || x[i] > upper[i]) + { + offending.Add((double[])x.Clone()); + break; + } + } + return CornerQuadratic(x); + } + + var solver = new Powell(recording, 2, initial, lower, upper) { ReportFailure = false, RecordTraces = false }; + solver.Minimize(); + + Assert.IsGreaterThanOrEqualTo(1, solver.Iterations, "The extrapolated point is only built once a full iteration completes, so this test must run at least one iteration to cover it."); + string report = offending.Count == 0 + ? "No point was evaluated outside the bounds." + : $"{offending.Count} points were evaluated outside the bounds, the first being ({offending[0][0]}, {offending[0][1]})."; + Assert.IsEmpty(offending, report); + } + + /// + /// Test that the reported solution is the constrained corner and that its fitness is the value of the + /// objective function at that corner. + /// + /// + /// + /// The unconstrained minimum of this objective is the point (20, 20) with value zero, and the + /// constrained minimum over the unit square is the corner (1, 1) with value 722. An unrestricted + /// search reaches the unconstrained minimum, scores it as zero and reports it, which is both + /// infeasible and wrong by 722. + /// + /// + /// Both halves are asserted exactly. A caller that checks feasibility and then trusts the fitness has + /// to be able to rely on the two describing the same point. + /// + /// + [TestMethod] + public void Test_ConstrainedCornerIsReportedWithItsOwnFitness() + { + var initial = new double[] { 0.5d, 0.5d }; + var lower = new double[] { 0d, 0d }; + var upper = new double[] { 1d, 1d }; + var solver = new Powell(CornerQuadratic, 2, initial, lower, upper) { ReportFailure = false, RecordTraces = false }; + solver.Minimize(); + + var solution = solver.BestParameterSet.Values; + Assert.AreEqual(1d, solution[0]); + Assert.AreEqual(1d, solution[1]); + Assert.AreEqual(722d, solver.BestParameterSet.Fitness); + Assert.AreEqual(CornerQuadratic(solution), solver.BestParameterSet.Fitness, "The reported fitness must be the objective function evaluated at the reported point."); + } + + /// + /// Test that maximization stores the negated objective, so that the same consistency check applies. + /// + /// + /// multiplies the objective by a scale that is minus one while + /// maximizing, and stores that scaled value as the fitness alongside the point. The stored fitness is + /// therefore the negated objective on this path, which is what the assertion below pins. + /// + [TestMethod] + public void Test_MaximizeStoresTheNegatedObjective() + { + var initial = new double[] { 0.5d, 0.5d }; + var lower = new double[] { 0d, 0d }; + var upper = new double[] { 1d, 1d }; + double peak(double[] x) => 5d - (x[0] - 0.3d) * (x[0] - 0.3d) - (x[1] - 0.4d) * (x[1] - 0.4d); + var solver = new Powell(peak, 2, initial, lower, upper) { ReportFailure = false, RecordTraces = false, ComputeHessian = false }; + solver.Maximize(); + + var solution = solver.BestParameterSet.Values; + Assert.AreEqual(0.3d, solution[0], 1E-6); + Assert.AreEqual(0.4d, solution[1], 1E-6); + Assert.AreEqual(-peak(solution), solver.BestParameterSet.Fitness, "While maximizing, the stored fitness is the negated objective at the stored point."); + } + + /// + /// Test that a box with no width returns its single feasible point. + /// + /// + /// Every coordinate is pinned, so the feasible step interval collapses to the single step length zero + /// in every direction and no line search can move. The solver must report the pinned point and the + /// value there, rather than dividing by a zero interval width or spending its whole iteration budget + /// discovering that it cannot move. + /// + [TestMethod] + public void Test_ZeroWidthBoxReturnsItsOnlyFeasiblePoint() + { + var initial = new double[] { 1d, 1d }; + var lower = new double[] { 1d, 1d }; + var upper = new double[] { 1d, 1d }; + double f(double[] x) => (x[0] - 3d) * (x[0] - 3d) + (x[1] + 2d) * (x[1] + 2d); + var solver = new Powell(f, 2, initial, lower, upper) { ReportFailure = false, RecordTraces = false, ComputeHessian = false }; + solver.Minimize(); + + var solution = solver.BestParameterSet.Values; + Assert.AreEqual(1d, solution[0]); + Assert.AreEqual(1d, solution[1]); + Assert.AreEqual(13d, solver.BestParameterSet.Fitness); + Assert.AreEqual(OptimizationStatus.Success, solver.Status); + } + + /// + /// Test that an iterate sitting on a bound with the minimum outside it stays on that bound. + /// + /// + /// The starting point is the corner (1, 1) and the unconstrained minimum is (5, 5), so every + /// coordinate direction that improves the objective points out of the box and the feasible step + /// interval collapses to a point in each of them. The corner is the constrained minimum, with value + /// 32, and it must be reported with that value rather than with the value at some point outside. + /// + [TestMethod] + public void Test_StartOnABoundWithTheMinimumOutsideStaysOnTheBound() + { + var initial = new double[] { 1d, 1d }; + var lower = new double[] { 0d, 0d }; + var upper = new double[] { 1d, 1d }; + double f(double[] x) => (x[0] - 5d) * (x[0] - 5d) + (x[1] - 5d) * (x[1] - 5d); + var solver = new Powell(f, 2, initial, lower, upper) { ReportFailure = false, RecordTraces = false, ComputeHessian = false }; + solver.Minimize(); + + var solution = solver.BestParameterSet.Values; + Assert.AreEqual(1d, solution[0]); + Assert.AreEqual(1d, solution[1]); + Assert.AreEqual(32d, solver.BestParameterSet.Fitness); + } + + /// + /// Test that infinite bounds leave the search unconstrained. + /// + /// + /// A caller that does not want to bound a coordinate passes an infinite bound for it. The feasible + /// step interval for such a coordinate is unbounded on that side, which is no constraint at all, so + /// the solver must still reach a minimum that lies far from its starting point. + /// + [TestMethod] + public void Test_InfiniteBoundsLeaveTheSearchUnconstrained() + { + var initial = new double[] { 0d, 0d }; + var lower = new double[] { double.NegativeInfinity, double.NegativeInfinity }; + var upper = new double[] { double.PositiveInfinity, double.PositiveInfinity }; + double f(double[] x) => (x[0] - 30d) * (x[0] - 30d) + (x[1] + 40d) * (x[1] + 40d); + var solver = new Powell(f, 2, initial, lower, upper) { ReportFailure = false, RecordTraces = false, ComputeHessian = false }; + solver.Minimize(); + + var solution = solver.BestParameterSet.Values; + Assert.AreEqual(30d, solution[0], 1E-4); + Assert.AreEqual(-40d, solution[1], 1E-4); + Assert.AreEqual(0d, solver.BestParameterSet.Fitness, 1E-8); + } + } } From 5657a5f96d9a8442621ab732a0e5ba469b002f35 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Tue, 25 Aug 2026 16:57:03 -0600 Subject: [PATCH 092/222] Keep the Powell line search out of the clamped plateau Clamping the step inside the line-search objective leaves the objective constant outside the feasible step interval. The bracketing search reverses into that constant region, and the minimization accepts a trial point that merely ties its incumbent, so a bracket carrying the constant region collapsed into it and the half holding the minimum was discarded. The solver then reported success on a bound with a live gradient. The bracket is now trimmed back to the feasible step interval before the minimization runs over it, so the minimization never sees the constant region. The trimmed interval makes the previous clamp of the accepted step unreachable, so it is removed. An end of the interval that is a bound is evaluated afterwards and taken when strictly better, because the minimization stops short of an end and a solution on a face would otherwise be reported just inside it. Also fixes the first bracketing step underflowing to zero, which the bracketing routine rejects, on a box whose feasible interval is a single subnormal wide. --- .../Mathematics/Optimization/Local/Powell.cs | 64 ++++++++++++++++--- 1 file changed, 54 insertions(+), 10 deletions(-) diff --git a/Numerics/Mathematics/Optimization/Local/Powell.cs b/Numerics/Mathematics/Optimization/Local/Powell.cs index 42a103cd..1230dae0 100644 --- a/Numerics/Mathematics/Optimization/Local/Powell.cs +++ b/Numerics/Mathematics/Optimization/Local/Powell.cs @@ -227,14 +227,26 @@ protected override void Optimize() /// between two genuinely different points rather than on a constant tail. /// /// - /// The accepted step length is clamped to the feasible interval before the displacement is applied, - /// so a bracket that ran past an endpoint cannot enlarge the recorded displacement or put an - /// infeasible direction into the direction set. Clamping the accepted step exactly matches the - /// clamping inside the objective, so the returned fitness is the value of the objective at the - /// returned point. + /// The interval reported by the bracketing search is trimmed back to the feasible interval before + /// the minimization runs over it, so the minimization never sees the constant tails at all. This + /// is required for correctness, not just for tidiness. accepts a trial + /// point whose value merely ties the incumbent, and every point on a constant tail ties, so a + /// bracket that included one would be collapsed into the tail and the half of it that holds the + /// minimum would be discarded. Trimming rather than widening keeps the search local: the interval + /// still comes from the outward expansion that started at the current point. It also means the + /// accepted step is already inside the feasible interval, so the clamp inside the objective is + /// inactive for it and the returned fitness is the objective at the returned point. /// /// /// + /// An end of the trimmed interval that is a bound of the feasible interval is evaluated after the + /// minimization and taken when it is strictly better. A line minimum that is cut off by a bound sits + /// exactly on that end, and stops short of an end by its own tolerance and + /// never evaluates it, so without this a solution on a face is reported a little inside the face + /// instead of on it. The comparison is strict so that a flat stretch reaching the boundary cannot + /// pull the iterate out to the boundary for no improvement. + /// + /// /// The zero step is evaluated before the search and is kept unless the search strictly improves on /// it, so this routine is non-increasing. The enclosing algorithm relies on that: it identifies the /// direction of largest decrease by index, and that index is only defined when some direction did @@ -280,15 +292,42 @@ double func(double alpha) return Evaluate(x, ref c); } - var brent = new BrentSearch(func, 0d, 1d) { RelativeTolerance = RelativeTolerance, AbsoluteTolerance = AbsoluteTolerance }; - brent.Bracket(GetBracketingStep(alphaMin, alphaMax)); + // Bracket the minimum first. The bracketing search steps outward from the current point and can + // run past an endpoint of the feasible interval, where the objective above is constant. That + // constant is what stops the expansion, but the interval it reports must be trimmed back to the + // feasible interval before the minimization runs over it. See the remarks on this method. + var bracketing = new BrentSearch(func, 0d, 1d) { RelativeTolerance = RelativeTolerance, AbsoluteTolerance = AbsoluteTolerance }; + bracketing.Bracket(GetBracketingStep(alphaMin, alphaMax)); + cancel = c; + if (cancel) return double.NaN; + double lower = bracketing.LowerBound < alphaMin ? alphaMin : (bracketing.LowerBound > alphaMax ? alphaMax : bracketing.LowerBound); + double upper = bracketing.UpperBound < alphaMin ? alphaMin : (bracketing.UpperBound > alphaMax ? alphaMax : bracketing.UpperBound); + + var brent = new BrentSearch(func, lower, upper) { RelativeTolerance = RelativeTolerance, AbsoluteTolerance = AbsoluteTolerance }; brent.Minimize(); cancel = c; if (cancel) return double.NaN; double xmin = brent.BestParameterSet.Values[0]; - // Keep the accepted step inside the feasible interval, matching the clamping in func above. - xmin = xmin < alphaMin ? alphaMin : (xmin > alphaMax ? alphaMax : xmin); double fmin = brent.BestParameterSet.Fitness; + + // A line minimum that is cut off by a bound sits exactly on the end of the feasible interval, and + // the minimization above stops short of an end by its own tolerance and never evaluates it. Try + // an end that is a bound of the feasible interval and take it only when it is strictly better, so + // that a step limited by a bound lands on the bound rather than just inside it, without a flat + // stretch being able to pull the iterate all the way out to the boundary for nothing. + if (upper == alphaMax && upper != 0d) + { + double atEnd = func(alphaMax); + if (atEnd < fmin) { xmin = alphaMax; fmin = atEnd; } + } + if (lower == alphaMin && lower != 0d) + { + double atEnd = func(alphaMin); + if (atEnd < fmin) { xmin = alphaMin; fmin = atEnd; } + } + cancel = c; + if (cancel) return double.NaN; + // Fall back on the zero step unless the search strictly improved on it. Written this way so a // search that returned NaN keeps the current point rather than moving to it. if (!(fmin < zeroStep)) @@ -334,7 +373,12 @@ private static double GetBracketingStep(double alphaMin, double alphaMax) const double defaultStep = 0.1; if (alphaMax >= defaultStep) return defaultStep; if (alphaMin <= -defaultStep) return -defaultStep; - return alphaMax >= -alphaMin ? 0.5 * alphaMax : 0.5 * alphaMin; + double wider = alphaMax >= -alphaMin ? alphaMax : alphaMin; + double half = 0.5 * wider; + // Halving underflows to zero once the wider side is the smallest subnormal, and a zero step is + // rejected by the bracketing routine. The endpoint itself is nonzero there, and the caller has + // already returned for the interval that collapses to a point, so it is a usable step. + return half == 0d ? wider : half; } /// From 40751869aa6570da74c5a5e91bd1d99975c3bf3b Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Tue, 25 Aug 2026 16:57:03 -0600 Subject: [PATCH 093/222] Pin each Powell line-search guard with a discriminating test Adds a test for the minimum just inside a bound that the plateau collapse missed, an iterate on the far corner that the bracketing step sign covers, a line search that must not keep a point worse than its start, and a box one subnormal wide that the bracketing step underflow rejected. Each was verified to fail when its own guard is removed and to pass when the others are. --- .../Optimization/Local/Test_Powell.cs | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/Test_Numerics/Mathematics/Optimization/Local/Test_Powell.cs b/Test_Numerics/Mathematics/Optimization/Local/Test_Powell.cs index 725dcfa9..fc4391a1 100644 --- a/Test_Numerics/Mathematics/Optimization/Local/Test_Powell.cs +++ b/Test_Numerics/Mathematics/Optimization/Local/Test_Powell.cs @@ -384,6 +384,140 @@ public void Test_StartOnABoundWithTheMinimumOutsideStaysOnTheBound() Assert.AreEqual(32d, solver.BestParameterSet.Fitness); } + /// + /// Test that a minimum lying just inside a bound is found from a start on that bound. + /// + /// + /// + /// The objective is strictly convex and smooth with its unique minimum at (0.04, 0.5), which is + /// inside the box. The start is on the lower bound of the first coordinate, so the first bracketing + /// probe is uphill and the bracketing search reverses back through the start and past the bound. + /// + /// + /// Past a bound the objective handed to the line search is constant, and the minimization accepts a + /// trial point that merely ties its incumbent, so a bracket carrying that constant region is + /// collapsed into it and the half holding the minimum is thrown away. The solver then stops on the + /// bound and reports success at a point whose gradient is nowhere near zero. Trimming the bracket + /// back to the feasible step interval before minimizing is what prevents that. + /// + /// + /// The assertions are on the solution rather than on the status, because the failure reported + /// success. A stop that leaves a feasible coordinate step still reducing the objective is the + /// property being ruled out. + /// + /// + [TestMethod] + public void Test_MinimumJustInsideABoundIsFoundFromThatBound() + { + var initial = new double[] { 0d, 0.5d }; + var lower = new double[] { 0d, 0d }; + var upper = new double[] { 1d, 1d }; + double f(double[] x) => (x[0] - 0.04d) * (x[0] - 0.04d) + (x[1] - 0.5d) * (x[1] - 0.5d); + var solver = new Powell(f, 2, initial, lower, upper) { ReportFailure = false, RecordTraces = false, ComputeHessian = false }; + solver.Minimize(); + + var solution = solver.BestParameterSet.Values; + Assert.AreEqual(0.04d, solution[0], 1E-6); + Assert.AreEqual(0.5d, solution[1], 1E-6); + Assert.AreEqual(0d, solver.BestParameterSet.Fitness, 1E-10); + + // No feasible coordinate step may still reduce the objective at the reported point. + double at = f(solution); + foreach (double h in new[] { 1E-2, 1E-3, 1E-4 }) + for (int i = 0; i < solution.Length; i++) + foreach (int sign in new[] { 1, -1 }) + { + var probe = (double[])solution.Clone(); + probe[i] = Math.Max(lower[i], Math.Min(upper[i], solution[i] + sign * h)); + Assert.IsGreaterThanOrEqualTo(at - 1E-12, f(probe), $"A feasible step of {sign * h} in parameter {i} still reduces the objective, so the reported point is not a constrained minimum."); + } + } + + /// + /// Test that an iterate on the far corner searches back into the box. + /// + /// + /// The start is the upper corner and the minimum is interior, so every improving direction points + /// back into the box and the feasible step interval is entirely negative. The bracketing routine + /// picks its direction from a single comparison against a first step of a fixed sign, and a first + /// step pointing out of the box is clamped back onto the start, which makes that comparison a tie + /// and leaves the search facing the wrong way. Choosing the sign of the first step from the feasible + /// interval is what prevents that. + /// + [TestMethod] + public void Test_IterateOnTheUpperCornerSearchesBackIntoTheBox() + { + var initial = new double[] { 1d, 1d }; + var lower = new double[] { 0d, 0d }; + var upper = new double[] { 1d, 1d }; + double f(double[] x) => (x[0] - 0.4d) * (x[0] - 0.4d) + (x[1] - 0.4d) * (x[1] - 0.4d); + var solver = new Powell(f, 2, initial, lower, upper) { ReportFailure = false, RecordTraces = false, ComputeHessian = false }; + solver.Minimize(); + + var solution = solver.BestParameterSet.Values; + Assert.AreEqual(0.4d, solution[0], 1E-6); + Assert.AreEqual(0.4d, solution[1], 1E-6); + Assert.AreEqual(0d, solver.BestParameterSet.Fitness, 1E-10); + } + + /// + /// Test that a line search which finds nothing better than its starting point keeps that point. + /// + /// + /// + /// The bracketing search hands the minimization an interval that need not contain the zero step, and + /// the minimization starts from the middle of the interval it is given, so it can return a point + /// worse than the one the line search was asked to improve on. Evaluating the zero step first and + /// keeping it unless the search strictly improves on it makes each line search non-increasing. + /// + /// + /// Goldstein-Price over this box is used because it is strongly multimodal, which is what makes a + /// line search settle on a point worse than its start. From this start the solver reaches the global + /// minimum of 3; without the fallback it stops in a basin four orders of magnitude worse. + /// + /// + [TestMethod] + public void Test_LineSearchNeverKeepsAPointWorseThanItsStart() + { + var initial = new double[] { -10d, -8d }; + var lower = new double[] { -10d, -10d }; + var upper = new double[] { 10d, 10d }; + var solver = new Powell(TestFunctions.GoldsteinPrice, 2, initial, lower, upper) { ReportFailure = false, RecordTraces = false, ComputeHessian = false }; + solver.Minimize(); + + Assert.AreEqual(3d, solver.BestParameterSet.Fitness, 1E-6); + var solution = solver.BestParameterSet.Values; + Assert.AreEqual(0d, solution[0], 1E-4); + Assert.AreEqual(-1d, solution[1], 1E-4); + } + + /// + /// Test that a box whose width is the smallest representable number still runs a line search. + /// + /// + /// The feasible step interval here is a single subnormal wide. Halving it to pick a first bracketing + /// step underflows to zero, and a zero step is rejected by the bracketing routine, which fails the + /// run on a box that is perfectly legal. Falling back on the interval endpoint itself keeps the step + /// nonzero. The interval that collapses to a single point is handled before this and does not reach + /// the step selection. + /// + [TestMethod] + public void Test_SubnormalWidthBoxStillRunsALineSearch() + { + var initial = new double[] { 0d }; + var lower = new double[] { 0d }; + var upper = new double[] { double.Epsilon }; + double f(double[] x) => (x[0] - 1d) * (x[0] - 1d); + var solver = new Powell(f, 1, initial, lower, upper) { ReportFailure = false, RecordTraces = false, ComputeHessian = false }; + solver.Minimize(); + + Assert.AreEqual(OptimizationStatus.Success, solver.Status); + var solution = solver.BestParameterSet.Values; + Assert.IsGreaterThanOrEqualTo(lower[0], solution[0], "The solution is below its lower bound."); + Assert.IsLessThanOrEqualTo(upper[0], solution[0], "The solution is above its upper bound."); + Assert.AreEqual(1d, solver.BestParameterSet.Fitness, 1E-300); + } + /// /// Test that infinite bounds leave the search unconstrained. /// From 2c066b05a65e2ece3a34376b59df0055a299ad37 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Tue, 25 Aug 2026 17:37:26 -0600 Subject: [PATCH 094/222] Make the MLSL InitialValues test detect the aliasing it guards The test asserted only value equality, which cannot fail for a legally constructed MLSL. The constructor rejects out-of-bounds initial values, so the bounds repair inside the local solver is a no-op, and the first sampled point is added with Minimized = true, which the local-search loop skips. Both aliasing paths are therefore inert for legal use and invisible to a value comparison: reverting the source fix left the test passing. Assert instead that no sampled point aliases the public InitialValues array. The whole collection is searched rather than element zero, because the run sorts SampledPoints by fitness and rebuilds the list, so the initial point does not stay at a known index. Verified discriminating: passes at HEAD, and fails with the MLSL half of commit 48b80ac reverted. --- .../Mathematics/Optimization/Global/Test_MLSL.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs b/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs index 2af59a3e..98087161 100644 --- a/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs +++ b/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs @@ -1,4 +1,5 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Linq; using Numerics.Mathematics.Optimization; namespace Mathematics.Optimization @@ -534,6 +535,19 @@ public void Test_InitialValuesAreNotMutatedByARun() CollectionAssert.AreEqual(callerSnapshot, initial, "The caller's own array must not be modified by a run."); CollectionAssert.AreEqual(callerSnapshot, solver.InitialValues, "InitialValues must still equal what was passed to the constructor after a run."); + + // Reference identity is the assertion that actually discriminates here. The value + // comparisons above cannot detect a regression of this fix: the constructor rejects + // out-of-bounds initial values, so the bounds repair inside the local solver is a no-op + // for any legally constructed MLSL, and the first sampled point is added with + // Minimized = true, which the local-search loop skips. Both aliasing paths are therefore + // inert for legal use, and only the aliasing itself is observable. + // + // The whole collection is searched rather than element zero, because the run sorts + // SampledPoints by fitness and rebuilds the list, so the initial point does not stay + // at a known index. + Assert.IsFalse(solver.SampledPoints.Any(p => ReferenceEquals(p.ParameterSet.Values, solver.InitialValues)), + "No sampled point may alias the public InitialValues array; each must own its own values."); } /// From c20045252ec5f6bd49458a6bbe1c84b3d5c64919 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 26 Aug 2026 10:19:24 -0600 Subject: [PATCH 095/222] Remove process narration from the shipping documentation --- .../Interpolation/Support/Interpolater.cs | 32 +--- .../Data/Paired Data/OrderedPairedData.cs | 28 +-- .../Bivariate Copulas/FrankCopula.cs | 2 +- .../Bivariate Copulas/JoeCopula.cs | 6 +- .../Multivariate/MultivariateNormal.cs | 89 ++++------ .../Multivariate/MultivariateStudentT.cs | 64 +++---- .../Base/UnivariateDistributionBase.cs | 15 +- .../Linear Algebra/CholeskyDecomposition.cs | 25 +-- .../Linear Algebra/DecompositionMethod.cs | 3 - .../Mathematics/Optimization/Global/MLSL.cs | 20 +-- .../Mathematics/Optimization/Local/BFGS.cs | 30 ++-- .../Mathematics/Optimization/Local/Powell.cs | 24 +-- .../Mathematics/Special Functions/Debye.cs | 14 +- Numerics/Sampling/Bootstrap/Bootstrap.cs | 6 +- Numerics/Sampling/MCMC/Base/MCMCSampler.cs | 114 ++++++------ Numerics/Sampling/MCMC/NUTS.cs | 164 +++++++----------- Numerics/Sampling/MCMC/SNIS.cs | 29 ++-- 17 files changed, 254 insertions(+), 411 deletions(-) diff --git a/Numerics/Data/Interpolation/Support/Interpolater.cs b/Numerics/Data/Interpolation/Support/Interpolater.cs index a18c4758..73291bfc 100644 --- a/Numerics/Data/Interpolation/Support/Interpolater.cs +++ b/Numerics/Data/Interpolation/Support/Interpolater.cs @@ -43,10 +43,8 @@ public Interpolater(IList xValues, IList yValues, SortOrder sort } this.XValues = xValues; this.YValues = yValues; - // This expression always evaluates to exactly 1 and never scales with the table size, despite - // its appearance. The constructor above requires Count >= 2, so Math.Pow(Count, 0.25) >= 1.189 - // and the (int) truncation is always at least 1, leaving Math.Min(1, >= 1) == 1. The intent was - // presumably Math.Max. See the remarks on deltaStart for why this is left as is. + // Always exactly 1: the constructor requires Count >= 2, so (int)Math.Pow(Count, 0.25) is at + // least 1 and Math.Min(1, ...) is 1. See the remarks on deltaStart. deltaStart = Math.Min(1, (int)Math.Pow((double)Count, 0.25)); SortOrder = sortOrder; @@ -67,25 +65,13 @@ public Interpolater(IList xValues, IList yValues, SortOrder sort /// treated as correlated, selecting the hunt search over bisection. /// /// - /// - /// This is always exactly 1. The constructor assigns - /// Math.Min(1, (int)Math.Pow(Count, 0.25)), and because the constructor also requires - /// Count >= 2 the right-hand term is never below 1, so the minimum is always 1. The - /// expression reads as though the window grows with the table size, but it does not, at any - /// . - /// - /// - /// The consequence is confined to which search path runs and never to the value returned. - /// Hunt and bisection are required to return the same bracket for the same input, and - /// Test_Search cross-checks both against Search.Sequential. A window of 1 simply means - /// the hunt search is selected less often than a size-scaled window would select it, which is a - /// performance characteristic and not a correctness one. It is therefore left as is deliberately. - /// - /// - /// This field is , and and - /// are public and settable, so a consumer that wants different search - /// behaviour can override the heuristic rather than depend on this value. - /// + /// The constructor assigns Math.Min(1, (int)Math.Pow(Count, 0.25)), which is always + /// exactly 1: the constructor requires Count >= 2, so the right-hand term is never + /// below 1. The window does not grow with the table size, at any . The value + /// affects only which search path runs — hunt and bisection return the same bracket for the same + /// input — so it is a performance characteristic rather than a correctness one. + /// and are public and settable, so a + /// consumer that wants different search behaviour can steer the search directly. /// protected int deltaStart = 0; diff --git a/Numerics/Data/Paired Data/OrderedPairedData.cs b/Numerics/Data/Paired Data/OrderedPairedData.cs index febc2eb3..0a2002a4 100644 --- a/Numerics/Data/Paired Data/OrderedPairedData.cs +++ b/Numerics/Data/Paired Data/OrderedPairedData.cs @@ -49,21 +49,12 @@ public class OrderedPairedData : IList, INotifyCollectionChanged /// treated as correlated, selecting the hunt search over bisection. /// /// - /// - /// This is permanently 0. It is initialized to zero here and assigned nowhere else in the - /// class, so the correlated test in — - /// Math.Abs(start - XSearchStart) > XdeltaStart — reports correlated only when a search - /// lands on exactly the same index as the previous one. The equivalent field on - /// Interpolater is at least assigned, though it too is always 1. - /// - /// - /// The consequence is confined to which search path runs and never to the value returned. - /// Hunt and bisection return the same bracket for the same input, and Test_Search cross-checks - /// both against Search.Sequential. A window of 0 simply means bisection is chosen in almost - /// every case, which is a performance characteristic and not a correctness one. It is therefore left - /// as is deliberately. and are public and - /// settable, so a consumer can steer the search directly rather than depend on this heuristic. - /// + /// This field is never assigned after initialization, so the correlated test in + /// reports correlated only when a search lands on exactly the same + /// index as the previous one. The value affects only which search path runs — hunt and bisection + /// return the same bracket for the same input — so it is a performance characteristic rather than + /// a correctness one. and are public and + /// settable, so a consumer can steer the search directly. /// private int XdeltaStart = 0; @@ -72,10 +63,9 @@ public class OrderedPairedData : IList, INotifyCollectionChanged /// treated as correlated, selecting the hunt search over bisection. /// /// - /// Permanently 0 for the same reason as : it is never assigned outside this - /// declaration, so reports correlated only on an exact index repeat. - /// The effect is confined to which search path runs, never to the bracket returned. See - /// for the full note. + /// Never assigned after initialization, so reports correlated only + /// on an exact index repeat. The effect is confined to which search path runs, never to the + /// bracket returned; see . /// private int YdeltaStart = 0; diff --git a/Numerics/Distributions/Bivariate Copulas/FrankCopula.cs b/Numerics/Distributions/Bivariate Copulas/FrankCopula.cs index 74c5fcaf..0ec28c0d 100644 --- a/Numerics/Distributions/Bivariate Copulas/FrankCopula.cs +++ b/Numerics/Distributions/Bivariate Copulas/FrankCopula.cs @@ -218,7 +218,7 @@ public override BivariateCopula Clone() /// -1. /// /// - /// Accuracy. The eight pinned pyvinecopulib 0.7.6 oracle points are matched to 3.4E-16. + /// Accuracy. Agrees with pyvinecopulib 0.7.6 reference values to 3.4E-16. /// Measured against mpmath at 60 decimal digits on a grid over 0.1 ≤ |θ| ≤ 100, the worst /// absolute error is 1.8E-15, at θ = 0.2. Below |θ| of about 0.01 the leading terms of the expression /// cancel against each other and the absolute error grows, reaching 2.0E-13 at θ = 0.001, which is the diff --git a/Numerics/Distributions/Bivariate Copulas/JoeCopula.cs b/Numerics/Distributions/Bivariate Copulas/JoeCopula.cs index d0cd377e..2857df05 100644 --- a/Numerics/Distributions/Bivariate Copulas/JoeCopula.cs +++ b/Numerics/Distributions/Bivariate Copulas/JoeCopula.cs @@ -211,9 +211,9 @@ public override BivariateCopula Clone() /// expanded in inverse powers of k, giving 1 / (θ² k³) * Σ[j ≥ 0] (-1)^j h_j / k^j with /// h_j = Σ[i = 0 to j] p^i q^(j - i), p = 2/θ and q = (2 - θ)/θ, and each power is summed over k > K /// with the Euler-Maclaurin form of the Hurwitz zeta function. Retaining terms through j = 6 leaves a - /// residual below 1E-25, so the accuracy of the result is limited only by double rounding. The seven - /// pinned pyvinecopulib 0.7.6 oracle points are matched to 4.5E-16, and against mpmath at 60 decimal - /// digits on a grid over θ in [1, 100] the worst absolute error is 1.7E-16. + /// residual below 1E-25, so the accuracy of the result is limited only by double rounding. Agrees + /// with pyvinecopulib 0.7.6 reference values to 4.5E-16, and against mpmath at 60 decimal digits + /// on a grid over θ in [1, 100] the worst absolute error is 1.7E-16. /// /// /// References: diff --git a/Numerics/Distributions/Multivariate/MultivariateNormal.cs b/Numerics/Distributions/Multivariate/MultivariateNormal.cs index 22319091..78c0ea48 100644 --- a/Numerics/Distributions/Multivariate/MultivariateNormal.cs +++ b/Numerics/Distributions/Multivariate/MultivariateNormal.cs @@ -67,8 +67,7 @@ public MultivariateNormal(double[] mean, double[,] covariance) /// The number of dimensions in the distribution. /// The decomposition method used to factorize the covariance matrix. /// - /// See for what the selector governs. Added for - /// . + /// See for what the selector governs. /// public MultivariateNormal(int dimension, DecompositionMethod decomposition) { @@ -84,8 +83,7 @@ public MultivariateNormal(int dimension, DecompositionMethod decomposition) /// The mean vector μ (mu) for the distribution. /// The decomposition method used to factorize the covariance matrix. /// - /// See for what the selector governs. Added for - /// . + /// See for what the selector governs. /// public MultivariateNormal(double[] mean, DecompositionMethod decomposition) { @@ -102,8 +100,7 @@ public MultivariateNormal(double[] mean, DecompositionMethod decomposition) /// /// accepts a singular (collinear) covariance matrix /// that rejects. See for what - /// the selector governs and for the degenerate-density convention. Added for - /// . + /// the selector governs and for the degenerate-density convention. /// public MultivariateNormal(double[] mean, double[,] covariance, DecompositionMethod decomposition) { @@ -156,16 +153,11 @@ public MultivariateNormal(double[] mean, double[,] covariance, DecompositionMeth /// silently break that agreement. /// /// - /// The literal above is written out rather than derived as 2d * Tools.DoubleMachineEpsilon, - /// and that matters. is the decimal literal - /// 1.11022302462516E-16, which is 2⁻⁵³ rounded to fifteen significant figures and not 2⁻⁵³ itself. - /// 2⁻⁵³ is exactly 1.1102230246251565E-16, so the literal is the larger of the two, at - /// 1.000000000000003 times 2⁻⁵³, and the ratio between the two class constants is - /// 1.9999999999999938 rather than 2. Doubling the rounded constant would therefore - /// not yield 2⁻⁵² bit-exactly, and the - /// thresholds built on it would drift off scipy's by a few ulps — enough to break an exact-agreement - /// test without producing any visible symptom. Do not "simplify" this back to a multiple of - /// . + /// The literal must be 2⁻⁵² bit-exactly, so it is written out rather than derived as + /// 2d * Tools.DoubleMachineEpsilon: that constant is the decimal literal + /// 1.11022302462516E-16, which is 2⁻⁵³ rounded to fifteen significant figures, so doubling it + /// gives 1.9999999999999938 times 2⁻⁵³ rather than 2⁻⁵², and the thresholds built on it would + /// drift off scipy's by a few ulps. /// /// private const double RelativeMachineEpsilon = 2.220446049250313E-16; @@ -349,9 +341,8 @@ public double[] StandardDeviation /// /// The selector is fixed at construction and is honoured by every re-factorization performed by /// , and . - /// It is deliberately get-only: a setter would have to re-factorize, and between the assignment and - /// the next call to every cached quantity — the normalizing constant and - /// the sampling factor — would be stale. + /// Every cached quantity — the normalizing constant and the sampling factor — is built for it, so + /// selecting a different method requires constructing a new distribution. /// /// /// The selector governs the density (, , @@ -365,10 +356,10 @@ public double[] StandardDeviation /// decomposition and return distributions that use the default /// selector. Those two helpers therefore throw /// when the sub-covariance they need is singular, even on a distribution built with - /// whose own density evaluates perfectly well — - /// which is exactly what a caller with collinear gridded cells runs into. Take the marginal or - /// conditional mean and covariance and construct the sub-distribution explicitly with the - /// constructor to work around it. + /// whose own density evaluates perfectly well. + /// To work around it, take the marginal or conditional mean and covariance and construct the + /// sub-distribution explicitly with the + /// constructor. /// /// /// Under the distribution follows the degenerate @@ -410,15 +401,12 @@ public double[] StandardDeviation /// tolerance proportional to ‖x − μ‖, where scipy uses one proportional to the eigenvalue scale of /// Σ. For a large-scale singular covariance scipy therefore admits points that are visibly off the /// support — for Σ = 1E12 · [[1,1],[1,1]] scipy returns a finite density at (1, −1) — where this - /// class returns negative infinity. The answer here is the mathematically exact one and is kept. + /// class returns negative infinity, the mathematically exact answer. /// Second, when Σ is identically zero the support is the single point μ; this class reports /// = 1 and = 0 there, the counting-measure density that the /// rank-0 case of the convention above implies, while scipy returns zero density even at μ. Every /// point other than μ is off the support and scores zero in both. /// - /// - /// Added for . - /// /// public DecompositionMethod Decomposition => _decomposition; @@ -445,9 +433,8 @@ public double[] StandardDeviation /// public void SetParameters(double[] mean, double[,] covariance) { - // Validate parameters. Under the singular value path the validation already builds the - // decomposition it needs, so it is handed back and reused for the factorization rather than - // being recomputed: an O(n^3) factorization is the whole cost this selector trades away. + // Under the singular value path the validation already builds the decomposition it needs, + // so it is handed back and reused for the factorization rather than recomputed. ValidateParameters(mean, covariance, true, out var singularValues); SetParametersCore(mean, covariance, singularValues); } @@ -461,11 +448,9 @@ public void SetParameters(double[] mean, double[,] covariance) /// validation under ; null under /// , where it is not used. /// - /// Split out of so that both the throwing entry point and the - /// non-throwing can validate once and factorize once. The - /// decomposition is a deterministic function of , so reusing the - /// instance built during validation produces exactly the same factorization that recomputing it - /// would — the saving is the duplicated O(n³) work, not a change of result. + /// Shared by the throwing and the non-throwing + /// so that each entry point validates once and factorizes once, + /// reusing the decomposition built during validation. /// private void SetParametersCore(double[] mean, double[,] covariance, SingularValueDecomposition? singularValues) { @@ -590,10 +575,9 @@ private static double SingularValueThreshold(SingularValueDecomposition singular /// . The projection is compared against /// · , scaled by the magnitude /// of the centred point so that the test stays meaningful for points far from the mean, where the - /// roundoff in the projection grows in proportion. On the reference cases of - /// an on-support point projects to at - /// most 6E-16 while an off-support point projects to more than 0.7, so the test has several orders - /// of margin on both sides. + /// roundoff in the projection grows in proportion. On representative singular covariances an + /// on-support point projects to at most 6E-16 while an off-support point projects to more than + /// 0.7, so the test has several orders of margin on both sides. /// /// /// The scaling is by ‖x − μ‖, which is not what @@ -601,22 +585,16 @@ private static double SingularValueThreshold(SingularValueDecomposition singular /// eigenvalue magnitude of Σ instead. The two agree whenever Σ is of order one, and diverge for a /// large-scale singular Σ, where scipy's tolerance becomes very loose — for Σ = 1E12 · [[1,1],[1,1]] /// scipy's tolerance is about 4.4E+5, so it returns a finite density at (1, −1), a point plainly off - /// the support. This class returns negative infinity there, which is the exact answer, and that is - /// deliberate. + /// the support. This class returns negative infinity there, which is the exact answer. /// /// - /// The absolute floor of the tolerance. Because the scale factor is max(1, ‖x − μ‖), the - /// smallest tolerance this test ever applies is + /// Because the scale factor is max(1, ‖x − μ‖), the smallest tolerance this test ever applies is /// · = 2.220446049250313E-10. - /// It is not an independently chosen constant: it follows the class epsilon, so it is exactly - /// one factor of above the same ε that decides the rank, the null - /// space and the pseudo-determinant, and it moves only if that epsilon moves. It was - /// 1.11022302462516E-10 while the class used and doubled - /// when the constant was aligned with NumPy's relative spacing. The measured margins above — - /// on-support residuals at most 6E-16 and off-support residuals at least 0.7 — leave roughly five - /// orders of headroom below the tolerance and nine above it, so neither value changes any outcome on - /// the reference cases. Do not retune it in isolation; changing it would decouple this test from the - /// threshold that produced the null space it is testing against. + /// It is not an independently tunable constant: it follows the class epsilon, exactly one factor + /// of above the same ε that decides the rank, the null space and + /// the pseudo-determinant, and moving it separately would decouple this test from the threshold + /// that produced the null space it tests against. The margins above leave several orders of + /// headroom on both sides of the tolerance. /// /// private bool IsOnSupport(double[] x) @@ -857,11 +835,8 @@ public bool TrySetParameters(double[] mean, double[,] covariance) // ones, so the non-throwing contract absorbs both failure modes. try { - // Validate through the overload that hands back the decomposition, then apply it directly. - // Routing through the public SetParameters would validate a second time and build a second - // singular value decomposition of the same matrix, doubling the O(n^3) cost on exactly the - // path this method exists to serve: proposal and likelihood loops that swap a covariance per - // evaluation. The decomposition is deterministic, so the factorization is unchanged. + // Validate through the overload that hands back the decomposition and apply it directly, + // so the covariance is validated and factorized exactly once per call. if (ValidateParameters(mean, covariance, false, out var singularValues) is null) { SetParametersCore(mean, covariance, singularValues); diff --git a/Numerics/Distributions/Multivariate/MultivariateStudentT.cs b/Numerics/Distributions/Multivariate/MultivariateStudentT.cs index 744ece4d..c9692d02 100644 --- a/Numerics/Distributions/Multivariate/MultivariateStudentT.cs +++ b/Numerics/Distributions/Multivariate/MultivariateStudentT.cs @@ -124,12 +124,11 @@ public MultivariateStudentT(double degreesOfFreedom, double[] location, double[, /// /// /// The default is a Mersenne twister seeded with , - /// which is what makes the default result reproducible — but it also means every instance left at the - /// default replays the identical lattice shifts, so the quadrature errors of separate instances are - /// correlated rather than independent and do not average out when many evaluations are aggregated. - /// Assign a seeded generator to tie results to a caller's own seed and to decorrelate the error - /// across instances. This mirrors so that the two classes - /// behave the same way. + /// which makes the default result reproducible — but every instance left at the default replays the + /// identical lattice shifts, so the quadrature errors of separate instances are correlated rather + /// than independent and do not average out when many evaluations are aggregated. Assign a seeded + /// generator to tie results to a caller's own seed and to decorrelate the error across instances; + /// this mirrors . /// /// /// MVNDST advances the generator, so successive CDF evaluations on the same instance consume @@ -140,11 +139,10 @@ public MultivariateStudentT(double degreesOfFreedom, double[] location, double[, /// Not thread-safe. has no internal synchronization, so above /// two dimensions a single instance must not have called concurrently /// from several threads. Give each thread its own instance, or assign each thread's instance a - /// generator of its own (mvt.MVNUNI = new MersenneTwister(seedForThisThread)). Note that - /// is not a remedy: it copies this reference, so a clone shares one - /// generator with the original and races exactly as the original would. It cannot do otherwise, - /// because the property is typed and an arbitrary has no - /// general deep copy. + /// generator of its own (mvt.MVNUNI = new MersenneTwister(seedForThisThread)). + /// is not a remedy: the property is typed , which exposes + /// no general deep copy, so a clone shares one generator with the original and races exactly as + /// the original would. /// /// /// Thrown when the assigned generator is null. @@ -508,26 +506,19 @@ public double Mahalanobis(double[] x) /// /// Above two dimensions this method is stochastic, stateful, and not thread-safe. The inner /// MVNDST is a randomized lattice rule that draws its shifts from , which is - /// instance state, so three things follow that do not apply at one or two dimensions (where the CDF - /// is a closed form and touches no instance state): - /// - /// - /// First, the result carries a small quadrature error, of the order of the - /// 1E-4 absolute tolerance MVNDST is given, rather than being exact to roundoff. - /// Second, each call advances the generator, so repeated calls at the same point on the same - /// instance return slightly different values; it is two freshly constructed instances with - /// the same parameters and seed that agree bit for bit, not two calls on one instance. - /// Third, and most easily overlooked: because the generator is shared instance state and - /// has no internal synchronization, calling this method - /// concurrently on a single shared instance is a data race. A caller parallelising over - /// quantiles — Parallel.For(… => mvt.CDF(points[i])) over one instance — will get - /// corrupted lattice shifts, and therefore silently wrong probabilities, or an + /// instance state; at one and two dimensions the CDF is a closed form and touches no instance + /// state. The result carries a small quadrature error, of the order of the 1E-4 absolute + /// tolerance MVNDST is given. Each call advances the generator, so repeated calls at the same + /// point on the same instance return slightly different values; it is two freshly constructed + /// instances with the same parameters and seed that agree bit for bit. And because + /// has no internal synchronization, calling this method concurrently + /// on a single shared instance — Parallel.For(… => mvt.CDF(points[i])) — is a data race + /// that produces corrupted lattice shifts and silently wrong probabilities, or an /// from inside the generator. /// /// /// The remedy is one instance per thread, or a distinct generator per thread assigned through - /// . does not help, because it copies the generator by - /// reference and the clone races with its original. See for the full note. + /// ; shares the generator by reference and does not help. /// /// /// Reference: Genz, A. and Bretz, F. (2009). "Computation of Multivariate Normal and t Probabilities." @@ -562,10 +553,8 @@ public override double CDF(double[] x) for (int i = 0; i < Dimension; i++) zVec[i] = x[i] - _location[i]; - // Create MVN with zero mean and the scale matrix Σ for CDF evaluation. The caller's generator is - // handed to it so that the lattice shifts MVNDST draws come from this instance's MVNUNI rather - // than from a private default-seeded one; without this the property could be assigned and would - // have no effect on the result. + // Create MVN with zero mean and the scale matrix Σ for CDF evaluation, handing it this + // instance's MVNUNI so the lattice shifts MVNDST draws come from the caller-visible generator. var mvn = new MultivariateNormal(new double[Dimension], _scaleMatrix.ToArray()) { MVNUNI = _MVNUNI }; double sum = 0.0; @@ -781,13 +770,12 @@ public double[] InverseCDF(double[] probabilities) /// /// A new instance with identical parameters. /// - /// The parameters, and the factorization built from them, are copied deeply. is - /// the one exception: it is copied by reference, so the clone and the original share a single - /// generator. Cloning is therefore not a way to make concurrent calls - /// safe above two dimensions, and the clone's CDF results are not reproducible independently of the - /// original's. Assign the clone its own generator when either matters. The reference copy is not an - /// oversight: is typed , and an arbitrary - /// exposes no general deep copy. + /// The parameters, and the factorization built from them, are copied deeply. + /// is the one exception: the property is typed , which exposes no general deep + /// copy, so it is copied by reference and the clone shares a single generator with the original. + /// Cloning is therefore not a way to make concurrent calls safe above + /// two dimensions, and the clone's CDF results are not reproducible independently of the + /// original's; assign the clone its own generator when either matters. /// public override MultivariateDistribution Clone() { diff --git a/Numerics/Distributions/Univariate/Base/UnivariateDistributionBase.cs b/Numerics/Distributions/Univariate/Base/UnivariateDistributionBase.cs index a3951e76..144d9fdc 100644 --- a/Numerics/Distributions/Univariate/Base/UnivariateDistributionBase.cs +++ b/Numerics/Distributions/Univariate/Base/UnivariateDistributionBase.cs @@ -294,15 +294,13 @@ public double[] InverseCDF(IList probabilities) /// site: CentralMoments(1E-8) requests a relative tolerance of 1E-8 from this adaptive /// routine, while CentralMoments(1000) selects the fixed-step bin-expectation overload with 1,000 /// steps. Writing CentralMoments(1) when a tolerance of 1.0 was meant silently runs the other - /// method with a single step. The C++ port team flagged this pair as a foot-gun; the signatures are - /// kept for source compatibility. + /// method with a single step. /// /// /// This overload integrates with over the range /// [InverseCDF(1E-16), InverseCDF(1 − 1E-16)], subdividing until the requested relative tolerance is /// met. A moment whose integration fails is returned as rather than - /// throwing. Within this library only NoncentralT uses this overload; every other in-repo - /// caller uses the fixed-step one. + /// throwing. /// /// public virtual double[] CentralMoments(double tolerance = 1E-8) @@ -346,12 +344,10 @@ public virtual double[] CentralMoments(double tolerance = 1E-8) /// site: CentralMoments(1000) selects this fixed-step routine with 1,000 steps, while /// CentralMoments(1E-8) selects the adaptive overload with a relative tolerance of 1E-8. /// Writing CentralMoments(1) when a tolerance was meant silently runs this method with a - /// single step. The C++ port team flagged this pair as a foot-gun; the signatures are kept for - /// source compatibility. + /// single step. /// /// - /// What this overload actually computes, which is worth stating precisely because a port - /// cannot be written from the word "trapezoidal" alone: it stratifies + /// What this overload computes: it stratifies /// [InverseCDF(1E-8), InverseCDF(1 − 1E-8)] into equal bins, takes each /// bin's probability mass ΔFᵢ as a difference of values, and accumulates /// the discrete expectation Σᵢ xᵢᵏ · ΔFᵢ. It is therefore a bin-probability (midpoint) expectation @@ -365,9 +361,6 @@ public virtual double[] CentralMoments(double tolerance = 1E-8) /// from −∞, and the final ΔF is 1 − CDF(lower bound of the last bin) — so despite the 1E-8 stratification /// endpoints the total probability sums to one and the effective range is not truncated. Cost /// and accuracy are both fixed by and there is no convergence check. - /// Within this library CompetingRisks, EmpiricalDistribution, - /// GeneralizedNormal, KappaFour, Mixture and TruncatedDistribution all - /// use this overload; only NoncentralT uses the adaptive one. /// /// public virtual double[] CentralMoments(int steps = 300) diff --git a/Numerics/Mathematics/Linear Algebra/CholeskyDecomposition.cs b/Numerics/Mathematics/Linear Algebra/CholeskyDecomposition.cs index 8b3a15c5..3a3452aa 100644 --- a/Numerics/Mathematics/Linear Algebra/CholeskyDecomposition.cs +++ b/Numerics/Mathematics/Linear Algebra/CholeskyDecomposition.cs @@ -55,10 +55,8 @@ public class CholeskyDecomposition /// The pivot tolerance is evaluated at the dimension of A. /// /// - /// Success is not a rank certificate. The pivot test rejects most numerically rank-deficient - /// matrices but not all of them — for a covariance estimated from m = n - 1 observations, 11% to - /// 18% still factorize — so being true does not prove full rank. Use a - /// singular value decomposition when the rank genuinely has to be known. + /// Success is not a rank certificate; see for the + /// limits of the pivot test. /// /// public CholeskyDecomposition(Matrix A) @@ -99,14 +97,11 @@ public CholeskyDecomposition(Matrix A) /// log pseudo-determinant 1.2528. See . /// /// - /// This test is not a rank certificate. It rejects most numerically rank-deficient matrices but - /// not all of them, so being true does not prove the matrix has full - /// rank. Measured over 1,000 trials per dimension on a covariance estimated from m = n - 1 - /// observations of n variables — exactly rank m, and the commonest way rank deficiency - /// arises in practice — the test removes roughly two thirds of the matrices the absolute test had - /// accepted, while 11% to 18% still factorize. When the rank of a matrix genuinely has to be known, - /// use a singular value decomposition; for a multivariate normal that is - /// DecompositionMethod.SingularValue, which is the only reliable rank test in this library. + /// The test is not a rank certificate: it rejects most numerically rank-deficient matrices but + /// not all of them, so being true does not prove the matrix has + /// full rank. See for the margins in both directions. + /// When the rank genuinely has to be known, use a singular value decomposition; for a + /// multivariate normal that is DecompositionMethod.SingularValue. /// /// /// When A[i,i] is not a positive finite number the threshold falls back to zero, which is the @@ -145,7 +140,7 @@ public CholeskyDecomposition(Matrix A, double relativeTolerance) if (i == j) { // Reject a pivot that is negligible relative to its own diagonal entry. The diagonal - // guard keeps the threshold at zero — today's absolute test — whenever A[i,i] is not a + // guard keeps the threshold at zero (the absolute test) whenever A[i,i] is not a // positive finite number. double diagonal = this.A[i, i]; double threshold = diagonal > 0d && !double.IsInfinity(diagonal) ? relativeTolerance * diagonal : 0d; @@ -204,9 +199,7 @@ public CholeskyDecomposition(Matrix A, double relativeTolerance) /// /// The margin against falsely rejecting a legitimate matrix is large. For two variables with correlation /// ρ the pivot ratio is 1 - ρ², so a false rejection at n = 2 requires - /// 1 - ρ² <= 4.44E-16, that is ρ within about two ulp of one. Measured across the Numerics - /// test suite (29.8 million factorizations) the smallest ratio produced by a genuinely positive-definite - /// matrix is 2.0E-12, some 4,500 times the tolerance that applies to it. + /// 1 - ρ² <= 4.44E-16, that is ρ within about two ulp of one. /// /// /// The margin in the other direction is far smaller, and no tolerance of this form can close it. A diff --git a/Numerics/Mathematics/Linear Algebra/DecompositionMethod.cs b/Numerics/Mathematics/Linear Algebra/DecompositionMethod.cs index 840e413d..12853706 100644 --- a/Numerics/Mathematics/Linear Algebra/DecompositionMethod.cs +++ b/Numerics/Mathematics/Linear Algebra/DecompositionMethod.cs @@ -27,9 +27,6 @@ namespace Numerics.Mathematics.LinearAlgebra /// References: /// /// "Numerical Recipes: The Art of Scientific Computing, Third Edition." Press et al., 2017. - /// - /// - /// /// /// /// diff --git a/Numerics/Mathematics/Optimization/Global/MLSL.cs b/Numerics/Mathematics/Optimization/Global/MLSL.cs index 91fbf953..68473681 100644 --- a/Numerics/Mathematics/Optimization/Global/MLSL.cs +++ b/Numerics/Mathematics/Optimization/Global/MLSL.cs @@ -245,18 +245,14 @@ protected override void Optimize() // Select the γkN points with the lowest objective function values. // This resultant set, Rk, is called the reduced sample. - // List.Sort is an unstable introspective sort, and the reduced sample is truncated - // exactly at the sort boundary, so ties would decide which points start local searches - // and therefore which optimum is returned. OrderBy is a stable sort, so equally fit - // points keep the order in which they were sampled. Do not replace it with Sort. - // The ordered result is copied back into the existing list rather than assigned as a - // new list, because SampledPoints is public and a caller holding a reference during a - // run would otherwise be left with a detached list that stops growing. Do not replace - // this with an assignment. The trade-off is that the shared list is transiently empty - // here, so a caller enumerating SampledPoints from another thread during a run could - // see a partial list or an invalidated enumerator where the previous code handed it a - // stable detached snapshot; the sort runs on the optimization thread between the - // sequential sample loop and the local searches, so no in-tree caller is affected. + // The reduced sample is truncated exactly at the sort boundary, so ties decide which + // points start local searches; OrderBy is a stable sort, so equally fit points keep the + // order in which they were sampled, where an unstable sort would make the selection + // implementation-defined. The ordered result is copied back into the existing list + // because SampledPoints is public and a caller holding the reference would otherwise be + // left with a detached list that stops growing. The shared list is transiently empty + // during the copy-back; the sort runs on the optimization thread between the sequential + // sample loop and the local searches. var sorted = SampledPoints.OrderBy(x => x.ParameterSet.Fitness).ToList(); SampledPoints.Clear(); SampledPoints.AddRange(sorted); diff --git a/Numerics/Mathematics/Optimization/Local/BFGS.cs b/Numerics/Mathematics/Optimization/Local/BFGS.cs index c268ad66..09ecc318 100644 --- a/Numerics/Mathematics/Optimization/Local/BFGS.cs +++ b/Numerics/Mathematics/Optimization/Local/BFGS.cs @@ -104,26 +104,16 @@ protected override void Optimize() { int D = NumberOfParameters; double EPS = Tools.DoubleMachineEpsilon; - // TOLX is declared here to match Numerical Recipes' dfpmin, but it is never compared against - // anything in this method: the outer parameter-change convergence test of dfpmin, which exits - // when the largest relative parameter step falls below TOLX, is NOT implemented. The only other - // TOLX in this file is a separate local in LineSearchArmijo, where it sets alamin = TOLX / test; - // that is the inner lnsrch step-size floor and is unrelated to outer convergence. Note that - // LineSearchArmijo is currently unreachable: Optimize calls the strong Wolfe LineSearch instead. - // Convergence here is therefore decided solely by CheckConvergence's relative-function-change - // test, so there is no stagnation exit: a run that stops improving its parameters while the - // function value still moves will keep iterating to MaxIterations. Do not assume such an - // exit exists. - // - // Adding the dfpmin test was measured against the full test suite and rejected. It is a large - // efficiency win in isolation, removing 98.9% of BFGS function evaluations across the suite - // (32.4M to 354k) and turning all 40 MaximumIterationsReached runs into Success with no change - // to any individual BFGS optimum. It was rejected because it changes the point the global - // searches report: MultiStart and MLSL share their evaluation counter with the BFGS runs they - // launch, so the wasted iterations currently act as extra sampling that feeds their best-point - // tracking. With the exit in place Test_MultiStart.Test_Eggholder fails, the returned x moving - // from within 1E-2 of 512 to 512.0564. The exit cannot be added until that dependence is - // addressed separately. + // TOLX matches Numerical Recipes' dfpmin but is not consumed here: dfpmin's outer + // parameter-change exit, which stops when the largest relative parameter step falls below + // TOLX, is not implemented. Convergence is decided solely by CheckConvergence's relative + // function-change test, so a run whose parameters stagnate while the function value still + // moves iterates to MaxIterations. Implementing the exit would change the points the global + // searches report: MultiStart and MLSL share their evaluation budget with the local runs + // they launch, and the additional iterations act as extra sampling for their best-point + // tracking. (The TOLX local in LineSearchArmijo is dfpmin's unrelated inner step-size + // floor, and that routine is not called by Optimize, which uses the strong Wolfe + // LineSearch.) double TOLX = 4 * EPS, STPMX = 100.0; bool cancel = false, check = false; diff --git a/Numerics/Mathematics/Optimization/Local/Powell.cs b/Numerics/Mathematics/Optimization/Local/Powell.cs index 1230dae0..1d08db6f 100644 --- a/Numerics/Mathematics/Optimization/Local/Powell.cs +++ b/Numerics/Mathematics/Optimization/Local/Powell.cs @@ -26,9 +26,9 @@ namespace Numerics.Mathematics.Optimization /// zero because the current iterate is feasible, so restricting the search never excludes the current point. The /// extrapolated point used by the direction-set update is scored only when the reflection that produces it is itself /// feasible; an infeasible extrapolation is treated as no improvement and the direction set is left alone for that - /// iteration. Projecting the reflection back onto the box instead was measured and rejected, because the projected point - /// sits on a face where the objective can be far smaller than at the reflection, which makes the acceptance test fire on a - /// point the test was not derived for and admits direction replacements that slow convergence badly on smooth problems. + /// iteration. Projecting the reflection back onto the box would score the acceptance test on a face point where the + /// objective can be far smaller than at the reflection, admitting direction replacements the test was not derived for + /// and slowing convergence badly on smooth problems. /// These restrictions matter most when this class is the local solver inside a global optimizer, because the objective /// function is then the global solver's own evaluation routine and any point it scores can be recorded as the reported /// solution. @@ -152,12 +152,9 @@ protected override void Optimize() } // Construct the extrapolated point and save the average direction moved. // Save the old starting point. - // The reflection through the current point readily leaves the box even when both points are - // inside it, and this point is scored through the objective function, so it has to be kept - // feasible. It is skipped rather than projected back: projection moves the point onto a face - // where the objective can be much smaller than at the reflection, which turns the acceptance - // test below into a test on a different point and admits direction replacements the test was - // never meant to admit. See the remarks on this class. + // The reflection through the current point can leave the box even when both points are + // inside it. It is skipped rather than projected back; see the feasible-region remarks on + // this class. bool extrapolationIsFeasible = true; for (j = 0; j < D; j++) { @@ -166,9 +163,7 @@ protected override void Optimize() xi[j] = p[j] - pt[j]; pt[j] = p[j]; } - // Function evaluated at the extrapolated point. An infeasible extrapolation is treated as no - // improvement, which is the same outcome the acceptance test reaches for a point that does - // not beat the value at the start of this iteration. + // An infeasible extrapolation is treated as no improvement. fptt = extrapolationIsFeasible ? Evaluate(ptt, ref cancel) : double.PositiveInfinity; if (cancel == true) return; if (fptt < fp) @@ -361,9 +356,8 @@ double func(double alpha) /// interval is what prevents that. /// /// - /// The default magnitude is the same 0.1 the routine has always used, and it is kept whenever the - /// interval is wide enough to hold it in either sense, so an interior iterate brackets exactly as - /// before. Only when the interval is narrower than the default in both senses is the magnitude + /// The default magnitude is 0.1, used whenever the interval is wide enough to hold it in either + /// sense. Only when the interval is narrower than the default in both senses is the magnitude /// reduced, to half of the wider side, which is inside the interval and nonzero because the caller /// has already handled the interval that collapses to a point. /// diff --git a/Numerics/Mathematics/Special Functions/Debye.cs b/Numerics/Mathematics/Special Functions/Debye.cs index 2127e360..25488092 100644 --- a/Numerics/Mathematics/Special Functions/Debye.cs +++ b/Numerics/Mathematics/Special Functions/Debye.cs @@ -25,12 +25,6 @@ namespace Numerics.Mathematics.SpecialFunctions /// is the order-1 Debye function D₁. The two are /// different functions and are not interchangeable: D₃(1) = 0.6744156 while D₁(1) = 0.7775046. /// - /// - /// The order of is visible in its own construction. Its small-argument - /// branch is 1 - 0.375 x + 0.05 x², which is the D₃ expansion, where the D₁ expansion is - /// 1 - 0.25 x + x²/36; its large-argument branch normalizes against π⁴/15 = 6 ζ(4), the D₃ limit - /// constant; and its unit test pins Function(1) = 0.6744156, which is D₃(1). - /// /// References: /// /// @@ -147,10 +141,10 @@ public static double Function(double x) /// until the term falls below 1E-20. /// /// - /// Accuracy. Measured against mpmath at 60 decimal digits, evaluated by two independent routes - /// that agree to better than 1E-58, the worst relative error over x in [1E-8, 100] and its negative - /// mirror is 3 ulp, or 3.4E-16 absolute, at x near 1.75. At the endpoints D₁(100) = 0.016449340668482266 matches the - /// asymptote π²/600 = 0.016449340668482264, and the reflection D₁(-1) - D₁(1) = 0.5 holds exactly. + /// Accuracy. Measured against mpmath at 60 decimal digits, the worst relative error over + /// x in [1E-8, 100] and its negative mirror is 3 ulp, or 3.4E-16 absolute, at x near 1.75. At the + /// endpoints D₁(100) = 0.016449340668482266 matches the asymptote π²/600 = 0.016449340668482264, + /// and the reflection D₁(-1) - D₁(1) = 0.5 holds exactly. /// /// public static double FunctionOrderOne(double x) diff --git a/Numerics/Sampling/Bootstrap/Bootstrap.cs b/Numerics/Sampling/Bootstrap/Bootstrap.cs index fd6561dd..e18cc373 100644 --- a/Numerics/Sampling/Bootstrap/Bootstrap.cs +++ b/Numerics/Sampling/Bootstrap/Bootstrap.cs @@ -1267,8 +1267,8 @@ private double[] ComputeAccelerationConstants(double[] populationEstimates) } catch (Exception exception) { - // Count the failed jackknife replicate and keep the first exception so the cause - // survives instead of being discarded silently. + // Count the failed jackknife replicate and keep the first exception as the + // eventual inner exception. failed++; if (firstFailure == null) firstFailure = exception; } @@ -1314,7 +1314,7 @@ private double[] ComputeAccelerationConstants(double[] populationEstimates) // value, so the statistic has no jackknife variation. Zero is the correct limit of the // acceleration there, and BCa degenerates to the bias-corrected interval. // 2. A non-finite second moment. Each leave-one-out statistic is validated as finite - // above, so this can now only arise from overflow while summing finite squared + // above, so this can only arise from overflow while summing finite squared // differences. Zero is a defensive fallback, not a modelling statement. a[i] = secondMoment > 0d && Tools.IsFinite(secondMoment) ? thirdMoment / (Math.Pow(secondMoment, 1.5) * 6d) diff --git a/Numerics/Sampling/MCMC/Base/MCMCSampler.cs b/Numerics/Sampling/MCMC/Base/MCMCSampler.cs index d6055179..275ecdab 100644 --- a/Numerics/Sampling/MCMC/Base/MCMCSampler.cs +++ b/Numerics/Sampling/MCMC/Base/MCMCSampler.cs @@ -165,63 +165,54 @@ public int ThinningInterval /// /// /// - /// Why this is not . The configured iteration count does not describe - /// the work a run actually does, and it under-reports it substantially at the defaults. Two - /// multipliers sit between the two numbers. First, runs + /// The configured under-reports the work a run performs. Two multipliers + /// sit between the two numbers: runs /// ceil( / ) recorded iterations beyond - /// in order to collect the posterior output. Second, every recorded - /// iteration advances the chain times, because thinning is applied by - /// discarding intermediate transitions rather than by discarding recorded draws. Each of those - /// transitions costs at least one evaluation of , so this - /// property, not , is what a runtime estimate should be built on. + /// to collect the posterior output, and every recorded iteration advances + /// the chain times, because thinning discards intermediate + /// transitions rather than recorded draws. Each transition costs at least one evaluation of + /// , so this property, not , is what a + /// runtime estimate should be built on. /// /// - /// Warmup is already included. is a subset of - /// , not an addition to it — ValidateSettings rejects a warmup longer - /// than half of — so it must not be added again when reasoning about total - /// cost. + /// is a subset of , not an addition to it, + /// so it must not be added again when reasoning about total cost. /// /// - /// At the defaults ( = 3,500, = 10,000, + /// At the defaults ( = 3,500, = 10,000, /// = 4, = 20) this is - /// (3,500 + 2,500) × 20 = 120,000 transitions per chain, against a user-facing - /// that reads 3,500 — a factor of roughly 34. + /// (3,500 + 2,500) × 20 = 120,000 transitions per chain, roughly 34 times the configured + /// . /// /// /// The type is because the product overflows well inside the - /// range of settings the sampler accepts: nothing bounds from above, and at - /// the default output length, chain count and thinning interval the per-chain product passes - /// at about 1.074E8 iterations. passes - /// it four times sooner, at about 2.68E7 iterations, because of the multiplication by the default - /// four chains. + /// range of settings the sampler accepts. /// /// - /// This reports the settings as they are currently configured and does not validate them; the - /// settings are checked by ValidateSettings when is called. The one - /// exception is : a value below one is not a samplable configuration and - /// would otherwise divide by zero here, so it reports 0 rather than a framework-dependent number. + /// This reports the settings as currently configured and does not validate them; the settings are + /// checked by ValidateSettings when is called. The one exception is + /// : a value below one is not a samplable configuration and would + /// otherwise divide by zero here, so it reports 0. /// /// - /// The count describes the base loop and the base SampleChain, neither of - /// which any chain sampler in this library overrides. It does not describe - /// , which replaces with a single non-Markovian importance - /// sampling pass and never advances a chain. + /// The count describes the base loop and the base SampleChain, neither + /// of which any chain sampler in this library overrides. It does not describe , + /// which replaces with a single non-Markovian importance sampling pass and + /// never advances a chain. /// /// public long TransitionCount { get { - // NumberOfChains is only validated by ValidateSettings when Sample() runs, but this property - // is readable at any time. At zero chains the division below would be Infinity, and casting - // Infinity to int saturates to int.MaxValue on .NET Core while being unspecified on .NET - // Framework, so the answer would differ by target framework. Report 0 for a configuration - // that cannot be sampled instead. + // NumberOfChains is only validated when Sample() runs, but this property is readable at + // any time. At zero chains the division below would be Infinity, and casting Infinity to + // int saturates on .NET Core while being unspecified on .NET Framework, so report 0 for a + // configuration that cannot be sampled. if (NumberOfChains < 1) return 0L; - // OutputIterations is the same member Sample() uses, so the two cannot drift apart. Each - // recorded iteration advances the chain ThinningInterval times. The sum is widened to long - // before the multiplication so that large settings do not overflow. + // OutputIterations is the member Sample() uses, so the two cannot drift apart. The sum is + // widened to long before the multiplication so that large settings do not overflow. return ((long)Iterations + OutputIterations) * ThinningInterval; } } @@ -231,10 +222,10 @@ public long TransitionCount /// in order to collect the posterior output, ceil( / ). /// /// - /// Shared by and so that the reported work and the - /// work actually performed are computed from a single expression rather than two copies of it. - /// Callers must ensure is at least one; does so via - /// ValidateSettings and guards it directly. + /// Shared by and so the reported and performed + /// work come from a single expression. Callers must ensure is at + /// least one; does so via ValidateSettings and + /// guards it directly. /// private int OutputIterations => (int)Math.Ceiling(OutputLength / (double)NumberOfChains); @@ -243,14 +234,12 @@ public long TransitionCount /// /// × . /// - /// At the defaults this is 120,000 × 4 = 480,000 transitions. Treat it as a lower bound on the - /// evaluation count, not as a budget: every transition costs at least one evaluation of - /// , but a gradient-based sampler such as HMC or NUTS spends many + /// At the defaults this is 120,000 × 4 = 480,000 transitions. Treat it as a lower bound on the + /// evaluation count rather than a budget: every transition costs at least one evaluation of + /// , a gradient-based sampler such as HMC or NUTS spends many /// likelihood and gradient evaluations per transition, and chain initialization adds further - /// evaluations on top of all of these. See - /// for why this differs so widely from . When - /// is true these are distributed across worker threads, so this is - /// the total work rather than the critical path. + /// evaluations on top of these. When is true the transitions are + /// distributed across worker threads, so this is the total work rather than the critical path. /// public long TotalTransitionCount => TransitionCount * NumberOfChains; @@ -299,23 +288,21 @@ public long TransitionCount /// /// /// - /// Thread-safety requirement. When this is true — which is the default — - /// advances all chains inside a + /// When this is true — the default — advances all + /// chains inside a /// , and every chain calls /// the same delegate instance. The log-likelihood is /// therefore invoked concurrently from multiple threads, as is the gradient delegate for - /// gradient-based samplers. That delegate must be thread-safe or stateless. A likelihood that closes - /// over mutable state — an automatic-differentiation tape, a reused workspace or buffer, a native - /// solver handle, a cached factorization, a non-thread-safe PRNG — races by default, and the - /// resulting corruption is silent: it surfaces as an implausible posterior rather than as an + /// gradient-based samplers, so the delegate must be thread-safe or stateless. A likelihood that + /// closes over mutable state — an automatic-differentiation tape, a reused workspace or buffer, a + /// native solver handle, a cached factorization, a non-thread-safe PRNG — races by default, and + /// the resulting corruption is silent: it surfaces as an implausible posterior rather than as an /// exception. /// /// - /// If your likelihood is not thread-safe, set this to false. That is the supported answer, and - /// it is the only one. Per-chain resources cannot be selected from inside the callback, because - /// neither nor the gradient delegate receives a chain index — they take - /// only the parameter vector, so a callback has no way to learn which chain is calling it. Those - /// signatures are part of the public API and are not going to change to add one. + /// Set this to false when the likelihood is not thread-safe. Per-chain resources cannot be + /// selected from inside the callback, because neither nor the + /// gradient delegate receives a chain index — they take only the parameter vector. /// /// public bool ParallelizeChains { get; set; } = true; @@ -580,12 +567,11 @@ protected virtual ParameterSet[] InitializeChains() } // Sort temp population by log-likelihood in descending order. - // List.Sort is an unstable introspective sort, and the chain starting states are taken from - // the front of this list, so ties would decide the starting states and with them the entire - // trajectory of every chain. A wide prior can leave many draws at exactly negative infinity. - // OrderByDescending is a stable sort using the same default double comparison, so the order - // of distinct log-likelihoods is unchanged and ties keep their draw order. Do not replace it - // with Sort. + // The chain starting states are taken from the front of this list, and a wide prior can + // leave many draws tied at exactly negative infinity, so the sort must be stable for ties to + // keep their draw order; an unstable sort would make the starting states, and with them every + // chain trajectory, implementation-defined. OrderByDescending is stable with the same default + // double comparison. tempPopulation = tempPopulation.OrderByDescending(x => x.Fitness).ToList(); // Set the initial vectors to the best performing parameter sets diff --git a/Numerics/Sampling/MCMC/NUTS.cs b/Numerics/Sampling/MCMC/NUTS.cs index 7a805fb4..ea71729b 100644 --- a/Numerics/Sampling/MCMC/NUTS.cs +++ b/Numerics/Sampling/MCMC/NUTS.cs @@ -142,8 +142,7 @@ public NUTS(List priorDistributions, LogLikelihood logL private bool[] _hasPreviousEnergy = Array.Empty(); // Dual averaging hyperparameters (Hoffman & Gelman 2014, Section 3.2). - // DELTA_TARGET is the default of the settable TargetAcceptanceRate, which is what the - // adaptation actually reads. + // The adaptation reads TargetAcceptanceRate; DELTA_TARGET is that property's default. private const double DELTA_TARGET = 0.80; private const double GAMMA = 0.05; private const double T0 = 10.0; @@ -169,10 +168,10 @@ public NUTS(List priorDistributions, LogLikelihood logL /// /// /// A doubling extends the trajectory in one direction, so the join a memo has to cover is - /// between two consecutive leaves and only one entry is strictly needed. Four leaves room for - /// the step-size heuristic, which re-enters the same starting position on every trial step, and - /// for the forward and backward endpoints to be resident at the same time, at a cost of - /// 8 × doubles per chain. + /// between two consecutive leaves and only one entry is strictly needed. A size of four leaves + /// room for the step-size heuristic, which re-enters the same starting position on every trial + /// step, and for the forward and backward endpoints to be resident at the same time, at a cost + /// of 8 × doubles per chain. /// private const int GRADIENT_CACHE_SIZE = 4; @@ -205,8 +204,7 @@ public NUTS(List priorDistributions, LogLikelihood logL /// /// /// The default of 0.80 is the value recommended by Hoffman and Gelman (2014) and is what Stan's - /// adapt_delta defaults to. Leaving it alone reproduces the sampler's historical behaviour - /// exactly. + /// adapt_delta defaults to. /// /// /// Raising it toward 0.90 or 0.95 makes dual averaging settle on a shorter step size, which @@ -222,8 +220,7 @@ public NUTS(List priorDistributions, LogLikelihood logL /// step size cannot fix on its own, such as a funnel, rather than an adaptation failure. /// /// - /// The value is validated when sampling starts, not at assignment, which is the convention the - /// other settings on this class and on follow. + /// The value is validated when sampling starts, not at assignment. /// /// public double TargetAcceptanceRate { get; set; } = DELTA_TARGET; @@ -351,18 +348,15 @@ private double[] ComputeDiagnosticMeans(double[] sums) /// width, which is what lets one step size serve a posterior whose parameters differ in scale. /// /// - /// The default is because an identity metric forces the step size to track the - /// narrowest direction while the trajectory has to span the widest, so on an ill-conditioned posterior - /// NUTS saturates on nearly every transition. On a 50-parameter Gaussian - /// whose standard deviations span 1e-3 to 1e1, adaptation reduces the cost from about - /// 4,090 leapfrog steps per transition to 7 and removes every maximum-tree-depth hit. On small, - /// well-conditioned fits the metric has little to correct and the adaptation costs up to about 40% - /// more leapfrog steps per transition, which is the price of the general case. + /// With an identity metric the step size must track the narrowest posterior direction while the + /// trajectory has to span the widest, so on an ill-conditioned posterior NUTS saturates + /// on nearly every transition; adaptation removes that failure mode. + /// On small, well-conditioned fits the metric has little to correct and the adaptation can cost + /// up to about 40% more leapfrog steps per transition. /// /// /// Set this to to sample with the fixed metric supplied through - /// . Doing so reproduces the sampler's behaviour exactly as it was before this - /// property defaulted to . + /// . /// /// public bool AdaptMassMatrix { get; set; } = true; @@ -606,58 +600,39 @@ private double TrySingleStepLogAcceptance(double[] theta0, double[] r0, double[] /// The gradient. The array is owned by the memo and must be treated as read-only by the caller. /// It is valid only until the next miss on this chain: a hit does not advance the ring, so a hit /// on the slot the ring is currently pointing at is overwritten by the very next miss. Both - /// callers consume the array before evaluating again, which is what makes that safe. + /// callers consume the array before evaluating again. /// /// /// - /// Why there is anything to reuse. A leapfrog step evaluates the gradient twice, once at - /// its opening half-step and once at the position it lands on. Consecutive leaves of a doubling - /// chain end to end, so leaf k lands on the position leaf k+1 opens from, and - /// without a memo leaf k+1 recomputes what leaf k already produced and threw away. - /// The step-size heuristic re-enters the same starting position on every trial step and repeats - /// the same waste. With the default finite-difference gradient each of those evaluations costs - /// on the order of 2 × log-likelihood evaluations. + /// Consecutive leaves of a doubling chain end to end, so each leapfrog step opens from the + /// position the previous step landed on and would otherwise recompute its gradient, and the + /// step-size heuristic re-enters the same starting position on every trial step. With the + /// default finite-difference gradient each avoided evaluation saves on the order of + /// 2 × log-likelihood evaluations. /// /// - /// Why reuse cannot move a draw. Positions are compared bitwise through - /// , never with == and never within a - /// tolerance, so a hit is only possible at the exact point the delegate was called on and - /// returns exactly the value a recomputation would produce. Bitwise comparison also keeps - /// +0 and -0 distinct, which == would not, so a sign of zero cannot be - /// silently substituted. A tolerance-based memo would change results and is not what this is. + /// Positions are compared bitwise through , + /// never with == and never within a tolerance, so a hit is possible only at the exact + /// point the delegate was called on and returns exactly the value a recomputation would produce, + /// with +0 and -0 kept distinct. The memo requires the gradient delegate to be a + /// deterministic function of its argument, which a seeded, reproducible run already requires. /// /// - /// Why the metric is not part of the key. takes a position - /// and nothing else, is fixed at construction, and the default implementation closes only over - /// the log-likelihood and the prior bounds. The gradient of the log-density is therefore a - /// function of position alone: it does not depend on the mass matrix, the step size, the - /// adaptation window, or the chain. An entry recorded under one metric is exactly as valid after - /// replaces the metric, which matters because that method re-runs - /// at the end of every adaptation window. The chain index is - /// part of the key for thread safety, not for correctness of the value. + /// The metric is not part of the key: takes a position and + /// nothing else, so the gradient does not depend on the mass matrix, the step size, or the + /// adaptation window, and an entry recorded under one metric remains valid after + /// replaces the metric. The chain index is part of the key for + /// thread safety: every array is indexed by chain first, and + /// gives each chain index to exactly one iteration + /// at a time. /// /// - /// Assumption. The gradient delegate must be a deterministic function of its argument. - /// That is already required for a seeded run to be reproducible, and the sampler is not usable - /// without it, but it is the one property this memo depends on. - /// - /// - /// Thread safety. Every array is indexed by chain first, and - /// gives each chain index to exactly one - /// iteration at a time, so no two threads touch - /// one chain's slots. The join at the end of each parallel iteration publishes the writes. - /// - /// - /// Copy semantics. Both the position and the gradient are copied into the memo. The - /// caller's position array is mutated in place by , and a - /// caller-supplied gradient delegate is free to return the same every call, - /// so neither may be retained by reference. The position is copied before the delegate - /// runs, so a delegate that writes through its argument cannot pair a mutated position with the - /// gradient of the point that was actually evaluated. - /// - /// - /// A gradient whose length does not match the parameter count is returned without being stored, - /// so a malformed delegate still fails where and how it failed before. + /// Both the position and the gradient are copied into the memo: the caller's position array is + /// mutated in place by , and a caller-supplied gradient delegate is + /// free to return the same every call, so neither may be retained by + /// reference. The position is copied before the delegate runs, so a delegate that writes through + /// its argument cannot pair a mutated position with the gradient of a different point. A + /// gradient whose length does not match the parameter count is returned without being stored. /// /// private double[] EvaluateGradient(double[] position, int chainIndex) @@ -685,11 +660,9 @@ private double[] EvaluateGradient(double[] position, int chainIndex) if (identical) return values[slot]; } - // A miss claims a slot and records the position it is about to evaluate at before calling - // the delegate, so a delegate that writes through its argument cannot leave the memo - // holding a mutated position paired with the gradient of the original point. The slot is - // marked empty for the whole window, so a throw or a malformed length leaves nothing that - // could be matched later. + // A miss claims a slot and records the position before the delegate runs, and the slot stays + // marked empty for the whole window, so a throw, a malformed length, or a delegate that + // writes through its argument leaves nothing that could be matched later. int next = _gradientCacheNextSlot[chainIndex]; var slotPosition = positions[next]; var slotValue = values[next]; @@ -698,8 +671,6 @@ private double[] EvaluateGradient(double[] position, int chainIndex) for (int j = 0; j < D; j++) slotPosition[j] = position[j]; - // Anything the delegate throws propagates untouched, so the failure paths in BuildTree and - // TrySingleStepLogAcceptance are unchanged. double[] gradient = GradientFunction(position).Array; if (gradient.Length != D) return gradient; @@ -998,42 +969,41 @@ private void AccumulateWelfordStatistics(int chainIndex, double[] sample) /// The current parameter state, used to find a new reasonable step size after the metric change. /// /// - /// Metric convention. The estimated posterior variance is the inverse mass, not the mass. - /// This class draws momentum with standard deviation sqrt(M), evaluates kinetic energy as - /// 0.5 * r' * M^-1 * r, and flows position as q += M^-1 * r * epsilon. A coordinate with - /// posterior standard deviation s therefore needs M = 1 / s^2 for its leapfrog step to - /// scale like s. The window variance is stored in and its - /// reciprocal in , which is the same correspondence Stan uses when it keeps - /// the estimated variance in inv_e_metric. + /// The estimated posterior variance is the inverse mass, not the mass. This class draws + /// momentum with standard deviation sqrt(M), evaluates kinetic energy as + /// 0.5 * r' * M^-1 * r, and flows position as q += M^-1 * r * epsilon. A coordinate + /// with posterior standard deviation s therefore needs M = 1 / s^2 for its leapfrog + /// step to scale like s. The window variance is stored in + /// and its reciprocal in , the same correspondence Stan uses when it + /// keeps the estimated variance in inv_e_metric. /// /// - /// Regularization. Stan shrinks the window variance toward a small absolute constant because it - /// works in an unconstrained space where the variance is O(1). This sampler works on the natural scale, - /// where no absolute constant is meaningful, so the window variance is used directly whenever it is + /// Stan shrinks the window variance toward a small absolute constant because it works in an + /// unconstrained space where the variance is O(1). This sampler works on the natural scale, where + /// no absolute constant is meaningful, so the window variance is used directly whenever it is /// usable and the prior-scaled fallback (priorRange / 6)^2 is engaged only when it is not. - /// Blending the fallback in unconditionally would impose a floor tied to the prior width rather than to - /// the posterior, which on a diffuse prior flattens the metric to isotropy and erases the adaptation. + /// An unconditional blend would impose a floor tied to the prior width rather than the posterior, + /// which on a diffuse prior flattens the metric to isotropy and erases the adaptation. /// /// - /// Guards. Two conditions decide that a window cannot produce a usable variance. First, the window - /// must contain at least draws: the relative standard error of a - /// sample variance is approximately sqrt(2 / (n - 1)), which is still about 47% at ten draws and - /// worse below that, so a shorter window is noise rather than an estimate. Second, the variance must be - /// finite and strictly positive. Windows failing either condition take the fallback. With the shipped - /// buffer sizes the first condition is only reachable at a total warmup of twelve transitions or fewer, - /// so in practice it guards the degenerate configuration rather than a routine one. + /// Two conditions decide that a window cannot produce a usable variance. First, the window must + /// contain at least draws: the relative standard error of a + /// sample variance is approximately sqrt(2 / (n - 1)), still about 47% at ten draws, so a + /// shorter window is noise rather than an estimate. Second, the variance must be finite and + /// strictly positive. Windows failing either condition take the fallback. With the shipped buffer + /// sizes the first condition is only reachable at a total warmup of twelve transitions or fewer. /// /// /// Retained variances are then floored at times the largest - /// measured variance in the same window; fallback values never set that scale, because letting - /// them do so would put the prior range back into the floor for every other coordinate. Stating the - /// guarantee precisely: this bounds the diagonal metric's condition number at 1e12 and prevents a - /// coordinate that is numerically degenerate over the window from producing an unbounded mass. It does - /// not correct a coordinate that merely under-explored — a variance that comes back at 1e-4 to - /// 1e-6 of the truth is four to six orders of magnitude above the floor and passes through untouched, - /// yielding a mass that is too large and a step that is too small, which persists until the next window - /// re-estimates it. A floor tight enough to catch that case would have to encode an expectation about - /// how well the window mixed, which is exactly the prior-scaled assumption this method removes. + /// measured variance in the same window; fallback values never set that scale, because + /// letting them do so would put the prior range back into the floor for every other coordinate. + /// The floor bounds the diagonal metric's condition number at 1e12 and prevents a coordinate that + /// is numerically degenerate over the window from producing an unbounded mass. It does not + /// correct a coordinate that merely under-explored: a variance that comes back at 1e-4 to 1e-6 of + /// the truth is far above the floor and passes through, yielding a mass that is too large and a + /// step that is too small until the next window re-estimates it. A floor tight enough to catch + /// that case would have to encode an expectation about how well the window mixed, which is the + /// prior-scaled assumption this method avoids. /// /// private void UpdateMassMatrix(int chainIndex, ParameterSet currentState) diff --git a/Numerics/Sampling/MCMC/SNIS.cs b/Numerics/Sampling/MCMC/SNIS.cs index e1bad703..349ad285 100644 --- a/Numerics/Sampling/MCMC/SNIS.cs +++ b/Numerics/Sampling/MCMC/SNIS.cs @@ -84,21 +84,15 @@ protected override void ValidateSettings() /// /// /// - /// The resampling list is ordered by Fitness, not by Weight. After the - /// normalized posterior weights are computed, the sample list is sorted ascending on + /// The resampling list is ordered by Fitness, not by Weight. After the normalized + /// posterior weights are computed, the sample list is sorted ascending on /// ParameterSet.Fitness, and the resampling CDF is then accumulated from - /// ParameterSet.Weight in that order. The two fields do not always hold the same quantity. - /// With no importance distribution supplied the weight is set to the log-likelihood itself, so - /// Fitness and Weight coincide and the ordering is monotone in both. When an - /// importance distribution is supplied the weight becomes the log-likelihood minus the proposal - /// log-density, so the two diverge and the list is not sorted by the quantity the CDF accumulates. - /// - /// - /// Inverse-CDF resampling does not require a sorted CDF to be correct — the CDF is non-decreasing - /// regardless, because every increment is clamped non-negative — so this affects which sample each - /// plotting position selects, not the validity of the draw. Whether Fitness or Weight - /// is the intended sort key is an open question pending review, so the behaviour is deliberately - /// left unchanged; do not "fix" the comparator without that decision. + /// ParameterSet.Weight in that order. With no importance distribution supplied the weight + /// is the log-likelihood itself, so the two keys coincide; with an importance distribution the + /// weight is the log-likelihood minus the proposal log-density, so the list is not ordered by the + /// quantity the CDF accumulates. The CDF is non-decreasing either way, because every increment is + /// clamped non-negative, so the ordering affects which sample each plotting position selects + /// rather than the validity of the draw. /// /// public override void Sample() @@ -187,11 +181,8 @@ public override void Sample() MarkovChains[0][idx] = new ParameterSet(MarkovChains[0][idx].Values, MarkovChains[0][idx].Fitness, w); }); - // Sort the list in ascending order of Fitness (the log-likelihood), which is NOT the same key - // the CDF below accumulates: that runs on Weight. The two coincide only when no importance - // distribution was supplied, where weight = logLH; with one, weight = logLH - mvn.LogPDF, so - // the list is not ordered by the accumulated quantity. See the remarks on Sample(). Which of - // the two is the intended key is pending review, so this comparator is deliberately unchanged. + // The list is sorted ascending on Fitness while the CDF below accumulates Weight; the two + // keys coincide only when no importance distribution is supplied. See the remarks on Sample(). MarkovChains[0].Sort((x, y) => x.Fitness.CompareTo(y.Fitness)); var cdf = new double[Iterations]; cdf[0] = Math.Max(0.0, MarkovChains[0][0].Weight); From 1a1bd1ec15e08eb1dbd5a798d97dbf382d46d89e Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 26 Aug 2026 10:29:25 -0600 Subject: [PATCH 096/222] State the test documentation as invariants rather than change history --- .../Data/Statistics/Test_Statistics.cs | 48 ++++++++++--------- .../Multivariate/Test_MultivariateNormal.cs | 41 ++++++++-------- .../Test_CholeskyDecomposition.cs | 10 ++-- .../Test_MatrixRegularization.cs | 14 +++--- .../Optimization/Global/Test_MLSL.cs | 20 ++++---- .../Optimization/Global/Test_MultiStart.cs | 8 ++-- .../Optimization/Local/Test_Powell.cs | 9 ++-- .../Sampling/MCMC/Test_NUTS_MassMatrix.cs | 20 ++++---- 8 files changed, 87 insertions(+), 83 deletions(-) diff --git a/Test_Numerics/Data/Statistics/Test_Statistics.cs b/Test_Numerics/Data/Statistics/Test_Statistics.cs index aeb70580..4b5743da 100644 --- a/Test_Numerics/Data/Statistics/Test_Statistics.cs +++ b/Test_Numerics/Data/Statistics/Test_Statistics.cs @@ -404,29 +404,30 @@ public void Test_ComputeLinearMoments_LargeSample() /// accumulated across n terms, independent of how small τ₄ itself is. Measured against the exact /// oracle, the library agrees to 0 ulp on λ₁, 1.1E-15 relative on λ₂ and 2.5E-14 relative on τ₃, but /// only to 1.9E-14 and 2.7E-14 absolute on τ₄ — which at τ₄ ≈ 2.2E-05 is 8.5E-10 in relative - /// terms. This was verified to be arithmetic and not a defect by transcribing the library's formula - /// into an independent double-precision evaluation, which reproduces the library's returned value - /// digit for digit. The tolerance below is therefore relative 1E-12 with an absolute floor of 1E-13, - /// roughly four times the worst measured deviation. That floor costs the test nothing: the overflow - /// it guards moved τ₄ by 1.9E-01 and 1.7E+01, twelve to fifteen orders of magnitude above it. + /// terms. The deviation is accumulation arithmetic, not a defect: an independent double-precision + /// evaluation of the same formula reproduces the returned value digit for digit. The tolerance + /// below is therefore relative 1E-12 with an absolute floor of 1E-13, roughly four times the worst + /// measured deviation. The floor costs the test nothing: the overflow it guards moves τ₄ by + /// 1.9E-01 and 1.7E+01, twelve to fifteen orders of magnitude above it. /// /// - /// What is being guarded. The probability-weighted-moment numerators were formed in 32-bit - /// integer arithmetic and wrapped silently on large samples (USACE-RMC/Numerics#146). On this same - /// sample the pre-fix code returned τ₄ = −0.185 at n = 1293 against an exact −3.80E−06, and - /// τ₄ = −17.01 at n = 1460 against an exact +1.40E−04. τ₄ is bounded roughly in [−0.25, 1] for any real + /// What is being guarded. With the probability-weighted-moment numerators formed in 32-bit + /// integer arithmetic, the products wrap silently on large samples (USACE-RMC/Numerics#146): on + /// this same sample τ₄ comes back as −0.185 at n = 1293 against an exact −3.80E−06, and as −17.01 + /// at n = 1460 against an exact +1.40E−04. τ₄ is bounded roughly in [−0.25, 1] for any real /// distribution, so −17.01 is not an imprecise answer but a meaningless one. /// /// /// The three lengths are chosen to straddle the overflow threshold. n = 1292 is the last - /// length whose b₃ numerator fits in an , so the pre-fix code is bit-compatible - /// there; that case pins the correct answer but does not by itself guard the bug. n = 1293 is the - /// first length that overflows, so it pins the threshold itself: exact τ₄ is −3.797E−06 against a - /// pre-fix −0.185. n = 1460 is four years of daily data, the scenario the issue reports as - /// triggering it in practice. n = 1293 is also the clearest evidence that the tolerance floor below - /// is the right shape: τ₄ there is six times smaller than at n = 1292, yet the absolute error is - /// unchanged in order (1.56E-14 against 1.88E-14), which is what a magnitude-independent - /// cancellation floor looks like and is not what a relative error bound would predict. + /// length whose b₃ numerator fits in an , so integer arithmetic is exact there; + /// that case pins the correct answer but does not by itself guard the bug. n = 1293 is the first + /// length that overflows, so it pins the threshold itself: exact τ₄ is −3.797E−06 where the + /// wrapped products give −0.185. n = 1460 is four years of daily data, the scenario the issue + /// reports as triggering it in practice. n = 1293 is also the clearest evidence that the tolerance + /// floor below is the right shape: τ₄ there is six times smaller than at n = 1292, yet the + /// absolute error is unchanged in order (1.56E-14 against 1.88E-14), which is what a + /// magnitude-independent cancellation floor looks like and is not what a relative error bound + /// would predict. /// /// [TestMethod] @@ -463,8 +464,9 @@ public void Test_ComputeLinearMoments_ExactOracle() $"{names[m]} disagrees with the exact oracle at n = {n}."); } - // τ₄ is bounded roughly in [-0.25, 1] for any real distribution. The pre-fix code returned - // -17.01 at n = 1460, so this alone separates a corrupted result from a merely imprecise one. + // τ₄ is bounded roughly in [-0.25, 1] for any real distribution, while the wrapped + // integer products give -17.01 at n = 1460, so this alone separates a corrupted result + // from a merely imprecise one. Assert.IsTrue(lmoms[3] > -0.25d && lmoms[3] < 1d, $"L-kurtosis (τ₄) is out of range at n = {n}."); } } @@ -516,10 +518,10 @@ public void Test_Percentiles() /// Test that the Percentile methods reject a null sample and a non-finite percentile. /// /// - /// Every comparison against NaN is false, so a NaN percentile used to pass the range check and - /// reach the interpolation index. On .NET Core the float-to-int conversion saturates and the - /// method silently returned NaN; on .NET Framework the conversion is undefined and the indexer - /// threw. Both infinities were already rejected by the range check. + /// Every comparison against NaN is false, so a NaN percentile must be rejected explicitly: without + /// the explicit test it passes the range check and reaches the interpolation index, where the + /// float-to-int conversion saturates on .NET Core and is undefined on .NET Framework. Both + /// infinities are rejected by the range check alone. /// [TestMethod] public void Test_Percentile_InvalidArguments() diff --git a/Test_Numerics/Distributions/Multivariate/Test_MultivariateNormal.cs b/Test_Numerics/Distributions/Multivariate/Test_MultivariateNormal.cs index 0f7a23a1..6c82fc4e 100644 --- a/Test_Numerics/Distributions/Multivariate/Test_MultivariateNormal.cs +++ b/Test_Numerics/Distributions/Multivariate/Test_MultivariateNormal.cs @@ -837,8 +837,8 @@ public void Test_SVD_Case3_SingularRankOneMatchesScipy() /// is 0.9189385332046727 per deficient dimension, a factor of 2.5066282746310002 on the density. /// Cases 2 and 3 are each rank-deficient by exactly one, so that bug would put every one of their /// oracle values off by that fixed amount. This test asserts both that the oracle value is - /// reproduced and that the value the old constant would have produced is exactly that far away, so - /// it fails loudly if the constant ever reverts. + /// reproduced and that the value the dimension-based constant would produce is exactly that far + /// away, so it fails loudly if the constant ever regresses. /// [TestMethod] public void Test_SVD_NormalizingConstantUsesRankNotDimension() @@ -1088,23 +1088,23 @@ public void Test_SVD_ValidationAcceptsSemiDefiniteAndRejectsIndefinite() } /// - /// Verifies that the Cholesky path now rejects both singular reference covariances, and that the - /// singular value path returns the correct density for the one that used to slip through. + /// Verifies that the Cholesky path rejects both singular reference covariances, and that the + /// singular value path returns the correct density for the one the absolute pivot test accepts. /// /// /// - /// Case 3, the rank-one covariance, always failed cleanly: the Cholesky factorization reaches a - /// pivot of exactly zero and throws. + /// Case 3, the rank-one covariance, fails cleanly at any tolerance: the Cholesky factorization + /// reaches a pivot of exactly zero and throws. /// /// - /// Case 2, the rank-two covariance, did not throw before this was fixed. Its final pivot - /// evaluates to 4.440892E-16 rather than exactly zero, so under the purely absolute - /// pivot <= 0 test the factorization completed with a factor entry of order 1E-8 and - /// reported the matrix positive-definite. The resulting log density at the origin was - /// +14.638629610696878 against the correct -2.4642585506570285 — wrong by 17.1 nats, a factor of - /// about 2.7E+7 on the density — with no exception, no warning and no flag. That silent wrong answer - /// is the failure mode issue #145 describes, and it is what the scale-relative pivot test in - /// now catches: the pivot ratio is 2.220446E-16 against a + /// Case 2, the rank-two covariance, is the hazardous one. Its final pivot evaluates to + /// 4.440892E-16 rather than exactly zero, so under a purely absolute pivot <= 0 test the + /// factorization completes with a factor entry of order 1E-8 and reports the matrix + /// positive-definite; the resulting log density at the origin is +14.638629610696878 against the + /// correct -2.4642585506570285 — wrong by 17.1 nats, a factor of about 2.7E+7 on the density — + /// with no exception, no warning and no flag. That silent wrong answer is the failure mode issue + /// #145 describes, and it is what the scale-relative pivot test in + /// catches: the pivot ratio is 2.220446E-16 against a /// tolerance of 6.661338E-16 at this dimension. /// /// @@ -1117,11 +1117,11 @@ public void Test_SVD_ValidationAcceptsSemiDefiniteAndRejectsIndefinite() [TestMethod] public void Test_Cholesky_RejectsSingularCovariances() { - // Case 3: a clean failure, unchanged — the pivot is exactly zero. + // Case 3: the pivot is exactly zero, so any tolerance rejects it. var case3Exception = AssertThrowsAny(() => new MultivariateNormal(new[] { 0d, 0d }, Case3Covariance)); Assert.IsGreaterThanOrEqualTo(0, case3Exception.Message.IndexOf("positive-definite", StringComparison.OrdinalIgnoreCase)); - // Case 2: now rejected rather than silently factorized. + // Case 2: rejected by the scale-relative pivot test. var case2Exception = AssertThrowsAny(() => new MultivariateNormal(new[] { 0d, 0d, 0d }, Case2Covariance)); Assert.IsGreaterThanOrEqualTo(0, case2Exception.Message.IndexOf("positive-definite", StringComparison.OrdinalIgnoreCase)); @@ -1135,7 +1135,8 @@ public void Test_Cholesky_RejectsSingularCovariances() var singular = new MultivariateNormal(new[] { 0d, 0d, 0d }, Case2Covariance, DecompositionMethod.SingularValue); Assert.AreEqual(oracle, singular.LogPDF(new[] { 0d, 0d, 0d }), OracleTolerance); - // The old behaviour is still reachable through the explicit zero tolerance, and it is still wrong. + // A zero tolerance selects the purely absolute pivot test, which accepts this matrix and + // returns a wrong determinant. var legacy = new CholeskyDecomposition(new Matrix(Case2Covariance), 0d); Assert.IsTrue(legacy.IsPositiveDefinite); Assert.AreEqual(-34.790890420621793d, legacy.LogDeterminant(), 1E-5d); @@ -1172,7 +1173,7 @@ public void Test_Decomposition_ClonePreservesTheSelector() } /// - /// Verifies that the paths the selector deliberately does not govern behave as they do today: the + /// Verifies the paths the selector does not govern: the /// Genz MVNDST integrator behind factorizes the correlation /// matrix internally, and and /// factorize the sub-covariance with their own Cholesky @@ -1247,8 +1248,8 @@ public void Test_SVD_RoundoffNegativeEigenvalueIsZeroedNotKept() Assert.AreEqual(-1.737085713764618d, singular.LogPDF(new[] { 1d, 0d }), OracleTolerance); Assert.AreEqual(Math.Exp(-1.612085713764618d), singular.PDF(new[] { 0d, 0d }), 1E-15d); - // The support is the x-axis, so any point off it has zero density. Before the fix this - // returned a finite value, because the zeroed direction was still being sampled and inverted. + // The support is the x-axis, so any point off it has zero density; a finite value here would + // mean the zeroed direction is still being sampled and inverted. Assert.AreEqual(double.NegativeInfinity, singular.LogPDF(new[] { 0d, 1d })); Assert.AreEqual(0d, singular.PDF(new[] { 0d, 1d }), 0d); diff --git a/Test_Numerics/Mathematics/Linear Algebra/Test_CholeskyDecomposition.cs b/Test_Numerics/Mathematics/Linear Algebra/Test_CholeskyDecomposition.cs index d189313a..b2f0d142 100644 --- a/Test_Numerics/Mathematics/Linear Algebra/Test_CholeskyDecomposition.cs +++ b/Test_Numerics/Mathematics/Linear Algebra/Test_CholeskyDecomposition.cs @@ -298,14 +298,14 @@ public void Test_RejectsExactlyRankDeficientMatrix() } /// - /// Pins the pre-existing behaviour of the absolute pivot test, reachable by passing a zero tolerance. + /// Pins the behaviour of the absolute pivot test, reachable by passing a zero tolerance. /// /// /// With relativeTolerance = 0 the test reduces to pivot <= 0 exactly, so the rank-two - /// covariance still factorizes and still yields the wrong answers it always did: a final factor entry - /// of 2.107342425544702E-08 and a log determinant of -34.790890420621793, against a true log - /// pseudo-determinant of 1.2527629684953678. This test exists so that the zero-tolerance escape hatch - /// is demonstrably identical to the old behaviour, not so that the old behaviour is endorsed. + /// covariance factorizes and yields a final factor entry of 2.107342425544702E-08 and a log + /// determinant of -34.790890420621793, against a true log pseudo-determinant of + /// 1.2527629684953678. This test pins the zero-tolerance escape hatch to the absolute pivot test + /// bit for bit. /// [TestMethod] public void Test_ZeroToleranceReproducesTheAbsolutePivotTest() diff --git a/Test_Numerics/Mathematics/Linear Algebra/Test_MatrixRegularization.cs b/Test_Numerics/Mathematics/Linear Algebra/Test_MatrixRegularization.cs index 1ba4ade5..de9dd02f 100644 --- a/Test_Numerics/Mathematics/Linear Algebra/Test_MatrixRegularization.cs +++ b/Test_Numerics/Mathematics/Linear Algebra/Test_MatrixRegularization.cs @@ -19,16 +19,16 @@ namespace Mathematics.LinearAlgebra /// symmetrizes its input, then adds a /// trace-scaled ridge of 1E-10 * trace / p and retries with the ridge multiplied by ten each time /// the factorization is rejected, up to eight attempts. Tightening the Cholesky pivot test could in - /// principle make the loop reject a matrix it used to accept, escalate to a larger ridge, and return a - /// different matrix to every downstream consumer. These tests pin the returned matrix so that any such - /// escalation shows up as a failure rather than as a silent change in a fitted result. + /// principle make the loop reject a matrix the absolute test accepts, escalate to a larger ridge, and + /// return a different matrix to every downstream consumer. These tests pin the returned matrix so that + /// any such escalation shows up as a failure rather than as a silent change in a fitted result. /// /// /// The loop is structurally immune to the scale-relative pivot test at any realistic dimension. For a /// positive semi-definite input the ridged matrix has a smallest eigenvalue of at least the ridge, so /// every pivot is at least 1E-10 * trace / p while no diagonal entry exceeds the trace. The pivot /// ratio is therefore at least 1E-10 / p, against a tolerance of p * 2^-52. Those two cross - /// only near p = 671; below that the first attempt always succeeds, exactly as it does today. + /// only near p = 671; below that the first attempt always succeeds. /// /// [TestClass] @@ -87,12 +87,12 @@ public void Test_MakeSymmetricPositiveDefinite_WellConditionedTakesTheBaseRidge( /// Verifies that an exactly rank-deficient input is still resolved by the base ridge. /// /// - /// This is the case the scale-relative pivot test was introduced for: the third row of the input + /// This is the motivating case for the scale-relative pivot test: the third row of the input /// equals the first, so the raw matrix is exactly rank two. The base ridge of /// 1E-10 * 5 / 3 = 1.666667E-10 lifts the smallest eigenvalue clear of the tolerance — /// the final pivot ratio is 1.666668E-10 against a tolerance of 6.66E-16 — so the first attempt - /// succeeds under both the old absolute test and the new relative one, and the returned matrix is - /// unchanged. + /// succeeds under both the absolute and the scale-relative pivot tests, and the returned matrix + /// is the same under either. /// [TestMethod] public void Test_MakeSymmetricPositiveDefinite_RankDeficientTakesTheBaseRidge() diff --git a/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs b/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs index 98087161..c309e4a9 100644 --- a/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs +++ b/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs @@ -536,12 +536,12 @@ public void Test_InitialValuesAreNotMutatedByARun() CollectionAssert.AreEqual(callerSnapshot, initial, "The caller's own array must not be modified by a run."); CollectionAssert.AreEqual(callerSnapshot, solver.InitialValues, "InitialValues must still equal what was passed to the constructor after a run."); - // Reference identity is the assertion that actually discriminates here. The value - // comparisons above cannot detect a regression of this fix: the constructor rejects - // out-of-bounds initial values, so the bounds repair inside the local solver is a no-op - // for any legally constructed MLSL, and the first sampled point is added with - // Minimized = true, which the local-search loop skips. Both aliasing paths are therefore - // inert for legal use, and only the aliasing itself is observable. + // Reference identity is the assertion that discriminates here. The value comparisons above + // are blind to the aliasing: the constructor rejects out-of-bounds initial values, so the + // bounds repair inside the local solver is a no-op for any legally constructed MLSL, and the + // first sampled point is added with Minimized = true, which the local-search loop skips. + // Both aliasing paths are therefore inert for legal use, and only the aliasing itself is + // observable. // // The whole collection is searched rather than element zero, because the run sorts // SampledPoints by fitness and rebuilds the list, so the initial point does not stay @@ -575,10 +575,10 @@ private static double CornerQuadratic(double[] x) /// that leaves the box scores better than the constrained corner and would be reported. /// /// - /// Both settings of are covered because the two used to fail differently. - /// Without polishing, the reported point was the infeasible probe itself. With polishing, the - /// reported point was repaired back to the corner while the fitness recorded at the infeasible probe - /// was kept, which produced a feasible-looking point carrying a fitness from somewhere else. + /// Both settings of are covered because the two can fail in different + /// ways: without polishing, an unguarded solver reports the infeasible probe itself; with + /// polishing, the probe's point is repaired into the box while its fitness is kept, producing a + /// feasible-looking point carrying a fitness from somewhere else. /// /// /// The fitness comparison is exact rather than approximate. stores diff --git a/Test_Numerics/Mathematics/Optimization/Global/Test_MultiStart.cs b/Test_Numerics/Mathematics/Optimization/Global/Test_MultiStart.cs index 896fa8d6..848f2f92 100644 --- a/Test_Numerics/Mathematics/Optimization/Global/Test_MultiStart.cs +++ b/Test_Numerics/Mathematics/Optimization/Global/Test_MultiStart.cs @@ -445,10 +445,10 @@ private static double CornerQuadratic(double[] x) /// that leaves the box scores better than the constrained corner and would be reported. /// /// - /// Both settings of are covered because the two used to fail - /// differently. Without polishing, the reported point was the infeasible probe itself. With polishing, - /// the reported point was repaired back to the corner while the fitness recorded at the infeasible - /// probe was kept, which produced a feasible-looking point carrying a fitness from somewhere else. + /// Both settings of are covered because the two can fail in + /// different ways: without polishing, an unguarded solver reports the infeasible probe itself; + /// with polishing, the probe's point is repaired into the box while its fitness is kept, + /// producing a feasible-looking point carrying a fitness from somewhere else. /// /// /// The fitness comparison is exact rather than approximate. stores diff --git a/Test_Numerics/Mathematics/Optimization/Local/Test_Powell.cs b/Test_Numerics/Mathematics/Optimization/Local/Test_Powell.cs index fc4391a1..487485b9 100644 --- a/Test_Numerics/Mathematics/Optimization/Local/Test_Powell.cs +++ b/Test_Numerics/Mathematics/Optimization/Local/Test_Powell.cs @@ -229,10 +229,11 @@ private static double CornerQuadratic(double[] x) /// /// /// - /// This covers both places the solver used to leave the box. The line search used to hand the step - /// length straight to a bracketing routine that expands geometrically without regard for the bounds, - /// and the extrapolated point built for the direction-set update is a reflection through the current - /// point, which leaves the box readily even when both points forming it are inside. + /// This covers both paths that can produce an out-of-box evaluation: the line search drives a + /// bracketing routine that expands geometrically without regard for the bounds and must be + /// confined to the feasible step interval, and the extrapolated point built for the direction-set + /// update is a reflection through the current point, which leaves the box readily even when both + /// points forming it are inside. /// /// /// Both matter because this class is used as the local solver inside and diff --git a/Test_Numerics/Sampling/MCMC/Test_NUTS_MassMatrix.cs b/Test_Numerics/Sampling/MCMC/Test_NUTS_MassMatrix.cs index e2e47f31..b9a065aa 100644 --- a/Test_Numerics/Sampling/MCMC/Test_NUTS_MassMatrix.cs +++ b/Test_Numerics/Sampling/MCMC/Test_NUTS_MassMatrix.cs @@ -165,9 +165,9 @@ private static double RecoveredStandardDeviation(NUTS sampler, int coordinate) /// /// The stated band is recovered sd / true sd in [0.75, 1.30] on all twenty coordinates. /// It is wide enough to absorb single-chain Monte Carlo error on 2,000 draws and any - /// cross-framework divergence of this chaotic trajectory, and narrow enough that the - /// pre-fix sampler fails it: with the additive prior-scaled floor in place the widest - /// coordinate returns about 0.33 to 0.37 of its true width. + /// cross-framework divergence of this chaotic trajectory, and narrow enough to discriminate: + /// with an additive prior-scaled floor in place the widest coordinate returns about 0.33 to + /// 0.37 of its true width and fails the band. /// [TestMethod] public void Test_NUTS_AdaptedMetric_RecoversKnownScales() @@ -198,10 +198,10 @@ public void Test_NUTS_AdaptedMetric_RecoversKnownScales() /// a factor of four on the variance, which is a factor of two on the standard deviation. /// /// - /// It fails on all three of the broken arrangements this fix replaces: an additive - /// prior-scaled floor drives the whole diagonal to one value, storing the variance in the - /// mass instead of the inverse mass reverses the ordering, and doing both leaves a metric - /// anti-correlated with the truth. + /// It fails under any of the three defective arrangements: an additive prior-scaled floor + /// drives the whole diagonal to one value, storing the variance in the mass instead of the + /// inverse mass reverses the ordering, and doing both leaves a metric anti-correlated with + /// the truth. /// /// [TestMethod] @@ -293,11 +293,11 @@ public void Test_NUTS_ShortAdaptationWindow_UsesFallbackVariance() /// Ten draws taken early in warmup under an identity metric under-estimate the posterior /// variances badly in absolute terms, so this test cannot pin the metric to the analytic /// truth. What it can pin is the property that separates a window estimate from the blended - /// fallback: the additive prior-scaled blend returned the same value on every coordinate to - /// within a part in a thousand, because the prior term dominated it, whereas a window estimate + /// fallback: an additive prior-scaled blend returns the same value on every coordinate to + /// within a part in a thousand, because the prior term dominates it, whereas a window estimate /// reflects the four decades of scale in this target. The assertions are that the metric is /// nowhere near the fallback and that it is anisotropic by more than a factor of ten, both of - /// which the pre-fix code fails. + /// which an additive prior-scaled blend fails. /// /// [TestMethod] From 3083d5ad27d4e789de63486eb93f7d6d1f3b50b0 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 26 Aug 2026 10:55:35 -0600 Subject: [PATCH 097/222] Keep the Powell direction set alive through the zero-step fallback --- .../Mathematics/Optimization/Local/Powell.cs | 41 +++++++++++-------- .../Optimization/Local/Test_Powell.cs | 29 +++++++++++++ 2 files changed, 52 insertions(+), 18 deletions(-) diff --git a/Numerics/Mathematics/Optimization/Local/Powell.cs b/Numerics/Mathematics/Optimization/Local/Powell.cs index 1d08db6f..473aa04f 100644 --- a/Numerics/Mathematics/Optimization/Local/Powell.cs +++ b/Numerics/Mathematics/Optimization/Local/Powell.cs @@ -193,8 +193,11 @@ protected override void Optimize() /// /// Auxiliary line minimization routine. /// - /// The initial point. Updated in place to the minimizing point along the direction. - /// The initial direction. Updated in place to the vector displacement actually taken. + /// The initial point. Updated in place to the minimizing point along the + /// direction when the search strictly improves on the current point; left untouched otherwise. + /// The initial direction. Updated in place to the vector displacement + /// actually taken when the search strictly improves on the current point; left untouched + /// otherwise. /// Determines if the solver should be canceled. /// /// The fitness at the returned point, or when the solver was canceled. @@ -242,17 +245,19 @@ protected override void Optimize() /// pull the iterate out to the boundary for no improvement. /// /// - /// The zero step is evaluated before the search and is kept unless the search strictly improves on - /// it, so this routine is non-increasing. The enclosing algorithm relies on that: it identifies the - /// direction of largest decrease by index, and that index is only defined when some direction did - /// decrease. + /// The zero step is evaluated before the search and its value is returned unless the search + /// strictly improves on it, so this routine is non-increasing. The enclosing algorithm relies on + /// that: it identifies the direction of largest decrease by index, and that index is only defined + /// when some direction did decrease. /// /// - /// When the feasible interval collapses to the single point zero, the line search cannot move. That - /// happens when the direction is identically zero and when the iterate sits on a bound with the - /// direction pointing out of the box in every constrained coordinate. In that case the value at the - /// current point is returned and the direction is left unchanged, so a blocked direction is retained - /// in the direction set and can be retried from a later iterate. + /// Both zero-progress paths — a feasible interval collapsed to the single point zero, which + /// happens when the direction is identically zero or when the iterate sits on a bound with the + /// direction pointing out of the box in every constrained coordinate, and a search that failed to + /// strictly improve on the current point — return the zero-step value and leave the point and the + /// direction untouched. A blocked direction is therefore retained in the direction set and can be + /// retried from a later iterate; scaling it by the zero step would instead store a zero direction + /// that no later iteration could use. /// /// private double LineMinimization(double[] startPoint, double[] direction, ref bool cancel) @@ -323,13 +328,13 @@ double func(double alpha) cancel = c; if (cancel) return double.NaN; - // Fall back on the zero step unless the search strictly improved on it. Written this way so a - // search that returned NaN keeps the current point rather than moving to it. - if (!(fmin < zeroStep)) - { - xmin = 0d; - fmin = zeroStep; - } + // Fall back on the zero step unless the search strictly improved on it, comparing so that a + // search that returned NaN keeps the current point. As on the degenerate-interval path above, + // the point and the direction are left untouched: scaling the direction by the zero step + // would zero it, and a zero direction stored into the enclosing direction set could never + // produce progress again. + if (!(fmin < zeroStep)) return zeroStep; + for (int j = 0; j < NumberOfParameters; j++) { direction[j] *= xmin; diff --git a/Test_Numerics/Mathematics/Optimization/Local/Test_Powell.cs b/Test_Numerics/Mathematics/Optimization/Local/Test_Powell.cs index 487485b9..e400b7fb 100644 --- a/Test_Numerics/Mathematics/Optimization/Local/Test_Powell.cs +++ b/Test_Numerics/Mathematics/Optimization/Local/Test_Powell.cs @@ -492,6 +492,35 @@ public void Test_LineSearchNeverKeepsAPointWorseThanItsStart() Assert.AreEqual(-1d, solution[1], 1E-4); } + /// + /// Test that a line search that fails to improve on its start does not disable a direction of the + /// direction set. + /// + /// + /// When the search along the average direction fails to strictly improve on the current point, the + /// fallback keeps the current point. Were the direction scaled by that zero step, the zero vector + /// would be stored into the direction set by the enclosing direction-set update, its feasible step + /// interval would collapse to the single point zero on every later iteration, and the direction + /// would be permanently lost. Eggholder from this corner start reaches that state and then stops + /// at f = -683.29 with one direction dead, while a direction set that keeps the unscaled direction + /// continues to f = -715.98. The assertion sits between the two regimes rather than pinning the + /// trajectory, because the objective is chaotic and its trigonometry varies in the last bits + /// across target frameworks. + /// + [TestMethod] + public void Test_ZeroStepFallbackKeepsTheDirectionSetAlive() + { + var initial = new double[] { 512d, -128d }; + var lower = new double[] { -512d, -512d }; + var upper = new double[] { 512d, 512d }; + var solver = new Powell(TestFunctions.Eggholder, 2, initial, lower, upper) { ReportFailure = false, RecordTraces = false, ComputeHessian = false }; + solver.Minimize(); + + Assert.AreEqual(OptimizationStatus.Success, solver.Status); + Assert.IsLessThan(-700d, solver.BestParameterSet.Fitness, + "The run stopped early with a dead direction in the direction set."); + } + /// /// Test that a box whose width is the smallest representable number still runs a line search. /// From 0c00e8333ffbedf650175ef8512f3ab96cc60dab Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 26 Aug 2026 10:57:52 -0600 Subject: [PATCH 098/222] Repair local-search starting points into a copy instead of the caller's array --- .../Mathematics/Optimization/Global/MLSL.cs | 30 ++++++---- .../Optimization/Global/MultiStart.cs | 23 +++---- .../Optimization/Global/Test_MLSL.cs | 60 +++++++++++++++++++ .../Optimization/Global/Test_MultiStart.cs | 54 +++++++++++++++++ 4 files changed, 144 insertions(+), 23 deletions(-) diff --git a/Numerics/Mathematics/Optimization/Global/MLSL.cs b/Numerics/Mathematics/Optimization/Global/MLSL.cs index 68473681..8a9e4962 100644 --- a/Numerics/Mathematics/Optimization/Global/MLSL.cs +++ b/Numerics/Mathematics/Optimization/Global/MLSL.cs @@ -212,10 +212,9 @@ protected override void Optimize() // On the first iteration, add the user-defined initial starting points // This can often be very close to the true minimum // - // Evaluate and minimize from a copy of InitialValues rather than the array itself. - // ParameterSet stores its array by reference, and GetLocalOptimizer repairs its - // argument in place, so operating directly on InitialValues would let either path - // silently corrupt the public InitialValues array. + // Evaluate from a copy of InitialValues rather than the array itself: ParameterSet + // stores its array by reference, so the sampled point recorded here would otherwise + // alias the public InitialValues array. var initial = InitialValues.ToArray(); SampledPoints.Add(new SamplePoint { ParameterSet = new ParameterSet(initial, Evaluate(initial, ref cancel)), Minimized = true }); @@ -361,9 +360,10 @@ protected override void Optimize() } /// - /// Returns an optimizer for the local search. + /// Returns an optimizer for the local search. /// - /// An array of initial values to evaluate. + /// An array of initial values to evaluate. Not modified; the bounds + /// repair is applied to a private copy. /// The desired relative tolerance for the solution. /// The desired absolute tolerance for the solution. /// By ref. Determines if the solver should be canceled. @@ -372,21 +372,25 @@ private Optimizer GetLocalOptimizer(IList initialValues, double relative bool localCancel = false; Optimizer? solver = null; - // Make sure the parameters are within the bounds. - for (int i = 0; i < NumberOfParameters; i++) - initialValues[i] = RepairParameter(initialValues[i], LowerBounds[i], UpperBounds[i]); - + // Make sure the parameters are within the bounds, repairing into a local copy rather than + // through the argument: the caller may pass the live array inside a recorded ParameterSet — + // the best parameter set on the polish path, or a published sample point — whose Fitness + // would not follow an in-place repair of its Values. + var repaired = new double[NumberOfParameters]; + for (int i = 0; i < NumberOfParameters; i++) + repaired[i] = RepairParameter(initialValues[i], LowerBounds[i], UpperBounds[i]); + if (Method == LocalMethod.BFGS) { - solver = new BFGS((x) => Evaluate(x, ref localCancel), NumberOfParameters, initialValues, LowerBounds, UpperBounds) { RelativeTolerance = relativeTolerance, AbsoluteTolerance = absoluteTolerance, MaxFunctionEvaluations = MaxFunctionEvaluations - FunctionEvaluations }; + solver = new BFGS((x) => Evaluate(x, ref localCancel), NumberOfParameters, repaired, LowerBounds, UpperBounds) { RelativeTolerance = relativeTolerance, AbsoluteTolerance = absoluteTolerance, MaxFunctionEvaluations = MaxFunctionEvaluations - FunctionEvaluations }; } else if (Method == LocalMethod.NelderMead) { - solver = new NelderMead((x) => Evaluate(x, ref localCancel), NumberOfParameters, initialValues, LowerBounds, UpperBounds) { RelativeTolerance = relativeTolerance, AbsoluteTolerance = absoluteTolerance, MaxFunctionEvaluations = MaxFunctionEvaluations - FunctionEvaluations }; + solver = new NelderMead((x) => Evaluate(x, ref localCancel), NumberOfParameters, repaired, LowerBounds, UpperBounds) { RelativeTolerance = relativeTolerance, AbsoluteTolerance = absoluteTolerance, MaxFunctionEvaluations = MaxFunctionEvaluations - FunctionEvaluations }; } else if (Method == LocalMethod.Powell) { - solver = new Powell((x) => Evaluate(x, ref localCancel), NumberOfParameters, initialValues, LowerBounds, UpperBounds) { RelativeTolerance = relativeTolerance, AbsoluteTolerance = absoluteTolerance, MaxFunctionEvaluations = MaxFunctionEvaluations - FunctionEvaluations }; + solver = new Powell((x) => Evaluate(x, ref localCancel), NumberOfParameters, repaired, LowerBounds, UpperBounds) { RelativeTolerance = relativeTolerance, AbsoluteTolerance = absoluteTolerance, MaxFunctionEvaluations = MaxFunctionEvaluations - FunctionEvaluations }; } else { diff --git a/Numerics/Mathematics/Optimization/Global/MultiStart.cs b/Numerics/Mathematics/Optimization/Global/MultiStart.cs index 352eb733..a133c88e 100644 --- a/Numerics/Mathematics/Optimization/Global/MultiStart.cs +++ b/Numerics/Mathematics/Optimization/Global/MultiStart.cs @@ -156,9 +156,8 @@ protected override void Optimize() if (Iterations == 0) { // Copy into the already-allocated array rather than re-pointing values at - // InitialValues. Re-pointing would alias the public InitialValues array, and the - // uniform draws below and the bounds repair inside GetLocalOptimizer both write - // through values in place, which would silently corrupt InitialValues. + // InitialValues: re-pointing would alias the public InitialValues array, and the + // uniform draws below write through values in place. Array.Copy(InitialValues, values, D); } else @@ -190,9 +189,10 @@ protected override void Optimize() } /// - /// Returns an optimizer for the local search. + /// Returns an optimizer for the local search. /// - /// An array of initial values to evaluate. + /// An array of initial values to evaluate. Not modified; the bounds + /// repair is applied to a private copy. /// The desired relative tolerance for the solution. /// The desired absolute tolerance for the solution. /// By ref. Determines if the solver should be canceled. @@ -201,21 +201,24 @@ private Optimizer GetLocalOptimizer(IList initialValues, double relative bool localCancel = false; Optimizer? solver = null; - // Make sure the parameters are within the bounds. + // Make sure the parameters are within the bounds, repairing into a local copy rather than + // through the argument: the caller may pass the live array inside a recorded ParameterSet, + // whose Fitness would not follow an in-place repair of its Values. + var repaired = new double[NumberOfParameters]; for (int i = 0; i < NumberOfParameters; i++) - initialValues[i] = RepairParameter(initialValues[i], LowerBounds[i], UpperBounds[i]); + repaired[i] = RepairParameter(initialValues[i], LowerBounds[i], UpperBounds[i]); if (Method == LocalMethod.BFGS) { - solver = new BFGS((x) => Evaluate(x, ref localCancel), NumberOfParameters, initialValues, LowerBounds, UpperBounds) { RelativeTolerance = relativeTolerance, AbsoluteTolerance = absoluteTolerance, MaxFunctionEvaluations = MaxFunctionEvaluations - FunctionEvaluations }; + solver = new BFGS((x) => Evaluate(x, ref localCancel), NumberOfParameters, repaired, LowerBounds, UpperBounds) { RelativeTolerance = relativeTolerance, AbsoluteTolerance = absoluteTolerance, MaxFunctionEvaluations = MaxFunctionEvaluations - FunctionEvaluations }; } else if (Method == LocalMethod.NelderMead) { - solver = new NelderMead((x) => Evaluate(x, ref localCancel), NumberOfParameters, initialValues, LowerBounds, UpperBounds) { RelativeTolerance = relativeTolerance, AbsoluteTolerance = absoluteTolerance, MaxFunctionEvaluations = MaxFunctionEvaluations - FunctionEvaluations }; + solver = new NelderMead((x) => Evaluate(x, ref localCancel), NumberOfParameters, repaired, LowerBounds, UpperBounds) { RelativeTolerance = relativeTolerance, AbsoluteTolerance = absoluteTolerance, MaxFunctionEvaluations = MaxFunctionEvaluations - FunctionEvaluations }; } else if (Method == LocalMethod.Powell) { - solver = new Powell((x) => Evaluate(x, ref localCancel), NumberOfParameters, initialValues, LowerBounds, UpperBounds) { RelativeTolerance = relativeTolerance, AbsoluteTolerance = absoluteTolerance, MaxFunctionEvaluations = MaxFunctionEvaluations - FunctionEvaluations }; + solver = new Powell((x) => Evaluate(x, ref localCancel), NumberOfParameters, repaired, LowerBounds, UpperBounds) { RelativeTolerance = relativeTolerance, AbsoluteTolerance = absoluteTolerance, MaxFunctionEvaluations = MaxFunctionEvaluations - FunctionEvaluations }; } else { diff --git a/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs b/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs index c309e4a9..ed078a62 100644 --- a/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs +++ b/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs @@ -550,6 +550,66 @@ public void Test_InitialValuesAreNotMutatedByARun() "No sampled point may alias the public InitialValues array; each must own its own values."); } + /// + /// An objective with a quadratic well around (0.5, 0.5) and a high plateau everywhere else, so a + /// point perturbed out of the well cannot re-attain a fitness recorded inside it. + /// + /// The point to evaluate. + /// The squared distance to the well centre inside the well; 1000 outside. + private static double WellInPlateau(double[] x) + { + double dx = x[0] - 0.5d, dy = x[1] - 0.5d; + double r2 = dx * dx + dy * dy; + return r2 <= 0.09d ? r2 : 1000d; + } + + /// + /// An MLSL whose bounds repair perturbs every value it is given, which makes any write-through of + /// the repair observable as a values/fitness mismatch on a recorded parameter set. + /// + private sealed class PerturbingRepairMLSL : MLSL + { + public PerturbingRepairMLSL(Func objectiveFunction, int numberOfParameters, IList initialValues, IList lowerBounds, IList upperBounds, LocalMethod method) + : base(objectiveFunction, numberOfParameters, initialValues, lowerBounds, upperBounds, method) { } + + protected override double RepairParameter(double value, double lowerBound, double upperBound) + { + return base.RepairParameter(value + 0.25d, lowerBound, upperBound); + } + } + + /// + /// Test that the bounds repair before a local search cannot rewrite a recorded parameter set. + /// + /// + /// The local-search entry point receives the live array inside a recorded + /// — the first sampled point, a reduced-sample point, or the best + /// parameter set on the polish path — and must not write its bounds repair through it, because the + /// recorded fitness would keep describing the unrepaired point. A repair that changes its input is + /// unreachable for a legally constructed solver, whose initial values and samples are already + /// inside the box, so the repair is forced here through an override that perturbs every value it + /// is given. The perturbation kicks any well point onto the plateau, where no later evaluation can + /// match a fitness recorded in the well, so a write-through cannot be healed by re-recording. The + /// invariant is that every recorded set still satisfies fitness = f(values) exactly. + /// + [TestMethod] + public void Test_BoundsRepairDoesNotRewriteRecordedParameterSets() + { + var lower = new double[] { 0d, 0d }; + var upper = new double[] { 1d, 1d }; + var solver = new PerturbingRepairMLSL(WellInPlateau, 2, new double[] { 0.5d, 0.5d }, lower, upper, LocalMethod.NelderMead) + { MaxIterations = 20, ReportFailure = false, ComputeHessian = false }; + solver.Minimize(); + + foreach (var point in solver.SampledPoints) + { + Assert.AreEqual(point.ParameterSet.Fitness, WellInPlateau(point.ParameterSet.Values), 0d, + "A sampled point's recorded fitness no longer matches its recorded values; the bounds repair wrote through the published array."); + } + Assert.AreEqual(solver.BestParameterSet.Fitness, WellInPlateau(solver.BestParameterSet.Values), 0d, + "The best parameter set's recorded fitness no longer matches its recorded values."); + } + /// /// A quadratic whose unconstrained minimum lies far outside the unit square used by the local method /// tests below, so that the constrained solution is the corner (1, 1) with value 722. diff --git a/Test_Numerics/Mathematics/Optimization/Global/Test_MultiStart.cs b/Test_Numerics/Mathematics/Optimization/Global/Test_MultiStart.cs index 848f2f92..2adb0a45 100644 --- a/Test_Numerics/Mathematics/Optimization/Global/Test_MultiStart.cs +++ b/Test_Numerics/Mathematics/Optimization/Global/Test_MultiStart.cs @@ -420,6 +420,60 @@ public void Test_InitialValuesAreNotMutatedByARun() CollectionAssert.AreEqual(callerSnapshot, solver.InitialValues, "InitialValues must still equal what was passed to the constructor after a run."); } + /// + /// An objective with a quadratic well around (0.5, 0.5) and a high plateau everywhere else, so a + /// point perturbed out of the well cannot re-attain a fitness recorded inside it. + /// + /// The point to evaluate. + /// The squared distance to the well centre inside the well; 1000 outside. + private static double WellInPlateau(double[] x) + { + double dx = x[0] - 0.5d, dy = x[1] - 0.5d; + double r2 = dx * dx + dy * dy; + return r2 <= 0.09d ? r2 : 1000d; + } + + /// + /// A MultiStart whose bounds repair perturbs every value it is given, which makes any + /// write-through of the repair observable as a values/fitness mismatch on the recorded best set. + /// + private sealed class PerturbingRepairMultiStart : MultiStart + { + public PerturbingRepairMultiStart(Func objectiveFunction, int numberOfParameters, IList initialValues, IList lowerBounds, IList upperBounds, LocalMethod method) + : base(objectiveFunction, numberOfParameters, initialValues, lowerBounds, upperBounds, method) { } + + protected override double RepairParameter(double value, double lowerBound, double upperBound) + { + return base.RepairParameter(value + 0.25d, lowerBound, upperBound); + } + } + + /// + /// Test that the bounds repair before the polish search cannot rewrite the recorded best set. + /// + /// + /// The polish path hands the live array inside to the + /// local-search entry point, whose bounds repair must not write through it, because the recorded + /// fitness would keep describing the unrepaired point. A repair that changes its input is + /// unreachable for a legally constructed solver, whose samples are already inside the box, so the + /// repair is forced here through an override that perturbs every value it is given. The + /// perturbation kicks the well-bottom best point onto the plateau, where the polish search cannot + /// re-attain the recorded fitness, so a write-through cannot be healed by re-recording. The + /// invariant is that the best set still satisfies fitness = f(values) exactly. + /// + [TestMethod] + public void Test_BoundsRepairDoesNotRewriteTheBestParameterSet() + { + var lower = new double[] { 0d, 0d }; + var upper = new double[] { 1d, 1d }; + var solver = new PerturbingRepairMultiStart(WellInPlateau, 2, new double[] { 0.5d, 0.5d }, lower, upper, LocalMethod.NelderMead) + { MaxIterations = 25, ReportFailure = false, ComputeHessian = false }; + solver.Minimize(); + + Assert.AreEqual(solver.BestParameterSet.Fitness, WellInPlateau(solver.BestParameterSet.Values), 0d, + "The best parameter set's recorded fitness no longer matches its recorded values; the bounds repair wrote through the recorded array."); + } + /// /// A quadratic whose unconstrained minimum lies far outside the unit square used by the local method /// tests below, so that the constrained solution is the corner (1, 1) with value 722. From 6137387b11f25d0c29f5640e2609fdfa8cb082ec Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 26 Aug 2026 10:57:52 -0600 Subject: [PATCH 099/222] Make the memo-ordering and percentile guards observable to their tests --- Numerics/Data/Statistics/Statistics.cs | 8 +-- .../Data/Statistics/Test_Statistics.cs | 19 ++++--- .../Sampling/MCMC/Test_NUTS_GradientReuse.cs | 52 +++++++++++++++++++ 3 files changed, 70 insertions(+), 9 deletions(-) diff --git a/Numerics/Data/Statistics/Statistics.cs b/Numerics/Data/Statistics/Statistics.cs index 46ac1d5d..60c5d81b 100644 --- a/Numerics/Data/Statistics/Statistics.cs +++ b/Numerics/Data/Statistics/Statistics.cs @@ -583,9 +583,11 @@ public static double[] LinearMoments(IList data) /// Thrown when is null. /// Thrown when is empty. /// - /// Thrown when is not a finite value in [0,1]. Every comparison against NaN - /// is false, so NaN is rejected explicitly; without that test it would reach the interpolation - /// index, which saturates to zero on .NET Core and is undefined on .NET Framework. + /// Thrown when is not a finite value in [0,1], with ParamName equal + /// to k. Every comparison against NaN is false, so NaN is rejected explicitly; without + /// that test it reaches the interpolation index, where the float-to-int conversion produces an + /// out-of-range index and the failure surfaces as an indexer exception rather than this contract + /// exception. /// public static double Percentile(IList data, double k, bool dataIsSorted = false) { diff --git a/Test_Numerics/Data/Statistics/Test_Statistics.cs b/Test_Numerics/Data/Statistics/Test_Statistics.cs index 4b5743da..6924e67d 100644 --- a/Test_Numerics/Data/Statistics/Test_Statistics.cs +++ b/Test_Numerics/Data/Statistics/Test_Statistics.cs @@ -520,16 +520,23 @@ public void Test_Percentiles() /// /// Every comparison against NaN is false, so a NaN percentile must be rejected explicitly: without /// the explicit test it passes the range check and reaches the interpolation index, where the - /// float-to-int conversion saturates on .NET Core and is undefined on .NET Framework. Both - /// infinities are rejected by the range check alone. + /// float-to-int conversion produces an out-of-range index whose failure surfaces as an indexer + /// exception — also an , but with ParamName + /// "index". The assertions therefore check ParamName as well as the exception type, which + /// is what distinguishes the contract rejection from the accidental one. Both infinities are + /// rejected by the range check alone. /// [TestMethod] public void Test_Percentile_InvalidArguments() { - Assert.Throws(() => Numerics.Data.Statistics.Statistics.Percentile(_sample1, double.NaN)); - Assert.Throws(() => Numerics.Data.Statistics.Statistics.Percentile(_sample1, double.PositiveInfinity)); - Assert.Throws(() => Numerics.Data.Statistics.Statistics.Percentile(_sample1, double.NegativeInfinity)); - Assert.Throws(() => Numerics.Data.Statistics.Statistics.Percentile(_sample1, new double[] { 0.5d, double.NaN })); + var nanScalar = Assert.Throws(() => Numerics.Data.Statistics.Statistics.Percentile(_sample1, double.NaN)); + Assert.AreEqual("k", nanScalar.ParamName); + var positiveInfinity = Assert.Throws(() => Numerics.Data.Statistics.Statistics.Percentile(_sample1, double.PositiveInfinity)); + Assert.AreEqual("k", positiveInfinity.ParamName); + var negativeInfinity = Assert.Throws(() => Numerics.Data.Statistics.Statistics.Percentile(_sample1, double.NegativeInfinity)); + Assert.AreEqual("k", negativeInfinity.ParamName); + var nanEntry = Assert.Throws(() => Numerics.Data.Statistics.Statistics.Percentile(_sample1, new double[] { 0.5d, double.NaN })); + Assert.AreEqual("k", nanEntry.ParamName); Assert.Throws(() => Numerics.Data.Statistics.Statistics.Percentile(null, 0.5d)); Assert.Throws(() => Numerics.Data.Statistics.Statistics.Percentile(null, new double[] { 0.5d })); diff --git a/Test_Numerics/Sampling/MCMC/Test_NUTS_GradientReuse.cs b/Test_Numerics/Sampling/MCMC/Test_NUTS_GradientReuse.cs index 5acf999e..a21ee036 100644 --- a/Test_Numerics/Sampling/MCMC/Test_NUTS_GradientReuse.cs +++ b/Test_Numerics/Sampling/MCMC/Test_NUTS_GradientReuse.cs @@ -312,6 +312,58 @@ public void Test_NUTS_GradientReuse_StoresACopyOfTheQueriedPosition() "The memo answered a position it never evaluated at; its stored key followed the caller's mutation."); } + /// + /// The memo must record the position before the delegate runs, so a delegate that writes through + /// its argument cannot leave the memo keyed by the mutated position. + /// + /// + /// receives the caller's position array by reference, and + /// nothing prevents an implementation from using it as scratch space during the evaluation. Were + /// the position recorded after such a delegate returned, the entry would be keyed by the mutated + /// position while holding the gradient of the original one: a later query at the mutated position + /// would falsely hit, and a later query at the original position would falsely miss. The delegate + /// here computes the gradient of its argument and then overwrites the argument, and the assertions + /// observe which keys the memo answers to afterwards. + /// + [TestMethod] + public void Test_NUTS_GradientReuse_RecordsThePositionBeforeTheDelegateRuns() + { + int count = 0; + bool hostile = false; + var sampler = BuildSampler(false, (x) => + { + count++; + var g = Gradient(x); + if (hostile) + { + // Overwrite the argument after computing its gradient, as a delegate that uses its + // argument as scratch space would. + for (int j = 0; j < x.Count; j++) x[j] = 1000d + j; + } + return g; + }); + sampler.Sample(); + ClearMemo(sampler); + hostile = true; + + var original = new double[] { 0.5d, -1.5d, 2.25d, -4d }; + var mutated = new double[] { 1000d, 1001d, 1002d, 1003d }; + + count = 0; + InvokeEvaluateGradient(sampler, (double[])original.Clone()); + Assert.AreEqual(1, count, "The first evaluation at a fresh position must reach the gradient delegate."); + + InvokeEvaluateGradient(sampler, (double[])original.Clone()); + Assert.AreEqual(1, count, + "A repeat of the original position must be served from the memo; the stored key followed " + + "the delegate's in-call mutation of its argument."); + + InvokeEvaluateGradient(sampler, (double[])mutated.Clone()); + Assert.AreEqual(2, count, + "The memo answered the mutated position, which was never evaluated; the position must be " + + "recorded before the delegate runs."); + } + /// /// The memo must store a copy of the gradient the delegate returned, not a reference to the /// it came back in. From 9b1f47f86efce515d72def7883fc65e0692ffa67 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 26 Aug 2026 12:38:08 -0600 Subject: [PATCH 100/222] Pin the seeded tree and forest contracts with goldens and an exact-arithmetic split oracle --- .../Supervised/Test_TreeGoldens.cs | 294 ++++++++++++++++++ 1 file changed, 294 insertions(+) create mode 100644 Test_Numerics/Machine Learning/Supervised/Test_TreeGoldens.cs diff --git a/Test_Numerics/Machine Learning/Supervised/Test_TreeGoldens.cs b/Test_Numerics/Machine Learning/Supervised/Test_TreeGoldens.cs new file mode 100644 index 00000000..d301aaf6 --- /dev/null +++ b/Test_Numerics/Machine Learning/Supervised/Test_TreeGoldens.cs @@ -0,0 +1,294 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.MachineLearning; +using Numerics.Mathematics.LinearAlgebra; +using System; +using System.Collections.Generic; +using System.Text; + +namespace MachineLearning +{ + /// + /// Characterization tests that pin the exact decision tree structures and random forest + /// predictions produced from fixed seeds, plus an exact-arithmetic oracle for the split search. + /// + /// + /// + /// Authors: + /// + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + /// + /// The golden strings serialize every tree node (pre-order: feature index, threshold and leaf + /// value as IEEE-754 bit patterns, leaf flag) and every prediction cell bit for bit. They pin the + /// seeded contract of and on the Iris + /// classification fixture and the quarterly consumption regression fixture, so any change to the + /// split search, stopping criteria, or prediction reductions is observable at full precision. + /// The prediction goldens cover the lower, median, and upper percentile columns; the mean column + /// is governed by . + /// + /// + /// The split-search oracle fixture was evaluated with exact rational arithmetic + /// (Python 3.12, fractions.Fraction): the variance reduction of every candidate split + /// (every raw value of every feature, left partition x <= t) was computed without rounding + /// error, giving a unique best split of feature 1 at threshold 4.997143 with an exact gain of + /// 1.87193397801 and a margin of 5.076E-02 over the second-best distinct split. The tree must + /// select that split at the root regardless of how the search is implemented. + /// + /// + [TestClass] + public class Test_TreeGoldens + { + private static readonly double[] sepalLengthTrain = new double[] { 5.1, 4.7, 5, 5.4, 5, 4.9, 5.4, 4.8, 5.8, 5.7, 5.1, 5.1, 5.4, 4.6, 4.8, 5, 5.2, 4.7, 4.8, 5.2, 4.9, 5, 4.9, 5.1, 5, 4.4, 5.1, 4.8, 4.6, 5, 7, 6.9, 6.5, 5.7, 4.9, 5.2, 5, 6, 5.6, 6.7, 5.8, 5.6, 5.9, 6.3, 6.4, 6.6, 6.7, 5.7, 5.5, 5.8, 5.4, 6, 6.3, 5.5, 5.5, 5.8, 5.6, 5.7, 6.2, 5.7, 6.3, 7.1, 6.5, 7.6, 7.3, 7.2, 6.5, 6.8, 5.8, 6.4, 7.7, 6, 6.9, 7.7, 6.7, 7.2, 6.1, 7.2, 7.4, 6.4, 6.1, 7.7, 6.4, 6.9, 6.7, 5.8, 6.7, 6.7, 6.5, 5.9 }; + private static readonly double[] sepalWidthTrain = new double[] { 3.5, 3.2, 3.6, 3.9, 3.4, 3.1, 3.7, 3, 4, 4.4, 3.5, 3.8, 3.4, 3.6, 3.4, 3, 3.5, 3.2, 3.1, 4.1, 3.1, 3.2, 3.6, 3.4, 3.5, 3.2, 3.8, 3, 3.2, 3.3, 3.2, 3.1, 2.8, 2.8, 2.4, 2.7, 2, 2.2, 2.9, 3.1, 2.7, 2.5, 3.2, 2.5, 2.9, 3, 3, 2.6, 2.4, 2.7, 3, 3.4, 2.3, 2.5, 2.6, 2.6, 2.7, 3, 2.9, 2.8, 3.3, 3, 3, 3, 2.9, 3.6, 3.2, 3, 2.8, 3.2, 3.8, 2.2, 3.2, 2.8, 3.3, 3.2, 3, 3, 2.8, 2.8, 2.6, 3, 3.1, 3.1, 3.1, 2.7, 3.3, 3, 3, 3 }; + private static readonly double[] petalLengthTrain = new double[] { 1.4, 1.3, 1.4, 1.7, 1.5, 1.5, 1.5, 1.4, 1.2, 1.5, 1.4, 1.5, 1.7, 1, 1.9, 1.6, 1.5, 1.6, 1.6, 1.5, 1.5, 1.2, 1.4, 1.5, 1.3, 1.3, 1.9, 1.4, 1.4, 1.4, 4.7, 4.9, 4.6, 4.5, 3.3, 3.9, 3.5, 4, 3.6, 4.4, 4.1, 3.9, 4.8, 4.9, 4.3, 4.4, 5, 3.5, 3.8, 3.9, 4.5, 4.5, 4.4, 4, 4.4, 4, 4.2, 4.2, 4.3, 4.1, 6, 5.9, 5.8, 6.6, 6.3, 6.1, 5.1, 5.5, 5.1, 5.3, 6.7, 5, 5.7, 6.7, 5.7, 6, 4.9, 5.8, 6.1, 5.6, 5.6, 6.1, 5.5, 5.4, 5.6, 5.1, 5.7, 5.2, 5.2, 5.1 }; + private static readonly double[] petalWidthTrain = new double[] { 0.2, 0.2, 0.2, 0.4, 0.2, 0.1, 0.2, 0.1, 0.2, 0.4, 0.3, 0.3, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.1, 0.2, 0.2, 0.1, 0.2, 0.3, 0.2, 0.4, 0.3, 0.2, 0.2, 1.4, 1.5, 1.5, 1.3, 1, 1.4, 1, 1, 1.3, 1.4, 1, 1.1, 1.8, 1.5, 1.3, 1.4, 1.7, 1, 1.1, 1.2, 1.5, 1.6, 1.3, 1.3, 1.2, 1.2, 1.3, 1.2, 1.3, 1.3, 2.5, 2.1, 2.2, 2.1, 1.8, 2.5, 2, 2.1, 2.4, 2.3, 2.2, 1.5, 2.3, 2, 2.1, 1.8, 1.8, 1.6, 1.9, 2.2, 1.4, 2.3, 1.8, 2.1, 2.4, 1.9, 2.5, 2.3, 2, 1.8 }; + private static readonly double[] speciesTrain = new double[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }; + private static readonly double[] sepalLengthTest = new double[] { 4.9, 4.6, 4.6, 4.4, 4.8, 4.3, 5.4, 5.7, 5.1, 5.1, 5, 5.2, 5.4, 5.5, 5.5, 4.4, 4.5, 5, 5.1, 5.3, 6.4, 5.5, 6.3, 6.6, 5.9, 6.1, 5.6, 6.2, 6.1, 6.1, 6.8, 6, 5.5, 6, 6.7, 5.6, 6.1, 5, 5.7, 5.1, 5.8, 6.3, 4.9, 6.7, 6.4, 5.7, 6.5, 7.7, 5.6, 6.3, 6.2, 6.4, 7.9, 6.3, 6.3, 6, 6.9, 6.8, 6.3, 6.2 }; + private static readonly double[] sepalWidthTest = new double[] { 3, 3.1, 3.4, 2.9, 3.4, 3, 3.9, 3.8, 3.7, 3.3, 3.4, 3.4, 3.4, 4.2, 3.5, 3, 2.3, 3.5, 3.8, 3.7, 3.2, 2.3, 3.3, 2.9, 3, 2.9, 3, 2.2, 2.8, 2.8, 2.8, 2.9, 2.4, 2.7, 3.1, 3, 3, 2.3, 2.9, 2.5, 2.7, 2.9, 2.5, 2.5, 2.7, 2.5, 3, 2.6, 2.8, 2.7, 2.8, 2.8, 3.8, 2.8, 3.4, 3, 3.1, 3.2, 2.5, 3.4 }; + private static readonly double[] petalLengthTest = new double[] { 1.4, 1.5, 1.4, 1.4, 1.6, 1.1, 1.3, 1.7, 1.5, 1.7, 1.6, 1.4, 1.5, 1.4, 1.3, 1.3, 1.3, 1.6, 1.6, 1.5, 4.5, 4, 4.7, 4.6, 4.2, 4.7, 4.5, 4.5, 4, 4.7, 4.8, 4.5, 3.7, 5.1, 4.7, 4.1, 4.6, 3.3, 4.2, 3, 5.1, 5.6, 4.5, 5.8, 5.3, 5, 5.5, 6.9, 4.9, 4.9, 4.8, 5.6, 6.4, 5.1, 5.6, 4.8, 5.1, 5.9, 5, 5.4 }; + private static readonly double[] petalWidthTest = new double[] { 0.2, 0.2, 0.3, 0.2, 0.2, 0.1, 0.4, 0.3, 0.4, 0.5, 0.4, 0.2, 0.4, 0.2, 0.2, 0.2, 0.3, 0.6, 0.2, 0.2, 1.5, 1.3, 1.6, 1.3, 1.5, 1.4, 1.5, 1.5, 1.3, 1.2, 1.4, 1.5, 1, 1.6, 1.5, 1.3, 1.4, 1, 1.3, 1.1, 1.9, 1.8, 1.7, 1.8, 1.9, 2, 1.8, 2.3, 2, 1.8, 1.8, 2.1, 2, 1.5, 2.4, 1.8, 2.3, 2.3, 1.9, 2.3 }; + private static readonly double[] speciesTest = new double[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }; + private static readonly double[] consumption = new double[] { 0.61598622, 0.46037569, 0.87679142, -0.27424514, 1.89737076, 0.91199291, 0.79453885, 1.64858747, 1.31372218, 1.89147495, 1.530714, 2.31829471, 1.81073916, -0.04173996, 0.35423556, -0.29163216, -0.87702794, 0.35113555, 0.4095977, -1.47580863, 0.83225762, 1.65583461, 1.41942029, 1.05437932, 1.97998024, 0.91391607, 1.05532326, 1.29889825, 1.13637586, 0.54994073, 0.94985262, 1.49599724, 0.57549599, 2.1112096, 0.41796279, 0.7979271, 0.50584598, -0.05775339, 0.9773001, 0.26826982, -0.15391875, -2.27411019, 1.07188123, 1.31644941, 0.5247277, -0.01728203, 0.4016515, -0.7528762, 0.65938376, 0.36854173, 0.76954464, 1.80876006, 0.96802954, 1.95946831, 1.73949442, 1.56389332, 0.84526442, 1.41504495, 0.76546608, 1.31380062, 1.6865532, 0.9343699, 1.90256675, 0.25656565, 0.84304279, 1.1117739, 1.79499406, 0.63768446, 0.01569397, 1.37731686, 1.15225712, 0.21016439, 1.76316026, 0.73053714, 0.85083233, 1.13789838, 0.46064152, 0.46937808, 0.98950145, 0.43942767, 0.85543417, 0.31230451, 0.40261313, -0.75910716, -0.34535008, 0.83564224, 0.48439843, -0.02626579, 1.85996999, 0.68354371, 1.07661214, 1.18372396, 0.37817936, 0.89392729, 1.09813766, 0.88122025, 1.14064791, 0.77176225, 0.77214364, 1.07014805, 0.26420505, 0.89311141, 0.91264702, 0.70025425, 0.92360967, 1.07997887, 0.60055799, 0.78298122, 1.04949253, 0.45219855, 1.69654264, 1.18062797, 1.02693626, 1.75069399, 1.30596977, 1.45888615, 0.94821191, 1.46971415, 1.12921436, 1.45748895, 1.51106759, 0.95508878, 0.96797647, 0.88629738, 0.42159086, 0.25689982, 0.36381084, 1.51630321, 0.29958257, 0.50899032, 0.69667241, 0.53634306, 0.43826169, 1.10719086, 1.46377882, 0.77334046, 0.96768535, 0.64760607, 0.95117167, 1.02041702, 0.76172556, 1.08136588, 0.77186494, 0.37591485, 1.11522822, 0.53100554, 0.58208747, 1.01434389, 0.52486184, 0.33874119, 0.44391875, 0.12505584, -0.20652548, 0.16783443, -0.72499446, -1.21068558, -0.3435437, -0.45174364, 0.60491332, -0.01115014, 0.5348174, 0.81040406, 0.64501881, 1.01833874, 0.50041315, 0.20141978, 0.43372599, 0.33593895, 0.60108995, 0.16942956, 0.26416034, 0.27877186, 0.46861292, 0.20545802, 0.46641787, 0.83917367, 0.47345118, 0.93375698, 0.91687178, 1.1253325, 0.59624005, 0.70814389, 0.66496956, 0.56167978, 0.40468216, 1.04770741, 0.72959779 }; + private static readonly double[] income = new double[] { 0.97226104, 1.16908472, 1.55327055, -0.25527238, 1.98715363, 1.44733417, 0.53181193, 1.16012514, 0.4570115, 1.01662441, 1.90410126, 3.89025866, 0.70825266, 0.79430954, 0.43381827, 1.09380979, -1.66168482, -0.93835321, 0.09448779, -0.12259599, -0.16369546, 4.53650956, -1.46376532, 0.76166351, 1.16825761, 0.51729906, 0.73370026, 0.59458339, -0.03108003, 1.23808955, 1.51880293, 1.9145624, 0.70266687, 0.98314132, 0.7199262, 0.78553605, 1.05755946, -0.86765105, 0.4710034, 0.44037974, 0.33827686, -1.46388507, 1.21301507, 1.94243865, -0.26813406, -0.02363025, 2.02680183, 0.19560628, 0.11969888, 0.57548997, 0.5348441, 0.44938311, 0.85588425, 0.70632719, 1.49810999, 2.13138911, 2.02348788, 1.64921136, 1.36163845, 0.81927319, -0.23895759, 1.90677905, -0.33536283, 1.14181151, 1.2395111, 1.31938549, 0.7047715, 0.17977925, 0.81973366, -0.97505791, 1.80185055, 1.32743427, 1.44861875, 1.02084894, 0.95820336, 0.96207024, 1.22693023, -0.29489091, 0.67822897, 0.80025832, 0.83939484, 0.59572848, 0.03740765, -0.79479735, 0.2118329, 0.69043356, 0.36205181, 0.85100324, 2.12421067, 1.04095059, 0.43562041, 0.34210852, 0.55877186, 0.17627103, 0.05868803, 0.65496353, 0.69846579, 1.05367166, 0.59247377, 1.38110661, 0.94873528, 0.22780635, 0.88957006, 0.57591998, 0.95255663, 0.95161791, 0.79369738, 0.52035746, 0.99858552, 0.85103564, 1.18352222, 1.42325742, 2.10753052, 1.38767133, 1.01464427, 0.80893032, 0.89173174, 0.24722185, 0.66729226, 1.46092242, 1.95061335, 1.03174349, 1.16178668, 0.33725343, 0.84865826, -0.08818148, 2.3367892, -1.24443353, 2.40331419, 0.50559877, -0.12828194, 0.47941927, 0.27834026, 1.43729445, 1.62544947, 0.40353864, 0.72653162, 0.98056746, 0.52450113, 1.24238706, -0.96827007, 0.78835467, 0.51136949, 0.82191843, 2.25904474, 0.14987813, 0.28490722, 1.30059162, 0.65373993, 0.1926087, 0.26238732, 0.08392938, 0.71926565, 2.08693775, -2.3261186, 0.64019534, -0.18888849, 0.70899368, -1.1034318, -0.13213193, 0.10094986, 1.29229259, 0.49678098, 0.69495229, 1.21571502, -0.15658108, 0.52891255, 0.06074719, 1.62204885, 0.76689543, -0.05071452, 2.59106697, -4.26525047, 0.58146541, 0.58328912, 0.21494896, 1.10369487, 1.29390492, 0.99853396, 1.04641801, 0.4904068, 0.95495949, 0.80166267, 0.7400626, 0.5190254, 0.72372078, 0.64470081 }; + private static readonly double[] production = new double[] { -2.45270031, -0.55152509, -0.35870786, -2.18545486, 1.90973412, 0.90153584, 0.30801942, 2.29130441, 4.14957387, 1.89062398, 1.2733529, 3.43689207, 2.79907636, 0.81768862, 0.86899693, 1.47296187, -0.88248358, 0.07427919, -0.41314971, -4.06411893, -6.85103912, -1.33129558, 2.42435972, 2.16904208, 3.02720471, 1.27881101, 1.30386487, 1.77537765, 2.05516067, 3.05838507, 1.10308888, 0.6334685, -0.29339056, 3.94815264, 0.87114701, 1.78447991, 0.42594327, -0.20491944, -0.29723637, 0.33560928, 0.41056141, -4.30076832, -1.64181977, 3.7804552, 0.24627687, 0.30977573, 0.91707444, -2.25457797, -2.07131293, -1.24766384, -1.4005043, -1.90375664, 1.1465572, 2.17942248, 3.36771897, 2.58168445, 2.89709545, 1.53821324, 0.7212874, 0.04115557, 0.32353159, 0.07020996, -0.14046924, 0.57978813, 0.58132135, -0.57641778, 0.37249329, 1.13734778, 1.30758228, 1.75000563, 1.843662, 2.40645058, 0.92013121, 0.87316353, 0.38103668, 0.70292025, 0.43372685, -0.36675732, -0.62142121, 0.42443392, 0.68265169, 0.77446547, 0.419448, -1.57345296, -1.91422028, 0.59131506, 1.36255645, 0.21710308, -0.13365365, 1.76874773, 0.76167388, 1.05024577, 0.87901471, 0.21755108, 0.40135891, 1.49618275, 1.22213656, 1.78250275, 1.267181, 2.04370404, 1.02552601, 0.33785685, 0.90043887, 0.87467273, 0.69285195, 2.11134752, 1.2441868, 1.3539689, 1.867147, 1.48763922, 2.28632066, 2.48091341, 1.10343775, 0.65122238, 0.72551955, 1.44421674, 1.10341663, 0.98574261, 0.90279881, 1.75533234, 0.99682019, 1.23293805, -0.10225268, -0.20388383, -1.35143911, -1.25954437, -1.44101744, -1.06013675, 0.70916406, 1.54280957, 0.59478143, -0.05776556, 0.53922789, -0.69876172, 0.60727351, 1.00599126, 0.65792806, 0.5746178, 0.5633003, 1.38522763, 1.39435718, 0.50586367, -0.50305848, 0.9336501, 0.95057853, 0.5963601, 0.33552773, 0.25603401, 0.91794957, 1.19594247, 0.22356909, 0.16424632, -0.42872571, -1.41297022, -3.26349945, -4.35417741, -5.75045075, -3.00372447, 1.39880419, 1.54400617, 1.88006931, 2.05402479, 1.42683671, 0.37927209, 0.5017404, 0.21878696, 1.01113866, 0.85151692, 0.88651817, 0.62923586, 0.07880166, 0.63305509, 0.67713243, 0.30744961, 0.23440888, 0.79208722, 0.54709166, 1.33801074, 0.62352731, 0.90355427, -0.46710878, -0.69702162, 0.3806061, -0.84554638, -0.41793048, -0.20331883, 0.47491844 }; + private static readonly double[] savings = new double[] { 4.8103115, 7.28799234, 7.28901306, 0.98522964, 3.65777061, 6.0513418, -0.44583221, -1.53087186, -4.35859438, -5.05452579, 5.80995904, 16.04471706, -5.34886849, 8.42603436, 2.75879565, 11.14642986, -2.53351449, -6.59264464, 0.51717884, 11.3433954, -5.47619069, 24.30960536, -17.65616104, 0.64809041, -2.95006644, -1.47455755, -0.06754475, -3.57672239, -9.16055658, 9.09050404, 7.94495719, 6.69627648, 2.92296383, -6.81114259, 4.79207162, 2.371184, 7.77418337, -5.28634896, -1.84549644, 4.0495981, 5.86168864, 8.24322919, 5.70775044, 9.15098787, -5.68139002, 0.88183993, 15.99035721, 7.8055065, -3.34243955, 2.19400166, 0.03499563, -9.57651468, 0.3459546, -10.17004699, 0.21217916, 8.21600068, 13.8691815, 4.38900229, 6.51686089, -2.87544931, -18.71008389, 11.8287195, -23.57393474, 11.36628338, 5.86126836, 3.27551734, -10.09044542, -4.82920131, 12.46424452, -29.52866718, 12.32810406, 16.63076101, -0.96896505, 5.67776867, 3.64649867, -0.19730358, 10.01461545, -8.15576525, -2.48622554, 5.44681102, 2.87544931, 5.10951644, -3.17767248, -0.17953326, 6.49315257, -0.30920615, -0.14086493, 11.3419301, 7.2326515, 5.46708666, -5.9364609, -5.88618856, 2.63464703, -6.91664675, -11.99337844, -1.8370887, -5.18600629, 5.15609751, -2.42215898, 6.32351898, 10.11514398, -10.60541172, -0.11570727, -2.90726686, 2.55933958, -0.75802112, 3.33843952, -3.33843952, 0.61269338, 6.17532322, -7.22796452, 5.43456565, 19.35335228, -4.81709478, -3.12983982, -9.14923404, 1.88735718, -23.49652903, -9.86264835, 2.35825225, 12.2868408, 1.28001748, 2.57390229, -13.16296208, 13.22491995, -6.89043916, 41.66826457, -56.75209674, 50.75796205, 0.87861837, -14.70397426, 1.58733492, 0.49744834, 7.00891625, 6.1841315, -6.89274778, -2.9615204, 8.30885627, -8.99318286, 6.23585017, -42.28191228, -18.27592893, -7.87665229, 20.37236078, 37.40653542, -12.34810568, -10.5527614, 6.0310008, 6.60516929, -7.23648452, -9.00674555, 2.32887238, 29.83728599, 46.43989041, -32.53252494, 36.3124049, 0.9230602, 16.09059408, -24.49229966, 0.8482922, -5.54399051, 11.65612884, -0.35208609, -3.27335958, 14.33860193, -4.07705131, 2.722504, -3.45447712, 17.6253051, 8.9694971, -3.04922177, 29.04670355, -68.78826698, 7.81647729, 3.49400682, -11.2766145, 13.52020248, 8.2440477, 2.46195256, -1.51305022, -0.75840017, 5.02391773, 3.18092976, 3.48278601, 2.23653405, -2.72150106, -0.57285793 }; + private static readonly double[] unemployment = new double[] { 0.9, 0.5, 0.5, 0.7, -0.1, -0.1, 0.1, 0, -0.2, -0.1, -0.2, -0.3, -0.3, 0, -0.1, 0.1, 0.2, 0.3, 0.5, 1.3, 1.4, 0.2, -0.4, -0.2, -0.6, 0, 0, 0.2, -0.4, -0.2, -0.4, -0.4, -0.1, -0.4, 0.1, 0, -0.2, -0.1, 0.2, 0.1, 0.3, 1.3, -0.1, -0.3, 0.2, 0.1, 0.1, 0.9, 0.5, 0.6, 0.5, 0.7, -0.5, -0.2, -0.9, -0.9, -0.5, -0.6, 0.1, 0, -0.1, 0.2, -0.3, -0.1, 0.2, 0, -0.2, -0.4, 0, -0.4, -0.3, -0.2, 0, -0.3, 0, -0.1, -0.3, 0.3, 0, 0.1, -0.2, 0, 0.7, 0.4, 0.5, 0.1, 0, 0.4, 0.1, 0.4, -0.2, -0.2, -0.4, 0, -0.3, -0.2, 0, -0.4, -0.2, -0.4, -0.1, 0.2, 0, 0, -0.1, -0.2, -0.1, 0.2, -0.2, -0.2, -0.1, -0.2, 0, -0.2, 0.1, -0.2, -0.2, 0.1, -0.1, -0.2, 0, 0, -0.1, 0, 0.4, 0.2, 0.5, 0.7, 0, 0.1, -0.1, 0.3, -0.1, 0.4, -0.2, -0.4, 0.1, -0.2, -0.2, 0, -0.2, -0.2, 0, -0.1, -0.2, -0.1, -0.1, -0.1, 0, 0.2, 0.1, 0.3, 0.1, 0.5, 0.5, 1.2, 1.4, 0.8, 0.3, 0.1, 0, -0.5, 0.1, -0.2, -0.3, 0.1, -0.1, -0.5, -0.3, 0, -0.4, 0.1, -0.4, 0, -0.3, -0.5, 0, -0.6, -0.2, -0.3, -0.2, -0.1, -0.3, 0, 0, -0.1, 0 }; + + private const string GoldenDecisionTreeIris = + "2;401199999999999A;FFF8000000000000;0|2;3FFE666666666666;FFF8000000000000;0|-1;FFF8000000000000;3FF0000000000000;1|-1;FFF8000000000000;4000000000000000;1|2;4014000000000000;FFF8000000000000;0|2;401333" + + "3333333333;FFF8000000000000;0|-1;FFF8000000000000;4000000000000000;1|0;4018666666666666;FFF8000000000000;0|-1;FFF8000000000000;4008000000000000;1|-1;FFF8000000000000;4000000000000000;1|-1;FFF800000000" + + "0000;4008000000000000;1|"; + + private const string GoldenDecisionTreeRegression = + "3;3FC999999999999A;FFF8000000000000;0|1;3FFDDFD5885D3133;FFF8000000000000;0|2;C0224C6867727FFA;FFF8000000000000;0|1;3FD4B4BDD79176E0;FFF8000000000000;0|1;BFC1FAE563F26DC6;FFF8000000000000;0|-1;FFF8000" + + "000000000;3FFE70E9D51B4FE8;1|-1;FFF8000000000000;3FFAFC1F354F6D26;1|0;3FE55A754C325DA0;FFF8000000000000;0|2;C0377F1C86C95D57;FFF8000000000000;0|3;BFD999999999999A;FFF8000000000000;0|-1;FFF800000000000" + + "0;3FF6097D675EDEDE;1|-1;FFF8000000000000;3FF783F2FC0B7EB9;1|3;BFB999999999999A;FFF8000000000000;0|3;BFD3333333333333;FFF8000000000000;0|-1;FFF8000000000000;3FF191F8CB834BA4;1|-1;FFF8000000000000;3FF21" + + "14313A5FA25;1|-1;FFF8000000000000;3FEC945E61344D65;1|2;C0242E4EDCB1F22B;FFF8000000000000;0|-1;FFF8000000000000;3FFCB84BB1036D9E;1|-1;FFF8000000000000;3FF7579900ED65CA;1|0;3FF5C945663B9E3C;FFF800000000" + + "0000;0|0;BFEBC3CC2282E1C7;FFF8000000000000;0|2;C0152538A969C642;FFF8000000000000;0|-1;FFF8000000000000;BFAD91DA3290F6DE;1|-1;FFF8000000000000;BFEC109CE5FF4DD3;1|2;401F18C3867D073D;FFF8000000000000;0|2" + + ";3FE39B2F25B26C23;FFF8000000000000;0|0;3FE2F58B8D3C6912;FFF8000000000000;0|2;C0178B75038D2847;FFF8000000000000;0|2;C01BAAA57214F052;FFF8000000000000;0|-1;FFF8000000000000;3FEC9B0D6771A93B;1|2;C017BEEF" + + "9B2DEE50;FFF8000000000000;0|-1;FFF8000000000000;3FF139CDA6BC6CD8;1|-1;FFF8000000000000;3FF2F08888FB10D8;1|0;3FD72BDB5ADA1636;FFF8000000000000;0|3;BFD999999999999A;FFF8000000000000;0|-1;FFF800000000000" + + "0;3FE467E93D9BC1E8;1|3;0000000000000000;FFF8000000000000;0|-1;FFF8000000000000;3FDF006245C55C15;1|-1;FFF8000000000000;3FE0CA91BED9C503;1|0;3FE08DB6C21E7308;FFF8000000000000;0|3;0000000000000000;FFF800" + + "0000000000;0|-1;FFF8000000000000;3FED3ECCE9FE0B08;1|-1;FFF8000000000000;3FEF460ADBFC14B9;1|3;0000000000000000;FFF8000000000000;0|0;3FE26DEFBCE6D0AF;FFF8000000000000;0|-1;FFF8000000000000;3FE6687B99D45" + + "1FC;1|-1;FFF8000000000000;3FE8B5669433A925;1|0;3FE0A6C4B01DBF84;FFF8000000000000;0|-1;FFF8000000000000;3FE90E2EA1A907A1;1|-1;FFF8000000000000;3FE96CDCBD04D8AD;1|2;C00700EB915E0BCF;FFF8000000000000;0|2" + + ";C014BE786ED199E6;FFF8000000000000;0|-1;FFF8000000000000;3FF2401805DB3647;1|2;C00909E975A4F2BE;FFF8000000000000;0|0;3FE306D3BED96F1D;FFF8000000000000;0|-1;FFF8000000000000;3FF4C84988094E5D;1|-1;FFF800" + + "0000000000;3FF4E5408EBB6F2C;1|-1;FFF8000000000000;3FF50553CC85D517;1|0;3FEC775BA1666C9C;FFF8000000000000;0|2;BFBD9EFDDC8F3796;FFF8000000000000;0|1;BFE3E2AEBBBFB435;FFF8000000000000;0|-1;FFF80000000000" + + "00;3FEFA9FEF1E306CB;1|3;0000000000000000;FFF8000000000000;0|1;3FECD0652D564F6D;FFF8000000000000;0|-1;FFF8000000000000;3FED346785F623B3;1|-1;FFF8000000000000;3FEC32F4CF4A558F;1|-1;FFF8000000000000;3FEA" + + "BD94CB7E990D;1|3;BFE0000000000000;FFF8000000000000;0|-1;FFF8000000000000;3FEEFA1915FB94E1;1|-1;FFF8000000000000;3FF0E29AA4868799;1|2;BFC9413E63BDEA87;FFF8000000000000;0|-1;FFF8000000000000;3FF234D4EE8" + + "45793;1|-1;FFF8000000000000;3FF0CAB8ADDBF0D7;1|0;3FE709A2AB9112E2;FFF8000000000000;0|2;3FEC38085F750591;FFF8000000000000;0|-1;FFF8000000000000;BF91B26166A24B6F;1|1;BFD2C6E93309ED95;FFF8000000000000;0|" + + "-1;FFF8000000000000;3FE26A769100EEBD;1|1;3FE8C86BCF310028;FFF8000000000000;0|1;3FD57A9F58756202;FFF8000000000000;0|-1;FFF8000000000000;3FD12B552DD9D7F8;1|-1;FFF8000000000000;3FD3FCCC0E35F8B5;1|3;BFB99" + + "9999999999A;FFF8000000000000;0|1;3FEBCED2A65594B2;FFF8000000000000;0|-1;FFF8000000000000;3FD6ABCBA051EF8C;1|-1;FFF8000000000000;3FD8341733CE38B3;1|-1;FFF8000000000000;3FDABFE7007FACB3;1|1;BFE27203B33D" + + "DA81;FFF8000000000000;0|2;400A34426F663F4F;FFF8000000000000;0|-1;FFF8000000000000;3FF1C9D36DD0BDF3;1|-1;FFF8000000000000;3FF1266CEEC0BD75;1|2;400700EB915E0BCF;FFF8000000000000;0|0;3FEADC529147659A;FFF" + + "8000000000000;0|3;BFC999999999999A;FFF8000000000000;0|-1;FFF8000000000000;3FEB5FB77B00FD2B;1|-1;FFF8000000000000;3FE9889E69E2F2A9;1|1;3FE62BD7DA4708D3;FFF8000000000000;0|-1;FFF8000000000000;3FED8E35DD" + + "DD6B56;1|-1;FFF8000000000000;3FEE57C080E41615;1|0;3FEB3BAF1830F947;FFF8000000000000;0|2;400AB51FC770976D;FFF8000000000000;0|-1;FFF8000000000000;3FE337C563CCD90C;1|3;BFC999999999999A;FFF8000000000000;0" + + "|-1;FFF8000000000000;3FDCF0D22FE31C9A;1|-1;FFF8000000000000;3FDC1F953BE6E0F5;1|2;401A1143FB2C869C;FFF8000000000000;0|1;3FE29A2F3B56EE23;FFF8000000000000;0|0;3FEEA99A17C3C10A;FFF8000000000000;0|-1;FFF8" + + "000000000000;3FEB3A04B8F757E6;1|-1;FFF8000000000000;3FEAFA34DF85B70F;1|0;3FF05565B2B74CDE;FFF8000000000000;0|-1;FFF8000000000000;3FE7608F6C9A0728;1|2;40149FD80691BF86;FFF8000000000000;0|-1;FFF80000000" + + "00000;3FE8B246BF01322F;1|-1;FFF8000000000000;3FE87EB2B87983A5;1|-1;FFF8000000000000;3FE02FE3E89D37DF;1|3;BFB999999999999A;FFF8000000000000;0|1;3FDBC22E43096A0C;FFF8000000000000;0|-1;FFF8000000000000;3" + + "FDD7B2691E51A4C;1|1;3FE28D9FD61EB019;FFF8000000000000;0|-1;FFF8000000000000;3FD06B925501BA14;1|-1;FFF8000000000000;3FD0E8BC4C4C08ED;1|0;3FEA3B4215A42C1A;FFF8000000000000;0|1;3FEA2A815326C38E;FFF800000" + + "0000000;0|-1;FFF8000000000000;BFA55EF0A645CF6D;1|-1;FFF8000000000000;3F9012147F93DEF0;1|-1;FFF8000000000000;BFD2AA19EF6A5B93;1|2;401CEE3C31DF761D;FFF8000000000000;0|3;BFB999999999999A;FFF8000000000000" + + ";0|3;BFC999999999999A;FFF8000000000000;0|0;3FF633E6DA3F2631;FFF8000000000000;0|-1;FFF8000000000000;3FFC02D7B385A8F7;1|1;3FF45FA74A5C44A4;FFF8000000000000;0|1;3FE4455FBB517A46;FFF8000000000000;0|-1;FFF" + + "8000000000000;3FF7EF9ACD4B4CDA;1|-1;FFF8000000000000;3FF87DCDF6987831;1|-1;FFF8000000000000;3FF6A4062C69EB56;1|-1;FFF8000000000000;3FED2F0BC15448AD;1|3;0000000000000000;FFF8000000000000;0|-1;FFF800000" + + "0000000;3FFC35E78864E8E3;1|-1;FFF8000000000000;3FFDC26FE4697691;1|2;40335A754B869129;FFF8000000000000;0|1;3FED58AC7EF9853D;FFF8000000000000;0|3;3FB999999999999A;FFF8000000000000;0|-1;FFF8000000000000;" + + "3FD9B4A87E38EB03;1|-1;FFF8000000000000;3FEDE65BB45BBCBF;1|1;3FF1A7AE5796BFCB;FFF8000000000000;0|3;BFD999999999999A;FFF8000000000000;0|-1;FFF8000000000000;3FEE6531525D6FF3;1|-1;FFF8000000000000;3FF06E5" + + "4B73C6CC3;1|-1;FFF8000000000000;3FF26FA5296FBAF9;1|-1;FFF8000000000000;3FFA7E4C6E988F03;1|2;40300B7293C645F5;FFF8000000000000;0|2;BFF87E737DD1112A;FFF8000000000000;0|0;3FDD3FAD2999567E;FFF800000000000" + + "0;0|1;400070F813CD6751;FFF8000000000000;0|-1;FFF8000000000000;3FF22E98742A9F3B;1|3;BFD999999999999A;FFF8000000000000;0|-1;FFF8000000000000;3FF6B5F20CCD42E9;1|-1;FFF8000000000000;3FF505018C7213C4;1|1;4" + + "00664822589A75E;FFF8000000000000;0|0;3FF04417F5143EA6;FFF8000000000000;0|3;BFD3333333333333;FFF8000000000000;0|-1;FFF8000000000000;3FFCF8C9A01C958D;1|2;C02457106629AA9E;FFF8000000000000;0|-1;FFF800000" + + "0000000;3FFF59FB714FF968;1|-1;FFF8000000000000;3FFE437B3CB74074;1|0;3FF28FDF60F91429;FFF8000000000000;0|-1;FFF8000000000000;3FFA609D4143ED15;1|-1;FFF8000000000000;3FFB2509E5311CAE;1|1;400837B71A5DFA31" + + ";FFF8000000000000;0|-1;FFF8000000000000;3FFFADFFC2986B71;1|-1;FFF8000000000000;4000E3C1DBD8028B;1|0;40010D15BBD4011F;FFF8000000000000;0|0;3FF6C5A9928B0786;FFF8000000000000;0|2;40194B488F3AF0ED;FFF8000" + + "000000000;0|1;40015A32BBE9FB87;FFF8000000000000;0|1;4000E40A2B27202A;FFF8000000000000;0|1;4000598180F4064D;FFF8000000000000;0|-1;FFF8000000000000;3FF11F538FCA0CFF;1|-1;FFF8000000000000;3FF14797EC70562" + + "9;1|-1;FFF8000000000000;3FF0DEBCD98FA8D5;1|-1;FFF8000000000000;3FF2E3DA277E4931;1|-1;FFF8000000000000;3FE1991D4D42D3E5;1|2;40206E97A421070C;FFF8000000000000;0|2;400D431D3CD1C280;FFF8000000000000;0|0;3" + + "FF7F8422E4DC631;FFF8000000000000;0|-1;FFF8000000000000;3FFBD4F819D79781;1|-1;FFF8000000000000;3FFE5BA1712962FB;1|-1;FFF8000000000000;3FF905B5007D5368;1|1;40072D4061191F8C;FFF8000000000000;0|-1;FFF8000" + + "000000000;3FEB0C67F80BEAC5;1|-1;FFF8000000000000;3FF5102D41AC9DAD;1|-1;FFF8000000000000;40028BDE18CF84B8;1|-1;FFF8000000000000;3FCAE6AAAEEAB99D;1|2;401D27F30A23447E;FFF8000000000000;0|2;C023272CEEA087" + + "1F;FFF8000000000000;0|-1;FFF8000000000000;3FFCF0AE63802D0C;1|0;3FD5A653FCAC6376;FFF8000000000000;0|2;C0096BDF8C97FE64;FFF8000000000000;0|1;C000920C83710E35;FFF8000000000000;0|0;BFC4F3F90B9A120F;FFF800" + + "0000000000;0|-1;FFF8000000000000;3FEAA1DABB77E5C0;1|-1;FFF8000000000000;3FE519ABF896D519;1|1;BFD778F3B1BDFDA7;FFF8000000000000;0|-1;FFF8000000000000;3FDE0A4A5BC3CB1B;1|2;C01A5EDE3C8BCC7C;FFF8000000000" + + "000;0|-1;FFF8000000000000;3FD679013DEDA159;1|-1;FFF8000000000000;3FD9C469DC929294;1|1;BFF92CDD02CFE90C;FFF8000000000000;0|2;BFC6FAF2241F4413;FFF8000000000000;0|-1;FFF8000000000000;BFE84A9B194B8087;1|3" + + ";3FE0000000000000;FFF8000000000000;0|-1;FFF8000000000000;BFD61A3738D157CC;1|-1;FFF8000000000000;BFD18D3B7CD8C3A6;1|2;3FE08CBAA37F7683;FFF8000000000000;0|-1;FFF8000000000000;3FDA36D945811391;1|-1;FFF80" + + "00000000000;BFC3B39C0EBEDFA4;1|2;401D26E776C731AD;FFF8000000000000;0|2;3FA1EAF27CB4B178;FFF8000000000000;0|-1;FFF8000000000000;3FE8A01C14B39645;1|2;40018D50BE088EC5;FFF8000000000000;0|-1;FFF8000000000" + + "000;3FD796300D63EBB8;1|0;3FF0A7BBCE4CD49A;FFF8000000000000;0|0;3FEF1CC32F3F32C6;FFF8000000000000;0|-1;FFF8000000000000;3FE3B628BBB5F921;1|-1;FFF8000000000000;3FE5DF970EFAC4AD;1|-1;FFF8000000000000;3FD" + + "D76CB991B198C;1|-1;FFF8000000000000;3FEC0EACE14A0760;1|3;3FECCCCCCCCCCCCD;FFF8000000000000;0|0;3FC909A067BF0107;FFF8000000000000;0|-1;FFF8000000000000;BFE8178FD41DF9E6;1|-1;FFF8000000000000;BF9AE56B54" + + "36E3ED;1|0;BFF76C12C04C0AF0;FFF8000000000000;0|-1;FFF8000000000000;C0023160AEEC671C;1|-1;FFF8000000000000;BFF79CE9829012B2;1|"; + + private const string GoldenForestIrisPercentiles = + "3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000" + + "000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF000000" + + "0000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|4000000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF00" + + "00000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3" + + "FF0000000000000|3FF0000000000000|3FF0000000000000|4000000000000000|3FF0000000000000|3FF0000000000000|4000000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF00000000000" + + "00|3FF0000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4008000000000000|4000000000000000|4000000000" + + "000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4008000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|400000" + + "0000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4008000000000000|4000000000000000|4000000000000000|4008000000000000|4000000000000000|40" + + "00000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4008000000000000|4008000000000000|4000000000000000|4000000000000000|4000000000000000|400000000000000" + + "0|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|40000000000" + + "00000|4000000000000000|4000000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4000000000000000|4000000000000000|4008000000000000|4008000" + + "000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4000000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|400" + + "8000000000000|4008000000000000|4008000000000000|4000000000000000|4008000000000000|4008000000000000|4000000000000000|4008000000000000|4008000000000000|4000000000000000|4000000000000000|4008000000000000" + + "|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4000000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|400800000000" + + "0000|4000000000000000|4000000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4000000000000000|4008000000000000|40080000" + + "00000000|4008000000000000|4008000000000000|4008000000000000|"; + + private const string GoldenForestRegressionPercentiles = + "3FE9889E69E2F2A9|3FF6A4062C69EB56|3FFE5BA1712962FB|3FD9B4A87E38EB03|3FF06E54B73C6CC3|3FF87DCDF6987831|3FE337C563CCD90C|3FEE57C080E41615|3FF87DCDF6987831|3FDD7B2691E51A4C|3FEB5FB77B00FD2B|3FF275749C8A0" + + "EDC|3FEC945E61344D65|3FF783F2FC0B7EB9|3FFE70E9D51B4FE8|BFE81A1D312D0D6E|BF9AE56B5436E3ED|3FEC0EACE14A0760|BFAD91DA3290F6DE|3FE519ABF896D519|3FF50553CC85D517|BFE93FACC9C47C00|3FFA7E4C6E988F03|3FFDC26FE" + + "4697691|BFEC109CE5FF4DD3|3FEC945E61344D65|3FFE70E9D51B4FE8|3FD9B4A87E38EB03|3FFA7E4C6E988F03|40028BDE18CF84B8|3FD12B552DD9D7F8|3FE8A01C14B39645|3FF4C84988094E5D|3FDE0A4A5BC3CB1B|3FF191F8CB834BA4|3FF7B" + + "05B98821745|BF9AE56B5436E3ED|3FD9C469DC929294|3FED3ECCE9FE0B08|BFC3B39C0EBEDFA4|3FDA36D945811391|3FEE57C080E41615|BF9AE56B5436E3ED|3FEC0EACE14A0760|3FFDC26FE4697691|3FEC0EACE14A0760|3FF7EF9ACD4B4CDA|3" + + "FFDC26FE4697691|3FE467E93D9BC1E8|3FF22E98742A9F3B|3FF7579900ED65CA|3FE6687B99D451FC|3FF4C84988094E5D|3FF7579900ED65CA|BFA55EF0A645CF6D|3FDD7B2691E51A4C|3FE7608F6C9A0728|3FE6687B99D451FC|3FF139CDA6BC6C" + + "D8|3FFCB84BB1036D9E|3FC9A80BE0918367|3FE87EB2B87983A5|3FF6A4062C69EB56|BFAD91DA3290F6DE|3FF6097D675EDEDE|3FFE70E9D51B4FE8|3FF191F8CB834BA4|3FF7579900ED65CA|3FFF59FB714FF968|3FE439A4DDABA879|3FEFA9FEF1" + + "E306CB|3FFCF0AE63802D0C|BFA55EF0A645CF6D|3FD06B925501BA14|3FE02FE3E89D37DF|3FD9B4A87E38EB03|3FFA7E4C6E988F03|40028BDE18CF84B8|3FEC945E61344D65|3FF191F8CB834BA4|3FF783F2FC0B7EB9|3FEC945E61344D65|3FF211" + + "4313A5FA25|3FFCF0AE63802D0C|3FDCF0D22FE31C9A|3FEB3A04B8F757E6|3FF1D21DEA657DC5|BFA55EF0A645CF6D|3FDABFE7007FACB3|3FE337C563CCD90C|3FDF006245C55C15|3FEC9B0D6771A93B|3FF4C84988094E5D|3FDF006245C55C15|3F" + + "EC9B0D6771A93B|3FFAFC1F354F6D26|BFD61A3738D157CC|3FD679013DEDA159|3FE0CA91BED9C503|BFD2AA19EF6A5B93|BF9AE56B5436E3ED|3FEDE65BB45BBCBF|BFE93FACC9C47C00|3FEDE65BB45BBCBF|3FFDC26FE4697691|C0023160AEEC671" + + "C|3FD679013DEDA159|3FFE70E9D51B4FE8|C0023160AEEC671C|BFE8178FD41DF9E6|3FE3B628BBB5F921|C0023160AEEC671C|BFD61A3738D157CC|3FEAA1DABB77E5C0|BFF79CE9829012B2|BFA55EF0A645CF6D|3FE3B628BBB5F921|BFAD91DA329" + + "0F6DE|3FF2114313A5FA25|3FFCF0AE63802D0C|BFD18D3B7CD8C3A6|BF91B26166A24B6F|3FF4C84988094E5D|3FDF006245C55C15|3FF2401805DB3647|3FFE437B3CB74074|3FCAE6AAAEEAB99D|3FE1991D4D42D3E5|3FF2E3DA277E4931|3FDEE60" + + "177E07284|3FEABD94CB7E990D|3FF0E29AA4868799|3FE6687B99D451FC|3FF234D4EE845793|3FFC3C6C8A6CD5E6|3F9012147F93DEF0|3FD06B925501BA14|3FF1266CEEC0BD75|BF91B26166A24B6F|3FE0CA91BED9C503|3FF3081EFC222D6D|3FD" + + "3FCCC0E35F8B5|3FD8341733CE38B3|3FEB5FB77B00FD2B|BF91B26166A24B6F|3FE467E93D9BC1E8|3FF2F08888FB10D8|3FD9B4A87E38EB03|3FF06E54B73C6CC3|3FF87DCDF6987831|BFA55EF0A645CF6D|BFA55EF0A645CF6D|3FE1991D4D42D3E5" + + "|BFAD91DA3290F6DE|3FE19A8427ED59E0|3FF2F08888FB10D8|3FD9B4A87E38EB03|3FFA7E4C6E988F03|40028BDE18CF84B8|BFEC109CE5FF4DD3|3FF6097D675EDEDD|3FFB1254A1E539E3|BFC3B39C0EBEDFA4|BFA55EF0A645CF6D|3FE02FE3E89D" + + "37DF|3FD12B552DD9D7F8|3FDABFE7007FACB3|3FEB5FB77B00FD2B|3FE6687B99D451FC|3FF2114313A5FA25|3FFB0942086BBCE3|BFD2AA19EF6A5B93|3FCDDEE7AC7716E2|3FE87EB2B87983A5|BFA55EF0A645CF6D|3FE1991D4D42D3E5|3FF6A406" + + "2C69EB56|3FE3B628BBB5F921|3FED8E35DDDD6B56|3FF234D4EE845793|3FEC32F4CF4A558F|3FF234D4EE845793|3FFC35E78864E8E3|3FDEF41446DEE1A3|3FEC32F4CF4A558F|3FF2130A5E977ECF|3FDC1F953BE6E0F5|3FEAFA34DF85B70F|3FF1" + + "C9D36DD0BDF3|3FDABFE7007FACB3|3FEB3A04B8F757E5|3FEE57C080E41615|3FD12B552DD9D7F8|3FE26A769100EEBD|3FF1C9D36DD0BDF3|3FD12B552DD9D7F8|3FD8341733CE38B3|3FEFA9FEF1E306CB|3FE8B5669433A925|3FF0E29AA4868799|" + + "3FFC02D7B385A8F7|3FE6687B99D451FC|3FEABD94CB7E990D|3FF139CDA6BC6CD8|"; + + private static readonly double[] OracleX0 = { 4.744781, 6.255026, 1.160139, 8.741791, 0.319224, 1.201623, 7.703759, 8.249999, 7.763458, 2.561846, 7.844684, 9.530505, 5.237435, 7.938164, 7.634402, 3.538497, 3.908757, 0.798954, 4.233405, 4.819062, 4.704432, 8.131584, 7.138561, 5.718968, 0.230697, 8.428248, 2.642866, 0.617403, 9.190608, 5.605545, 9.120073, 5.506271, 1.210414, 7.754063, 6.823279, 8.004035, 7.097406, 5.353946, 3.109105, 1.01253 }; + private static readonly double[] OracleX1 = { 6.041412, 1.540177, 0.705583, 2.134676, 3.987951, 8.706857, 4.997143, 7.688693, 7.269118, 9.894784, 2.850013, 2.908263, 4.640485, 9.239211, 2.437277, 9.911633, 5.759533, 1.255405, 2.324209, 0.226532, 2.977218, 1.713005, 4.749645, 2.982202, 8.679028, 2.531092, 8.802673, 1.569402, 4.644899, 2.859959, 8.467362, 0.291196, 5.153139, 1.003399, 3.90648, 6.822431, 9.421524, 6.81047, 2.76754, 4.48085 }; + private static readonly double[] OracleX2 = { 8.287756, 2.053361, 1.083714, 0.926085, 2.324783, 6.528751, 1.224496, 0.580007, 5.802834, 7.568654, 5.376091, 3.487552, 8.249803, 4.264968, 8.381896, 1.098973, 6.334179, 6.910272, 7.131958, 8.413748, 7.770878, 5.584611, 1.671792, 9.032317, 4.131488, 1.241772, 5.149096, 3.04752, 6.142418, 9.40855, 4.380326, 6.494782, 3.475771, 7.189178, 5.217128, 8.68896, 2.452654, 1.702436, 4.397793, 0.220322 }; + private static readonly double[] OracleY = { 9.203648, 6.973726, 5.396438, 7.922964, 5.253539, 8.372256, 6.887424, 10.483275, 10.398861, 8.988918, 7.161026, 7.715756, 6.251278, 10.311859, 7.417448, 9.087316, 9.140687, 5.769176, 6.332959, 6.354408, 6.481743, 7.217564, 7.019442, 6.991094, 7.940477, 7.538989, 8.986207, 5.176143, 7.738771, 6.852212, 11.024926, 6.459788, 8.495679, 7.49478, 7.280696, 10.587507, 10.360391, 9.330927, 5.997098, 5.517587 }; + private const int OracleBestFeature = 1; + private const double OracleBestThreshold = 4.997143; + + /// + /// Serializes a tree in pre-order with thresholds and leaf values as IEEE-754 bit patterns. + /// + /// The root of the subtree to serialize. + /// One pipe-delimited entry per node. + private static string SerializeTree(DecisionNode? node) + { + var sb = new StringBuilder(); + void Walk(DecisionNode? n) + { + if (n == null) return; + sb.Append(n.FeatureIndex).Append(';') + .Append(BitConverter.DoubleToInt64Bits(n.Threshold).ToString("X16")).Append(';') + .Append(BitConverter.DoubleToInt64Bits(n.Value).ToString("X16")).Append(';') + .Append(n.IsLeafNode ? '1' : '0').Append('|'); + Walk(n.Left); Walk(n.Right); + } + Walk(node); + return sb.ToString(); + } + + /// + /// Serializes the leading columns of a prediction matrix as IEEE-754 bit patterns. + /// + /// The prediction matrix. + /// The number of leading columns to serialize. + /// One pipe-delimited entry per cell, row major. + private static string SerializeColumns(double[,] p, int cols) + { + var sb = new StringBuilder(); + for (int i = 0; i < p.GetLength(0); i++) + for (int j = 0; j < cols; j++) + sb.Append(BitConverter.DoubleToInt64Bits(p[i, j]).ToString("X16")).Append('|'); + return sb.ToString(); + } + + private static Matrix IrisTrainX() => new Matrix(new List { sepalLengthTrain, sepalWidthTrain, petalLengthTrain, petalWidthTrain }); + private static Vector IrisTrainY() => new Vector(speciesTrain); + private static Matrix IrisTestX() => new Matrix(new List { sepalLengthTest, sepalWidthTest, petalLengthTest, petalWidthTest }); + + /// + /// Returns the subarray from through , + /// or through the final element when is negative. + /// + /// The source array. + /// The first index to copy. + /// The last index to copy, or a negative value for the end of the array. + private static double[] Sub(double[] a, int start, int endInclusive) + { + int len = (endInclusive < 0 ? a.Length : endInclusive + 1) - start; + var r = new double[len]; + Array.Copy(a, start, r, 0, len); + return r; + } + + private static Matrix RegressionTrainX() => new Matrix(new List { Sub(income, 0, 118), Sub(production, 0, 118), Sub(savings, 0, 118), Sub(unemployment, 0, 118) }); + private static Vector RegressionTrainY() => new Vector(Sub(consumption, 0, 118)); + private static Matrix RegressionTestX() => new Matrix(new List { Sub(income, 119, -1), Sub(production, 119, -1), Sub(savings, 119, -1), Sub(unemployment, 119, -1) }); + + /// + /// The Iris classification tree grown from seed 12345 with all four features must reproduce + /// the pinned structure node for node at full precision. + /// + [TestMethod] + public void Test_DecisionTree_GoldenStructure_Iris() + { + var dt = new DecisionTree(IrisTrainX(), IrisTrainY(), 12345) { IsRegression = false, Features = 4 }; + dt.Train(); + Assert.AreEqual(GoldenDecisionTreeIris, SerializeTree(dt.Root)); + } + + /// + /// The consumption regression tree grown from seed 12345 with all four features must + /// reproduce the pinned structure node for node at full precision. + /// + [TestMethod] + public void Test_DecisionTree_GoldenStructure_Regression() + { + var dt = new DecisionTree(RegressionTrainX(), RegressionTrainY(), 12345) { Features = 4 }; + dt.Train(); + Assert.AreEqual(GoldenDecisionTreeRegression, SerializeTree(dt.Root)); + } + + /// + /// The Iris random forest from seed 12345 must reproduce the pinned lower, median, and upper + /// percentile predictions bit for bit on the held-out rows. + /// + [TestMethod] + public void Test_RandomForest_GoldenPredictions_Iris() + { + var rf = new RandomForest(IrisTrainX(), IrisTrainY(), 12345) { IsRegression = false, Features = 4 }; + rf.Train(); + Assert.AreEqual(GoldenForestIrisPercentiles, SerializeColumns(rf.Predict(IrisTestX())!, 3)); + } + + /// + /// The consumption regression random forest from seed 12345 must reproduce the pinned lower, + /// median, and upper percentile predictions bit for bit on the held-out rows. + /// + [TestMethod] + public void Test_RandomForest_GoldenPredictions_Regression() + { + var rf = new RandomForest(RegressionTrainX(), RegressionTrainY(), 12345) { Features = 4 }; + rf.Train(); + Assert.AreEqual(GoldenForestRegressionPercentiles, SerializeColumns(rf.Predict(RegressionTestX())!, 3)); + } + + /// + /// Repeated predictions from one trained forest must be bit-for-bit identical across all + /// four output columns, including the mean. + /// + [TestMethod] + public void Test_RandomForest_PredictIsDeterministic() + { + var rf = new RandomForest(RegressionTrainX(), RegressionTrainY(), 12345) { Features = 4 }; + rf.Train(); + var first = rf.Predict(RegressionTestX())!; + var second = rf.Predict(RegressionTestX())!; + for (int i = 0; i < first.GetLength(0); i++) + for (int j = 0; j < 4; j++) + Assert.AreEqual(BitConverter.DoubleToInt64Bits(first[i, j]), BitConverter.DoubleToInt64Bits(second[i, j]), $"cell [{i},{j}]"); + } + + /// + /// The root split of the regression tree must match the best split determined by exhaustive + /// search in exact rational arithmetic over every candidate threshold of every feature. + /// + [TestMethod] + public void Test_DecisionTree_BestSplitMatchesExactOracle() + { + var x = new Matrix(new List { OracleX0, OracleX1, OracleX2 }); + var y = new Vector(OracleY); + var dt = new DecisionTree(x, y, 12345) { Features = 3 }; + dt.Train(); + Assert.AreEqual(OracleBestFeature, dt.Root.FeatureIndex); + Assert.AreEqual(BitConverter.DoubleToInt64Bits(OracleBestThreshold), BitConverter.DoubleToInt64Bits(dt.Root.Threshold)); + } + } +} From c68f852fd493d969055589fc2a38314ce0f8b43c Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 26 Aug 2026 12:38:40 -0600 Subject: [PATCH 101/222] Keep the golden serializer inside the non-nullable annotation context --- Test_Numerics/Machine Learning/Supervised/Test_TreeGoldens.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Test_Numerics/Machine Learning/Supervised/Test_TreeGoldens.cs b/Test_Numerics/Machine Learning/Supervised/Test_TreeGoldens.cs index d301aaf6..0b29abfd 100644 --- a/Test_Numerics/Machine Learning/Supervised/Test_TreeGoldens.cs +++ b/Test_Numerics/Machine Learning/Supervised/Test_TreeGoldens.cs @@ -158,10 +158,10 @@ public class Test_TreeGoldens /// /// The root of the subtree to serialize. /// One pipe-delimited entry per node. - private static string SerializeTree(DecisionNode? node) + private static string SerializeTree(DecisionNode node) { var sb = new StringBuilder(); - void Walk(DecisionNode? n) + void Walk(DecisionNode n) { if (n == null) return; sb.Append(n.FeatureIndex).Append(';') From 83f0bab14da9b6e7a454e4b97f35f35dcbe5f6f1 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 26 Aug 2026 12:49:53 -0600 Subject: [PATCH 102/222] Share the parent impurity, skip searches at leaves, and stream the split statistics --- .../Supervised/DecisionTree.cs | 256 ++++++++++++------ 1 file changed, 179 insertions(+), 77 deletions(-) diff --git a/Numerics/Machine Learning/Supervised/DecisionTree.cs b/Numerics/Machine Learning/Supervised/DecisionTree.cs index f6db1b76..156ba0ab 100644 --- a/Numerics/Machine Learning/Supervised/DecisionTree.cs +++ b/Numerics/Machine Learning/Supervised/DecisionTree.cs @@ -1,5 +1,4 @@ using Numerics.Data.Statistics; -using Numerics.Distributions; using Numerics.Mathematics.LinearAlgebra; using Numerics.Sampling; using System; @@ -172,26 +171,22 @@ private DecisionNode GrowTree(Matrix xTrain, Vector yTrain, int depth = 0) int numberOfSamples = xTrain.NumberOfRows; int numberOfLabels = IsRegression ? yTrain.Length : yTrain.ToList().Distinct().Count(); - // Find the best split + // The feature subset is drawn for every node, split or leaf, so the generator consumes + // exactly one draw per node and the draw schedule is independent of the stopping conditions. var featureIdxs = Random.NextIntegers(0, Dimensions, Features, false); + + // Leaf conditions that need no split search + if (depth >= MaxDepth || numberOfLabels <= 1 || numberOfSamples < MinimumSplitSize) + { + return CreateLeaf(yTrain); + } + + // Find the best split int bestIndex = 0; double bestThreshold = 0; BestSplit(xTrain, yTrain.ToArray(), featureIdxs, out bestIndex, out bestThreshold); - - // Check stopping criteria - if (bestIndex == -1 || depth >= MaxDepth || numberOfLabels <= 1 || numberOfSamples < MinimumSplitSize) + if (bestIndex == -1) { - if (IsRegression) - { - // If regression return the average of Y - var avg = Tools.Mean(yTrain.ToArray()); - return new DecisionNode() { Value = avg, IsLeafNode = true }; - } - else - { - // If classification, return the most common value - var most = yTrain.ToList().GroupBy(i => i).OrderByDescending(grp => grp.Count()).Select(grp => grp.Key).First(); - return new DecisionNode() { Value = most, IsLeafNode = true }; - } + return CreateLeaf(yTrain); } @@ -230,6 +225,27 @@ private DecisionNode GrowTree(Matrix xTrain, Vector yTrain, int depth = 0) return new DecisionNode() { FeatureIndex = bestIndex, Threshold = bestThreshold, Left = left, Right = right }; } + /// + /// Creates a leaf node from the response values. + /// + /// The training vector of response values. + /// A leaf node holding the mean response for regression or the most common label for classification. + private DecisionNode CreateLeaf(Vector yTrain) + { + if (IsRegression) + { + // If regression return the average of Y + var avg = Tools.Mean(yTrain.ToArray()); + return new DecisionNode() { Value = avg, IsLeafNode = true }; + } + else + { + // If classification, return the most common value + var most = yTrain.ToList().GroupBy(i => i).OrderByDescending(grp => grp.Count()).Select(grp => grp.Key).First(); + return new DecisionNode() { Value = most, IsLeafNode = true }; + } + } + /// /// Returns the best split feature index and threshold. /// @@ -238,26 +254,39 @@ private DecisionNode GrowTree(Matrix xTrain, Vector yTrain, int depth = 0) /// The feature indexes to evaluate. /// Output. The best feature index. /// Output. The best threshold for splitting the tree. + /// + /// The parent impurity is constant within a node, so it is evaluated once and shared across + /// every candidate. Duplicate candidate thresholds produce identical splits, so only the + /// first occurrence of each value is evaluated; because ties break toward the earliest + /// candidate, the selected split is the same one an exhaustive scan selects. + /// private void BestSplit(Matrix xTrain, double[] yTrain, int[] indices, out int bestFeatureIndex, out double bestThreshold) { double best = double.MinValue; bestFeatureIndex = -1; bestThreshold = 0; + double parentImpurity = IsRegression ? Statistics.PopulationVariance(yTrain) : Entropy(yTrain); + var seen = new HashSet(); + for (int i = 0; i < indices.Length; i++) { var x = xTrain.Column(indices[i]); - var thresholds = IsRegression ? x : x.Distinct().ToArray(); - for (int j = 0; j < thresholds.Count(); j++) + seen.Clear(); + for (int j = 0; j < x.Length; j++) { + if (!seen.Add(x[j])) + { + continue; + } // Test if the split variance reduction or information gain - double performance = IsRegression ? VarianceReduction(x, yTrain, thresholds[j]) : InformationGain(x, yTrain, thresholds[j]); + double performance = IsRegression ? VarianceReduction(x, yTrain, x[j], parentImpurity) : InformationGain(x, yTrain, x[j], parentImpurity); // Keep track of the best value if (performance > best) { best = performance; bestFeatureIndex = indices[i]; - bestThreshold = thresholds[j]; + bestThreshold = x[j]; } } } @@ -270,31 +299,60 @@ private void BestSplit(Matrix xTrain, double[] yTrain, int[] indices, out int be /// The column of x-values. /// The column of y-values. /// The split threshold. - private double VarianceReduction(double[] x, double[] y, double threshold) + /// The population variance of the parent node response values. + /// The variance reduction of the split, or double.MinValue when the threshold places every sample in one child. + /// + /// The child variances are accumulated in sample order with the same recurrence as + /// , so + /// the reduction is identical to evaluating that method on materialized child arrays. + /// + private double VarianceReduction(double[] x, double[] y, double threshold, double parentVariance) { - // parent entropy - var parentVariance = Statistics.PopulationVariance(y); - - // create children - var leftIdxs = new List(); - var rightIdxs = new List(); - Split(x, threshold, out leftIdxs, out rightIdxs); + // Stream both child variances in one pass over the sample + int countLeft = 0, countRight = 0; + double sumLeft = 0, sumRight = 0, accLeft = 0, accRight = 0; + for (int i = 0; i < x.Length; i++) + { + double v = y[i]; + if (x[i] <= threshold) + { + countLeft++; + if (countLeft == 1) + { + sumLeft = v; + } + else + { + sumLeft += v; + double diff = countLeft * v - sumLeft; + accLeft += diff * diff / (countLeft * (countLeft - 1.0)); + } + } + else + { + countRight++; + if (countRight == 1) + { + sumRight = v; + } + else + { + sumRight += v; + double diff = countRight * v - sumRight; + accRight += diff * diff / (countRight * (countRight - 1.0)); + } + } + } - if (leftIdxs.Count == 0 || rightIdxs.Count == 0) + if (countLeft == 0 || countRight == 0) return double.MinValue; // calculate the weighted average variance of the children var n = (double)y.Length; - var nLeft = (double)leftIdxs.Count; - var nRight = (double)rightIdxs.Count; - var yLeft = new double[leftIdxs.Count]; - for (int i = 0; i < leftIdxs.Count; i++) - yLeft[i] = y[leftIdxs[i]]; - var yRight = new double[rightIdxs.Count]; - for (int i = 0; i < rightIdxs.Count; i++) - yRight[i] = y[rightIdxs[i]]; - var varLeft = Statistics.PopulationVariance(yLeft); - var varRight = Statistics.PopulationVariance(yRight); + var nLeft = (double)countLeft; + var nRight = (double)countRight; + var varLeft = accLeft / countLeft; + var varRight = accRight / countRight; var childVariance = nLeft / n * varLeft + nRight / n * varRight; // Return variance reduction @@ -307,61 +365,105 @@ private double VarianceReduction(double[] x, double[] y, double threshold) /// The column of x-values. /// The column of y-values. /// The split threshold. - private double InformationGain(double[] x, double[] y, double threshold) + /// The entropy of the parent node response values. + /// The information gain of the split, or double.MinValue when the threshold places every sample in one child. + /// + /// Label probabilities are the per-child label counts divided by the child size, and entropy + /// terms are accumulated in sample order, so each child entropy is identical to evaluating + /// on a materialized child array. + /// + private double InformationGain(double[] x, double[] y, double threshold, double parentEntropy) { - // parent entropy - var parentE = Entropy(y); - - // create children - var leftIdxs = new List(); - var rightIdxs = new List(); - Split(x, threshold, out leftIdxs, out rightIdxs); + // Count the labels on each side of the threshold + var leftCounts = new Dictionary(); + var rightCounts = new Dictionary(); + int countLeft = 0, countRight = 0; + for (int i = 0; i < x.Length; i++) + { + double v = y[i]; + if (x[i] <= threshold) + { + countLeft++; + if (!double.IsNaN(v)) + { + leftCounts.TryGetValue(v, out int c); + leftCounts[v] = c + 1; + } + } + else + { + countRight++; + if (!double.IsNaN(v)) + { + rightCounts.TryGetValue(v, out int c); + rightCounts[v] = c + 1; + } + } + } - if (leftIdxs.Count == 0 || rightIdxs.Count == 0) + if (countLeft == 0 || countRight == 0) return double.MinValue; // calculate the weighted average entropy of children + double sumLeft = 0, sumRight = 0; + for (int i = 0; i < x.Length; i++) + { + double v = y[i]; + if (double.IsNaN(v)) + continue; + if (x[i] <= threshold) + { + double p = (double)leftCounts[v] / countLeft; + sumLeft += p * Math.Log(p); + } + else + { + double p = (double)rightCounts[v] / countRight; + sumRight += p * Math.Log(p); + } + } + var n = (double)y.Length; - var nl = (double)leftIdxs.Count; - var nr = (double)rightIdxs.Count; - var yl = new double[leftIdxs.Count]; - for (int i = 0; i < leftIdxs.Count; i++) - yl[i] = y[leftIdxs[i]]; - var yr = new double[rightIdxs.Count]; - for (int i = 0; i < rightIdxs.Count; i++) - yr[i] = y[rightIdxs[i]]; - var el = Entropy(yl); - var er = Entropy(yr); - var childrenE = nl / n * el + nr / n * er; + var nl = (double)countLeft; + var nr = (double)countRight; + var childrenE = nl / n * (-sumLeft) + nr / n * (-sumRight); - return parentE - childrenE; + return parentEntropy - childrenE; } /// - /// Computes the entropy for vector of y-values. + /// Computes the entropy for a vector of classification labels. /// /// The column of y-values. + /// The entropy of the labels. + /// + /// The empirical probability of each label is its count divided by the sample length, and + /// terms are accumulated in sample order. Labels of NaN carry an empirical probability of + /// zero and contribute nothing. + /// private double Entropy(double[] y) { - if (IsRegression == true) + var counts = new Dictionary(); + for (int i = 0; i < y.Length; i++) { - // use kernel density - var kde = new KernelDensity(y); - return Statistics.Entropy(y, kde.PDF); + double v = y[i]; + if (!double.IsNaN(v)) + { + counts.TryGetValue(v, out int c); + counts[v] = c + 1; + } } - else + + double sum = 0; + for (int i = 0; i < y.Length; i++) { - // Use histogram - return Statistics.Entropy(y, (x) => - { - double n = 0; - for (int i = 0; i < y.Length; i++) - { - if (x == y[i]){ n++; } - } - return n / y.Length; - }); + double v = y[i]; + if (double.IsNaN(v)) + continue; + double p = (double)counts[v] / y.Length; + sum += p * Math.Log(p); } + return -sum; } /// From d5473201fe3219a02916610bba31f25c9f52e396 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 26 Aug 2026 12:53:56 -0600 Subject: [PATCH 103/222] Make the forest mean reduction sequential and pin all four prediction columns --- .../Supervised/RandomForest.cs | 6 +- .../Supervised/Test_TreeGoldens.cs | 96 +++++++++++-------- 2 files changed, 58 insertions(+), 44 deletions(-) diff --git a/Numerics/Machine Learning/Supervised/RandomForest.cs b/Numerics/Machine Learning/Supervised/RandomForest.cs index e19675f2..15ff9d93 100644 --- a/Numerics/Machine Learning/Supervised/RandomForest.cs +++ b/Numerics/Machine Learning/Supervised/RandomForest.cs @@ -236,19 +236,21 @@ private DecisionTree BootstrapDecisionTree(int seed = -1) var values = bootResults.GetRow(idx); Array.Sort(values); + // The mean is accumulated sequentially so the reduction is deterministic on every + // host regardless of processor count. if (IsRegression) { // Record percentiles for CIs for (int j = 0; j < percentiles.Length; j++) output[idx, j] = Statistics.Percentile(values, percentiles[j], true); - output[idx, 3] = Statistics.ParallelMean(values); + output[idx, 3] = Statistics.Mean(values); } else { // Record percentiles for CIs for (int j = 0; j < percentiles.Length; j++) output[idx, j] = Math.Floor(Statistics.Percentile(values, percentiles[j], true)); - output[idx, 3] = Math.Floor(Statistics.ParallelMean(values)); + output[idx, 3] = Math.Floor(Statistics.Mean(values)); } }); diff --git a/Test_Numerics/Machine Learning/Supervised/Test_TreeGoldens.cs b/Test_Numerics/Machine Learning/Supervised/Test_TreeGoldens.cs index 0b29abfd..9cb8c6d3 100644 --- a/Test_Numerics/Machine Learning/Supervised/Test_TreeGoldens.cs +++ b/Test_Numerics/Machine Learning/Supervised/Test_TreeGoldens.cs @@ -24,8 +24,9 @@ namespace MachineLearning /// seeded contract of and on the Iris /// classification fixture and the quarterly consumption regression fixture, so any change to the /// split search, stopping criteria, or prediction reductions is observable at full precision. - /// The prediction goldens cover the lower, median, and upper percentile columns; the mean column - /// is governed by . + /// The prediction goldens cover all four output columns, and + /// holds repeated predictions to bitwise + /// equality. /// /// /// The split-search oracle fixture was evaluated with exact rational arithmetic @@ -108,43 +109,54 @@ public class Test_TreeGoldens "D76CB991B198C;1|-1;FFF8000000000000;3FEC0EACE14A0760;1|3;3FECCCCCCCCCCCCD;FFF8000000000000;0|0;3FC909A067BF0107;FFF8000000000000;0|-1;FFF8000000000000;BFE8178FD41DF9E6;1|-1;FFF8000000000000;BF9AE56B54" + "36E3ED;1|0;BFF76C12C04C0AF0;FFF8000000000000;0|-1;FFF8000000000000;C0023160AEEC671C;1|-1;FFF8000000000000;BFF79CE9829012B2;1|"; - private const string GoldenForestIrisPercentiles = + private const string GoldenForestIrisPredictions = "3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000" + "000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF000000" + - "0000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|4000000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF00" + - "00000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3" + - "FF0000000000000|3FF0000000000000|3FF0000000000000|4000000000000000|3FF0000000000000|3FF0000000000000|4000000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF00000000000" + - "00|3FF0000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4008000000000000|4000000000000000|4000000000" + - "000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4008000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|400000" + - "0000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4008000000000000|4000000000000000|4000000000000000|4008000000000000|4000000000000000|40" + - "00000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4008000000000000|4008000000000000|4000000000000000|4000000000000000|4000000000000000|400000000000000" + - "0|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|40000000000" + - "00000|4000000000000000|4000000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4000000000000000|4000000000000000|4008000000000000|4008000" + - "000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4000000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|400" + - "8000000000000|4008000000000000|4008000000000000|4000000000000000|4008000000000000|4008000000000000|4000000000000000|4008000000000000|4008000000000000|4000000000000000|4000000000000000|4008000000000000" + - "|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4000000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|400800000000" + - "0000|4000000000000000|4000000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4000000000000000|4008000000000000|40080000" + - "00000000|4008000000000000|4008000000000000|4008000000000000|"; + "0000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF00" + + "00000000000|3FF0000000000000|3FF0000000000000|4000000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3" + + "FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF00000000000" + + "00|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|4000000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|4000000000" + + "000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|3FF0000000000000|4000000000000000|4000000000000000|400000" + + "0000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4008000000000000|4000000000000000|4000000000000000|4000000000000000|40" + + "00000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4008000000000000|4000000000000000|4000000000000000|400000000000000" + + "0|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|3FF0000000000000|4000000000000000|40000000000" + + "00000|4008000000000000|4000000000000000|4000000000000000|4000000000000000|4008000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000" + + "000000000|4000000000000000|4000000000000000|4000000000000000|4008000000000000|4008000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|400" + + "0000000000000|4000000000000000|3FF0000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|4000000000000000|3FF0000000000000|4000000000000000" + + "|4000000000000000|4000000000000000|3FF0000000000000|4000000000000000|4000000000000000|4000000000000000|3FF0000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|400800000000" + + "0000|4008000000000000|4008000000000000|4008000000000000|4000000000000000|4000000000000000|4008000000000000|4000000000000000|4008000000000000|4008000000000000|4008000000000000|4000000000000000|40080000" + + "00000000|4008000000000000|4008000000000000|4008000000000000|4000000000000000|4008000000000000|4008000000000000|4000000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4008" + + "000000000000|4008000000000000|4008000000000000|4008000000000000|4000000000000000|4008000000000000|4008000000000000|4000000000000000|4000000000000000|4008000000000000|4008000000000000|4000000000000000|" + + "4000000000000000|4000000000000000|4008000000000000|4000000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000" + + "000|4000000000000000|4008000000000000|4008000000000000|4000000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4000000000000000|4000000000000000|4008000000000000|400000000" + + "0000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|4000000000000000|4008000000000000|4008000000000000|40000" + + "00000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|"; - private const string GoldenForestRegressionPercentiles = - "3FE9889E69E2F2A9|3FF6A4062C69EB56|3FFE5BA1712962FB|3FD9B4A87E38EB03|3FF06E54B73C6CC3|3FF87DCDF6987831|3FE337C563CCD90C|3FEE57C080E41615|3FF87DCDF6987831|3FDD7B2691E51A4C|3FEB5FB77B00FD2B|3FF275749C8A0" + - "EDC|3FEC945E61344D65|3FF783F2FC0B7EB9|3FFE70E9D51B4FE8|BFE81A1D312D0D6E|BF9AE56B5436E3ED|3FEC0EACE14A0760|BFAD91DA3290F6DE|3FE519ABF896D519|3FF50553CC85D517|BFE93FACC9C47C00|3FFA7E4C6E988F03|3FFDC26FE" + - "4697691|BFEC109CE5FF4DD3|3FEC945E61344D65|3FFE70E9D51B4FE8|3FD9B4A87E38EB03|3FFA7E4C6E988F03|40028BDE18CF84B8|3FD12B552DD9D7F8|3FE8A01C14B39645|3FF4C84988094E5D|3FDE0A4A5BC3CB1B|3FF191F8CB834BA4|3FF7B" + - "05B98821745|BF9AE56B5436E3ED|3FD9C469DC929294|3FED3ECCE9FE0B08|BFC3B39C0EBEDFA4|3FDA36D945811391|3FEE57C080E41615|BF9AE56B5436E3ED|3FEC0EACE14A0760|3FFDC26FE4697691|3FEC0EACE14A0760|3FF7EF9ACD4B4CDA|3" + - "FFDC26FE4697691|3FE467E93D9BC1E8|3FF22E98742A9F3B|3FF7579900ED65CA|3FE6687B99D451FC|3FF4C84988094E5D|3FF7579900ED65CA|BFA55EF0A645CF6D|3FDD7B2691E51A4C|3FE7608F6C9A0728|3FE6687B99D451FC|3FF139CDA6BC6C" + - "D8|3FFCB84BB1036D9E|3FC9A80BE0918367|3FE87EB2B87983A5|3FF6A4062C69EB56|BFAD91DA3290F6DE|3FF6097D675EDEDE|3FFE70E9D51B4FE8|3FF191F8CB834BA4|3FF7579900ED65CA|3FFF59FB714FF968|3FE439A4DDABA879|3FEFA9FEF1" + - "E306CB|3FFCF0AE63802D0C|BFA55EF0A645CF6D|3FD06B925501BA14|3FE02FE3E89D37DF|3FD9B4A87E38EB03|3FFA7E4C6E988F03|40028BDE18CF84B8|3FEC945E61344D65|3FF191F8CB834BA4|3FF783F2FC0B7EB9|3FEC945E61344D65|3FF211" + - "4313A5FA25|3FFCF0AE63802D0C|3FDCF0D22FE31C9A|3FEB3A04B8F757E6|3FF1D21DEA657DC5|BFA55EF0A645CF6D|3FDABFE7007FACB3|3FE337C563CCD90C|3FDF006245C55C15|3FEC9B0D6771A93B|3FF4C84988094E5D|3FDF006245C55C15|3F" + - "EC9B0D6771A93B|3FFAFC1F354F6D26|BFD61A3738D157CC|3FD679013DEDA159|3FE0CA91BED9C503|BFD2AA19EF6A5B93|BF9AE56B5436E3ED|3FEDE65BB45BBCBF|BFE93FACC9C47C00|3FEDE65BB45BBCBF|3FFDC26FE4697691|C0023160AEEC671" + - "C|3FD679013DEDA159|3FFE70E9D51B4FE8|C0023160AEEC671C|BFE8178FD41DF9E6|3FE3B628BBB5F921|C0023160AEEC671C|BFD61A3738D157CC|3FEAA1DABB77E5C0|BFF79CE9829012B2|BFA55EF0A645CF6D|3FE3B628BBB5F921|BFAD91DA329" + - "0F6DE|3FF2114313A5FA25|3FFCF0AE63802D0C|BFD18D3B7CD8C3A6|BF91B26166A24B6F|3FF4C84988094E5D|3FDF006245C55C15|3FF2401805DB3647|3FFE437B3CB74074|3FCAE6AAAEEAB99D|3FE1991D4D42D3E5|3FF2E3DA277E4931|3FDEE60" + - "177E07284|3FEABD94CB7E990D|3FF0E29AA4868799|3FE6687B99D451FC|3FF234D4EE845793|3FFC3C6C8A6CD5E6|3F9012147F93DEF0|3FD06B925501BA14|3FF1266CEEC0BD75|BF91B26166A24B6F|3FE0CA91BED9C503|3FF3081EFC222D6D|3FD" + - "3FCCC0E35F8B5|3FD8341733CE38B3|3FEB5FB77B00FD2B|BF91B26166A24B6F|3FE467E93D9BC1E8|3FF2F08888FB10D8|3FD9B4A87E38EB03|3FF06E54B73C6CC3|3FF87DCDF6987831|BFA55EF0A645CF6D|BFA55EF0A645CF6D|3FE1991D4D42D3E5" + - "|BFAD91DA3290F6DE|3FE19A8427ED59E0|3FF2F08888FB10D8|3FD9B4A87E38EB03|3FFA7E4C6E988F03|40028BDE18CF84B8|BFEC109CE5FF4DD3|3FF6097D675EDEDD|3FFB1254A1E539E3|BFC3B39C0EBEDFA4|BFA55EF0A645CF6D|3FE02FE3E89D" + - "37DF|3FD12B552DD9D7F8|3FDABFE7007FACB3|3FEB5FB77B00FD2B|3FE6687B99D451FC|3FF2114313A5FA25|3FFB0942086BBCE3|BFD2AA19EF6A5B93|3FCDDEE7AC7716E2|3FE87EB2B87983A5|BFA55EF0A645CF6D|3FE1991D4D42D3E5|3FF6A406" + - "2C69EB56|3FE3B628BBB5F921|3FED8E35DDDD6B56|3FF234D4EE845793|3FEC32F4CF4A558F|3FF234D4EE845793|3FFC35E78864E8E3|3FDEF41446DEE1A3|3FEC32F4CF4A558F|3FF2130A5E977ECF|3FDC1F953BE6E0F5|3FEAFA34DF85B70F|3FF1" + - "C9D36DD0BDF3|3FDABFE7007FACB3|3FEB3A04B8F757E5|3FEE57C080E41615|3FD12B552DD9D7F8|3FE26A769100EEBD|3FF1C9D36DD0BDF3|3FD12B552DD9D7F8|3FD8341733CE38B3|3FEFA9FEF1E306CB|3FE8B5669433A925|3FF0E29AA4868799|" + - "3FFC02D7B385A8F7|3FE6687B99D451FC|3FEABD94CB7E990D|3FF139CDA6BC6CD8|"; + private const string GoldenForestRegressionPredictions = + "3FE9889E69E2F2A9|3FF6A4062C69EB56|3FFE5BA1712962FB|3FF669BEF2471996|3FD9B4A87E38EB03|3FF06E54B73C6CC3|3FF87DCDF6987831|3FED787B466FED2D|3FE337C563CCD90C|3FEE57C080E41615|3FF87DCDF6987831|3FEF3C65183B9" + + "5D8|3FDD7B2691E51A4C|3FEB5FB77B00FD2B|3FF275749C8A0EDC|3FED8BCF8A3EA284|3FEC945E61344D65|3FF783F2FC0B7EB9|3FFE70E9D51B4FE8|3FF5E276A85D96D5|BFE81A1D312D0D6E|BF9AE56B5436E3ED|3FEC0EACE14A0760|BF8A9A106" + + "7774B3B|BFAD91DA3290F6DE|3FE519ABF896D519|3FF50553CC85D517|3FE50E478D0CE481|BFE93FACC9C47C00|3FFA7E4C6E988F03|3FFDC26FE4697691|3FF18499CE91ABDF|BFEC109CE5FF4DD3|3FEC945E61344D65|3FFE70E9D51B4FE8|3FEC9" + + "74FC2621C16|3FD9B4A87E38EB03|3FFA7E4C6E988F03|40028BDE18CF84B8|3FF6937CD109372E|3FD12B552DD9D7F8|3FE8A01C14B39645|3FF4C84988094E5D|3FE5474DEABF5D04|3FDE0A4A5BC3CB1B|3FF191F8CB834BA4|3FF7B05B98821745|3" + + "FF137B5668FE832|BF9AE56B5436E3ED|3FD9C469DC929294|3FED3ECCE9FE0B08|3FE03D532E1BB86F|BFC3B39C0EBEDFA4|3FDA36D945811391|3FEE57C080E41615|3FDCD0BD403D8C18|BF9AE56B5436E3ED|3FEC0EACE14A0760|3FFDC26FE46976" + + "91|3FEA7750859B0944|3FEC0EACE14A0760|3FF7EF9ACD4B4CDA|3FFDC26FE4697691|3FF5298D228CA27E|3FE467E93D9BC1E8|3FF22E98742A9F3B|3FF7579900ED65CA|3FF19F0F39740BBE|3FE6687B99D451FC|3FF4C84988094E5D|3FF7579900" + + "ED65CA|3FF218C2768CC36F|BFA55EF0A645CF6D|3FDD7B2691E51A4C|3FE7608F6C9A0728|3FD8C3E144374170|3FE6687B99D451FC|3FF139CDA6BC6CD8|3FFCB84BB1036D9E|3FF23D057EA77524|3FC9A80BE0918367|3FE87EB2B87983A5|3FF6A4" + + "062C69EB56|3FEA3C2E0B5DAE88|BFAD91DA3290F6DE|3FF6097D675EDEDE|3FFE70E9D51B4FE8|3FF42ED149C1376E|3FF191F8CB834BA4|3FF7579900ED65CA|3FFF59FB714FF968|3FF80C0D04C61905|3FE439A4DDABA879|3FEFA9FEF1E306CB|3F" + + "FCF0AE63802D0C|3FF12D4008BECB6D|BFA55EF0A645CF6D|3FD06B925501BA14|3FE02FE3E89D37DF|3FCA42D289F5EE8A|3FD9B4A87E38EB03|3FFA7E4C6E988F03|40028BDE18CF84B8|3FF749F1C483CB4E|3FEC945E61344D65|3FF191F8CB834BA" + + "4|3FF783F2FC0B7EB9|3FF1D76D2BC2F57B|3FEC945E61344D65|3FF2114313A5FA25|3FFCF0AE63802D0C|3FF39AE1A59586EC|3FDCF0D22FE31C9A|3FEB3A04B8F757E6|3FF1D21DEA657DC5|3FEB6F37EE1B6285|BFA55EF0A645CF6D|3FDABFE7007" + + "FACB3|3FE337C563CCD90C|3FD0598A1E4782F9|3FDF006245C55C15|3FEC9B0D6771A93B|3FF4C84988094E5D|3FEE1B53358BEFB6|3FDF006245C55C15|3FEC9B0D6771A93B|3FFAFC1F354F6D26|3FEF383A93FAF60F|BFD61A3738D157CC|3FD6790" + + "13DEDA159|3FE0CA91BED9C503|3FC5153FBFABFA86|BFD2AA19EF6A5B93|BF9AE56B5436E3ED|3FEDE65BB45BBCBF|3FAC52E825050225|BFE93FACC9C47C00|3FEDE65BB45BBCBF|3FFDC26FE4697691|3FEB81A844AF3E1E|C0023160AEEC671C|3FD" + + "679013DEDA159|3FFE70E9D51B4FE8|3FA5DAC759D9DD1A|C0023160AEEC671C|BFE8178FD41DF9E6|3FE3B628BBB5F921|BFE7E0701172873E|C0023160AEEC671C|BFD61A3738D157CC|3FEAA1DABB77E5C0|BFE266A61E705704|BFF79CE9829012B2" + + "|BFA55EF0A645CF6D|3FE3B628BBB5F921|BFD5CA89BCE25E56|BFAD91DA3290F6DE|3FF2114313A5FA25|3FFCF0AE63802D0C|3FEEA3922738EEC3|BFD18D3B7CD8C3A6|BF91B26166A24B6F|3FF4C84988094E5D|3FD08D9ED3641B99|3FDF006245C5" + + "5C15|3FF2401805DB3647|3FFE437B3CB74074|3FF26244B5D39531|3FCAE6AAAEEAB99D|3FE1991D4D42D3E5|3FF2E3DA277E4931|3FE3E43274C80241|3FDEE60177E07284|3FEABD94CB7E990D|3FF0E29AA4868799|3FE9EFBCD97D7338|3FE6687B" + + "99D451FC|3FF234D4EE845793|3FFC3C6C8A6CD5E6|3FF20B5B5D553A1C|3F9012147F93DEF0|3FD06B925501BA14|3FF1266CEEC0BD75|3FD8F42F692488CE|BF91B26166A24B6F|3FE0CA91BED9C503|3FF3081EFC222D6D|3FE0E4CA7315F855|3FD3" + + "FCCC0E35F8B5|3FD8341733CE38B3|3FEB5FB77B00FD2B|3FDE9CD912B28A9C|BF91B26166A24B6F|3FE467E93D9BC1E8|3FF2F08888FB10D8|3FE53B51273F2834|3FD9B4A87E38EB03|3FF06E54B73C6CC3|3FF87DCDF6987831|3FEF0FDB42F34DB2|" + + "BFA55EF0A645CF6D|BFA55EF0A645CF6D|3FE1991D4D42D3E5|3FB6D5D221D1DE12|BFAD91DA3290F6DE|3FE19A8427ED59E0|3FF2F08888FB10D8|3FE034365CB56F1A|3FD9B4A87E38EB03|3FFA7E4C6E988F03|40028BDE18CF84B8|3FF6697794218" + + "FF3|BFEC109CE5FF4DD3|3FF6097D675EDEDD|3FFB1254A1E539E3|3FE5822ED2268439|BFC3B39C0EBEDFA4|BFA55EF0A645CF6D|3FE02FE3E89D37DF|3FB9464AC95B34EA|3FD12B552DD9D7F8|3FDABFE7007FACB3|3FEB5FB77B00FD2B|3FDD2421E" + + "869D7D0|3FE6687B99D451FC|3FF2114313A5FA25|3FFB0942086BBCE3|3FF2875F2B0EF177|BFD2AA19EF6A5B93|3FCDDEE7AC7716E2|3FE87EB2B87983A5|3FC54F0130A8D3B1|BFA55EF0A645CF6D|3FE1991D4D42D3E5|3FF6A4062C69EB56|3FE59" + + "401013DDFE3|3FE3B628BBB5F921|3FED8E35DDDD6B56|3FF234D4EE845793|3FED869191872C8D|3FEC32F4CF4A558F|3FF234D4EE845793|3FFC35E78864E8E3|3FF42F9B0CEBE0B8|3FDEF41446DEE1A3|3FEC32F4CF4A558F|3FF2130A5E977ECF|3" + + "FEBA94A918F8568|3FDC1F953BE6E0F5|3FEAFA34DF85B70F|3FF1C9D36DD0BDF3|3FE8B98EFC8F8CCA|3FDABFE7007FACB3|3FEB3A04B8F757E5|3FEE57C080E41615|3FE74D9A68F6056E|3FD12B552DD9D7F8|3FE26A769100EEBD|3FF1C9D36DD0BD" + + "F3|3FE3B0CD989CC01F|3FD12B552DD9D7F8|3FD8341733CE38B3|3FEFA9FEF1E306CB|3FE15BD80F536093|3FE8B5669433A925|3FF0E29AA4868799|3FFC02D7B385A8F7|3FF18F7394501A7B|3FE6687B99D451FC|3FEABD94CB7E990D|3FF139CDA6" + + "BC6CD8|3FEBBE06D4815EBF|"; private static readonly double[] OracleX0 = { 4.744781, 6.255026, 1.160139, 8.741791, 0.319224, 1.201623, 7.703759, 8.249999, 7.763458, 2.561846, 7.844684, 9.530505, 5.237435, 7.938164, 7.634402, 3.538497, 3.908757, 0.798954, 4.233405, 4.819062, 4.704432, 8.131584, 7.138561, 5.718968, 0.230697, 8.428248, 2.642866, 0.617403, 9.190608, 5.605545, 9.120073, 5.506271, 1.210414, 7.754063, 6.823279, 8.004035, 7.097406, 5.353946, 3.109105, 1.01253 }; private static readonly double[] OracleX1 = { 6.041412, 1.540177, 0.705583, 2.134676, 3.987951, 8.706857, 4.997143, 7.688693, 7.269118, 9.894784, 2.850013, 2.908263, 4.640485, 9.239211, 2.437277, 9.911633, 5.759533, 1.255405, 2.324209, 0.226532, 2.977218, 1.713005, 4.749645, 2.982202, 8.679028, 2.531092, 8.802673, 1.569402, 4.644899, 2.859959, 8.467362, 0.291196, 5.153139, 1.003399, 3.90648, 6.822431, 9.421524, 6.81047, 2.76754, 4.48085 }; @@ -237,27 +249,27 @@ public void Test_DecisionTree_GoldenStructure_Regression() } /// - /// The Iris random forest from seed 12345 must reproduce the pinned lower, median, and upper - /// percentile predictions bit for bit on the held-out rows. + /// The Iris random forest from seed 12345 must reproduce every pinned prediction cell + /// bit for bit on the held-out rows, mean column included. /// [TestMethod] public void Test_RandomForest_GoldenPredictions_Iris() { var rf = new RandomForest(IrisTrainX(), IrisTrainY(), 12345) { IsRegression = false, Features = 4 }; rf.Train(); - Assert.AreEqual(GoldenForestIrisPercentiles, SerializeColumns(rf.Predict(IrisTestX())!, 3)); + Assert.AreEqual(GoldenForestIrisPredictions, SerializeColumns(rf.Predict(IrisTestX())!, 4)); } /// - /// The consumption regression random forest from seed 12345 must reproduce the pinned lower, - /// median, and upper percentile predictions bit for bit on the held-out rows. + /// The consumption regression random forest from seed 12345 must reproduce every pinned + /// prediction cell bit for bit on the held-out rows, mean column included. /// [TestMethod] public void Test_RandomForest_GoldenPredictions_Regression() { var rf = new RandomForest(RegressionTrainX(), RegressionTrainY(), 12345) { Features = 4 }; rf.Train(); - Assert.AreEqual(GoldenForestRegressionPercentiles, SerializeColumns(rf.Predict(RegressionTestX())!, 3)); + Assert.AreEqual(GoldenForestRegressionPredictions, SerializeColumns(rf.Predict(RegressionTestX())!, 4)); } /// From 49944621afb6eb95ef3cb475f888c6e6778784f9 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 26 Aug 2026 13:23:32 -0600 Subject: [PATCH 104/222] Grow trees over partitioned indices with a sorted single-sweep split search --- .../Supervised/DecisionTree.cs | 613 ++++++++++++------ .../Supervised/RandomForest.cs | 62 +- .../Supervised/Test_TreeGoldens.cs | 66 +- 3 files changed, 466 insertions(+), 275 deletions(-) diff --git a/Numerics/Machine Learning/Supervised/DecisionTree.cs b/Numerics/Machine Learning/Supervised/DecisionTree.cs index 156ba0ab..5f308464 100644 --- a/Numerics/Machine Learning/Supervised/DecisionTree.cs +++ b/Numerics/Machine Learning/Supervised/DecisionTree.cs @@ -91,10 +91,42 @@ public DecisionTree(Matrix x, Vector y, int seed = -1) } + /// + /// Create new Decision Tree that trains on a caller-specified multiset of shared training rows. + /// + /// The training matrix of predictor values, shared with the caller. + /// The training response vector, shared with the caller. + /// The rows of that form the training multiset, in training order. + /// The prng seed. + internal DecisionTree(Matrix x, Vector y, int[] sampleIndices, int seed) + { + // Set inputs + Y = y; + X = x; + Dimensions = X.NumberOfColumns; + Features = Math.Max(1, Dimensions - 1); + Root = new DecisionNode(); + Random = new MersenneTwister(seed); + _sampleIndices = sampleIndices; + + if (Y.Length != X.NumberOfRows) throw new ArgumentException("The y vector must be the same length as the x matrix."); + if (Y.Length < 10) throw new ArgumentException("There must be at least ten training data points."); + + } + #endregion #region Members + // The training rows this tree trains on; null trains on every row. + private readonly int[]? _sampleIndices; + + // Split-search scratch, allocated per training run and shared down the single-threaded recursion + private double[] _keyScratch = Array.Empty(); + private double[] _valScratch = Array.Empty(); + private double[] _rightAccScratch = Array.Empty(); + private int[] _partitionScratch = Array.Empty(); + /// /// The minimum split size of the samples. Default = 2. /// @@ -148,28 +180,55 @@ public DecisionTree(Matrix x, Vector y, int seed = -1) #endregion #region Methods - + /// - /// Train the decision tree. + /// Train the decision tree. /// public void Train() { IsTrained = false; Features = Math.Min(Features, Dimensions); - Root = GrowTree(X, Y); + int n = X.NumberOfRows; + + // Row indices define the training multiset, so a bootstrap tree shares the parent + // matrix rather than holding its own copy. + var indices = new int[_sampleIndices == null ? n : _sampleIndices.Length]; + if (_sampleIndices == null) + { + for (int i = 0; i < n; i++) indices[i] = i; + } + else + { + Array.Copy(_sampleIndices, indices, _sampleIndices.Length); + } + + // Scratch buffers sized once per training run and shared down the recursion, which is + // single threaded within a tree. + int m = indices.Length; + _keyScratch = new double[m]; + _valScratch = new double[m]; + _rightAccScratch = new double[m + 1]; + _partitionScratch = new int[m]; + + Root = GrowTree(indices, 0, m, 0); + + _keyScratch = _valScratch = _rightAccScratch = Array.Empty(); + _partitionScratch = Array.Empty(); IsTrained = true; } /// - /// Grow the decision tree recursively. + /// Grow the decision tree recursively over a range of training row indices. /// - /// The training matrix of predictor values. - /// The training vector of response values. + /// The training row indices, partitioned in place as the tree grows. + /// The inclusive start of the node's range within . + /// The exclusive end of the node's range within . /// The depth of the recursion. - private DecisionNode GrowTree(Matrix xTrain, Vector yTrain, int depth = 0) + /// The subtree root for the range: a decision node when a split is found, otherwise a leaf. + private DecisionNode GrowTree(int[] indices, int lo, int hi, int depth) { - int numberOfSamples = xTrain.NumberOfRows; - int numberOfLabels = IsRegression ? yTrain.Length : yTrain.ToList().Distinct().Count(); + int numberOfSamples = hi - lo; + int numberOfLabels = IsRegression ? numberOfSamples : CountDistinctLabels(indices, lo, hi); // The feature subset is drawn for every node, split or leaf, so the generator consumes // exactly one draw per node and the draw schedule is independent of the stopping conditions. @@ -178,316 +237,434 @@ private DecisionNode GrowTree(Matrix xTrain, Vector yTrain, int depth = 0) // Leaf conditions that need no split search if (depth >= MaxDepth || numberOfLabels <= 1 || numberOfSamples < MinimumSplitSize) { - return CreateLeaf(yTrain); + return CreateLeaf(indices, lo, hi); } // Find the best split - int bestIndex = 0; double bestThreshold = 0; - BestSplit(xTrain, yTrain.ToArray(), featureIdxs, out bestIndex, out bestThreshold); + BestSplit(indices, lo, hi, featureIdxs, out int bestIndex, out double bestThreshold); if (bestIndex == -1) { - return CreateLeaf(yTrain); + return CreateLeaf(indices, lo, hi); } + // Partition the index range in place, keeping the relative row order stable on both + // sides. Rows with a NaN predictor fail x <= t and fall to the right. + int split = StablePartition(indices, lo, hi, bestIndex, bestThreshold); - // Create child nodes - var leftIdxs = new List(); - var rightIdxs = new List(); - Split(xTrain.Column(bestIndex), bestThreshold, out leftIdxs, out rightIdxs); - - // Split to the left - var xLeft = new Matrix(leftIdxs.Count, xTrain.NumberOfColumns); - var yLeft = new Vector(leftIdxs.Count); - for (int i = 0; i < leftIdxs.Count; i++) - { - yLeft[i] = yTrain[leftIdxs[i]]; - for (int j = 0; j < xTrain.NumberOfColumns; j++) - { - xLeft[i, j] = xTrain[leftIdxs[i], j]; - } - } - var left = GrowTree(xLeft, yLeft, depth + 1); - - // Split to the right - var xRight = new Matrix(rightIdxs.Count, xTrain.NumberOfColumns); - var yRight = new Vector(rightIdxs.Count); - for (int i = 0; i < rightIdxs.Count; i++) - { - yRight[i] = yTrain[rightIdxs[i]]; - for (int j = 0; j < xTrain.NumberOfColumns; j++) - { - xRight[i, j] = xTrain[rightIdxs[i], j]; - } - } - var right = GrowTree(xRight, yRight, depth + 1); + var left = GrowTree(indices, lo, split, depth + 1); + var right = GrowTree(indices, split, hi, depth + 1); // Return decision node return new DecisionNode() { FeatureIndex = bestIndex, Threshold = bestThreshold, Left = left, Right = right }; } /// - /// Creates a leaf node from the response values. + /// Counts the distinct classification labels within an index range. /// - /// The training vector of response values. + /// The training row indices. + /// The inclusive start of the range. + /// The exclusive end of the range. + /// The number of distinct labels, counting NaN labels as one label. + private int CountDistinctLabels(int[] indices, int lo, int hi) + { + var labels = new HashSet(); + for (int i = lo; i < hi; i++) + labels.Add(Y[indices[i]]); + return labels.Count; + } + + /// + /// Creates a leaf node from the responses within an index range. + /// + /// The training row indices. + /// The inclusive start of the range. + /// The exclusive end of the range. /// A leaf node holding the mean response for regression or the most common label for classification. - private DecisionNode CreateLeaf(Vector yTrain) + /// + /// The classification mode is the first label in sample order to reach the maximum count, so + /// ties resolve to the earliest observed label. + /// + private DecisionNode CreateLeaf(int[] indices, int lo, int hi) { if (IsRegression) { - // If regression return the average of Y - var avg = Tools.Mean(yTrain.ToArray()); - return new DecisionNode() { Value = avg, IsLeafNode = true }; + // The average of Y over the node, accumulated in sample order + double sum = 0; + for (int i = lo; i < hi; i++) + sum += Y[indices[i]]; + return new DecisionNode() { Value = sum / (hi - lo), IsLeafNode = true }; } else { - // If classification, return the most common value - var most = yTrain.ToList().GroupBy(i => i).OrderByDescending(grp => grp.Count()).Select(grp => grp.Key).First(); + // The most common label over the node + var counts = new Dictionary(); + for (int i = lo; i < hi; i++) + { + double v = Y[indices[i]]; + counts.TryGetValue(v, out int c); + counts[v] = c + 1; + } + double most = double.NaN; + int best = -1; + for (int i = lo; i < hi; i++) + { + double v = Y[indices[i]]; + if (counts[v] > best) + { + best = counts[v]; + most = v; + } + } return new DecisionNode() { Value = most, IsLeafNode = true }; } } /// - /// Returns the best split feature index and threshold. + /// Returns the best split feature index and threshold for an index range. /// - /// The matrix of predictor values. - /// The array of y-values. - /// The feature indexes to evaluate. - /// Output. The best feature index. + /// The training row indices. + /// The inclusive start of the node's range. + /// The exclusive end of the node's range. + /// The feature indexes to evaluate. + /// Output. The best feature index, or -1 when no candidate separates the node. /// Output. The best threshold for splitting the tree. /// - /// The parent impurity is constant within a node, so it is evaluated once and shared across - /// every candidate. Duplicate candidate thresholds produce identical splits, so only the - /// first occurrence of each value is evaluated; because ties break toward the earliest - /// candidate, the selected split is the same one an exhaustive scan selects. + /// + /// Each candidate feature is sorted once and every distinct value is evaluated as a + /// threshold in a single sweep that maintains running child statistics, so a node costs + /// O(features × m log m) rather than one full pass per candidate. Rows with a NaN predictor + /// fail every x <= t comparison and are held on the right side throughout the sweep. + /// + /// + /// Regression splits maximize the variance reduction, with child variances accumulated by + /// the Youngs and Cramer (1971) updating formula, the same recurrence used by + /// . + /// Classification splits maximize the information gain of the per-sample entropy, in which + /// each label's term is weighted by its own empirical probability. + /// + /// References: + /// + /// + /// Youngs, E. A. and Cramer, E. M. (1971). Some results relevant to choice of sum and sum-of-product algorithms. Technometrics, 13(3), 657-665. + /// + /// /// - private void BestSplit(Matrix xTrain, double[] yTrain, int[] indices, out int bestFeatureIndex, out double bestThreshold) + private void BestSplit(int[] indices, int lo, int hi, int[] featureIdxs, out int bestFeatureIndex, out double bestThreshold) { double best = double.MinValue; bestFeatureIndex = -1; bestThreshold = 0; - double parentImpurity = IsRegression ? Statistics.PopulationVariance(yTrain) : Entropy(yTrain); - var seen = new HashSet(); + var keys = _keyScratch; + var vals = _valScratch; - for (int i = 0; i < indices.Length; i++) + for (int f = 0; f < featureIdxs.Length; f++) { - var x = xTrain.Column(indices[i]); - seen.Clear(); - for (int j = 0; j < x.Length; j++) + int feature = featureIdxs[f]; + + // Gather the feature and response pairs, holding NaN predictors aside on the right + int valid = 0, nanCount = 0; + for (int i = lo; i < hi; i++) { - if (!seen.Add(x[j])) + double key = X[indices[i], feature]; + if (double.IsNaN(key)) { - continue; + nanCount++; } - // Test if the split variance reduction or information gain - double performance = IsRegression ? VarianceReduction(x, yTrain, x[j], parentImpurity) : InformationGain(x, yTrain, x[j], parentImpurity); - // Keep track of the best value - if (performance > best) + else { - best = performance; - bestFeatureIndex = indices[i]; - bestThreshold = x[j]; + keys[valid] = key; + vals[valid] = Y[indices[i]]; + valid++; } } - } + if (valid == 0) + continue; + Array.Sort(keys, vals, 0, valid); + + double performance; + double threshold; + if (IsRegression) + { + if (!SweepVariance(indices, lo, hi, feature, valid, nanCount, out performance, out threshold)) + continue; + } + else + { + if (!SweepEntropy(indices, lo, hi, feature, valid, nanCount, out performance, out threshold)) + continue; + } + + // Keep track of the best value + if (performance > best) + { + best = performance; + bestFeatureIndex = feature; + bestThreshold = threshold; + } + } } /// - /// Computes the variance reduction for the threshold. + /// Sweeps the sorted candidate thresholds of one feature and returns the best variance reduction. /// - /// The column of x-values. - /// The column of y-values. - /// The split threshold. - /// The population variance of the parent node response values. - /// The variance reduction of the split, or double.MinValue when the threshold places every sample in one child. - /// - /// The child variances are accumulated in sample order with the same recurrence as - /// , so - /// the reduction is identical to evaluating that method on materialized child arrays. - /// - private double VarianceReduction(double[] x, double[] y, double threshold, double parentVariance) + /// The training row indices. + /// The inclusive start of the node's range. + /// The exclusive end of the node's range. + /// The feature under evaluation. + /// The number of sorted non-NaN feature values in the scratch buffers. + /// The number of rows whose feature value is NaN, held on the right side. + /// Output. The largest variance reduction over the candidate thresholds. + /// Output. The threshold attaining . + /// True when at least one threshold produces two non-empty children. + private bool SweepVariance(int[] indices, int lo, int hi, int feature, int valid, int nanCount, out double bestGain, out double bestThreshold) { - // Stream both child variances in one pass over the sample - int countLeft = 0, countRight = 0; - double sumLeft = 0, sumRight = 0, accLeft = 0, accRight = 0; - for (int i = 0; i < x.Length; i++) + int m = hi - lo; + var keys = _keyScratch; + var vals = _valScratch; + + // Parent variance over the full node in sample order + double parentVariance = RangeVariance(indices, lo, hi); + + // Right-side running statistics from the top of the sort down, seeded with the NaN rows + var rightAcc = _rightAccScratch; + int rightSeed = 0; + double accR = 0, sumR = 0; + if (nanCount > 0) { - double v = y[i]; - if (x[i] <= threshold) + for (int i = lo; i < hi; i++) { - countLeft++; - if (countLeft == 1) + if (!double.IsNaN(X[indices[i], feature])) + continue; + rightSeed++; + double v = Y[indices[i]]; + if (rightSeed == 1) { - sumLeft = v; + sumR = v; } else { - sumLeft += v; - double diff = countLeft * v - sumLeft; - accLeft += diff * diff / (countLeft * (countLeft - 1.0)); + sumR += v; + double diff = rightSeed * v - sumR; + accR += diff * diff / (rightSeed * (rightSeed - 1.0)); } } + } + rightAcc[valid] = accR; + for (int i = valid - 1; i >= 0; i--) + { + int k = rightSeed + (valid - i); + double v = vals[i]; + if (k == 1) + { + sumR = v; + } else { - countRight++; - if (countRight == 1) - { - sumRight = v; - } - else - { - sumRight += v; - double diff = countRight * v - sumRight; - accRight += diff * diff / (countRight * (countRight - 1.0)); - } + sumR += v; + double diff = k * v - sumR; + accR += diff * diff / (k * (k - 1.0)); } + rightAcc[i] = accR; } - if (countLeft == 0 || countRight == 0) - return double.MinValue; - - // calculate the weighted average variance of the children - var n = (double)y.Length; - var nLeft = (double)countLeft; - var nRight = (double)countRight; - var varLeft = accLeft / countLeft; - var varRight = accRight / countRight; - var childVariance = nLeft / n * varLeft + nRight / n * varRight; + // Forward sweep: grow the left side one sorted value at a time and score every distinct + // boundary + bestGain = double.MinValue; + bestThreshold = 0; + bool found = false; + double n = m; + int countLeft = 0; + double sumLeft = 0, accLeft = 0; + for (int i = 0; i < valid; i++) + { + countLeft++; + double v = vals[i]; + if (countLeft == 1) + { + sumLeft = v; + } + else + { + sumLeft += v; + double diff = countLeft * v - sumLeft; + accLeft += diff * diff / (countLeft * (countLeft - 1.0)); + } - // Return variance reduction - return parentVariance - childVariance; + // A boundary exists after the final occurrence of each distinct value + if (i + 1 < valid && keys[i + 1] == keys[i]) + continue; + int countRight = m - countLeft; + if (countRight == 0) + break; + + double varLeft = accLeft / countLeft; + double varRight = rightAcc[i + 1] / countRight; + double childVariance = countLeft / n * varLeft + countRight / n * varRight; + double gain = parentVariance - childVariance; + if (gain > bestGain) + { + bestGain = gain; + bestThreshold = keys[i]; + found = true; + } + } + return found; } /// - /// Returns the information gain of the split threshold. + /// Sweeps the sorted candidate thresholds of one feature and returns the best information gain. /// - /// The column of x-values. - /// The column of y-values. - /// The split threshold. - /// The entropy of the parent node response values. - /// The information gain of the split, or double.MinValue when the threshold places every sample in one child. - /// - /// Label probabilities are the per-child label counts divided by the child size, and entropy - /// terms are accumulated in sample order, so each child entropy is identical to evaluating - /// on a materialized child array. - /// - private double InformationGain(double[] x, double[] y, double threshold, double parentEntropy) + /// The training row indices. + /// The inclusive start of the node's range. + /// The exclusive end of the node's range. + /// The feature under evaluation. + /// The number of sorted non-NaN feature values in the scratch buffers. + /// The number of rows whose feature value is NaN, held on the right side. + /// Output. The largest information gain over the candidate thresholds. + /// Output. The threshold attaining . + /// True when at least one threshold produces two non-empty children. + private bool SweepEntropy(int[] indices, int lo, int hi, int feature, int valid, int nanCount, out double bestGain, out double bestThreshold) { - // Count the labels on each side of the threshold - var leftCounts = new Dictionary(); + int m = hi - lo; + var keys = _keyScratch; + var vals = _valScratch; + + // Class counts for the full node; NaN labels carry zero probability and are excluded + // from the counts while remaining in the child sizes var rightCounts = new Dictionary(); - int countLeft = 0, countRight = 0; - for (int i = 0; i < x.Length; i++) + for (int i = lo; i < hi; i++) { - double v = y[i]; - if (x[i] <= threshold) - { - countLeft++; - if (!double.IsNaN(v)) - { - leftCounts.TryGetValue(v, out int c); - leftCounts[v] = c + 1; - } - } - else + double v = Y[indices[i]]; + if (!double.IsNaN(v)) { - countRight++; - if (!double.IsNaN(v)) - { - rightCounts.TryGetValue(v, out int c); - rightCounts[v] = c + 1; - } + rightCounts.TryGetValue(v, out int c); + rightCounts[v] = c + 1; } } + double parentEntropy = CountsEntropy(rightCounts, m); + var leftCounts = new Dictionary(); - if (countLeft == 0 || countRight == 0) - return double.MinValue; - - // calculate the weighted average entropy of children - double sumLeft = 0, sumRight = 0; - for (int i = 0; i < x.Length; i++) + bestGain = double.MinValue; + bestThreshold = 0; + bool found = false; + double n = m; + int countLeft = 0; + for (int i = 0; i < valid; i++) { - double v = y[i]; - if (double.IsNaN(v)) - continue; - if (x[i] <= threshold) + countLeft++; + double v = vals[i]; + if (!double.IsNaN(v)) { - double p = (double)leftCounts[v] / countLeft; - sumLeft += p * Math.Log(p); + leftCounts.TryGetValue(v, out int c); + leftCounts[v] = c + 1; + rightCounts[v] = rightCounts[v] - 1; } - else + + // A boundary exists after the final occurrence of each distinct value + if (i + 1 < valid && keys[i + 1] == keys[i]) + continue; + int countRight = m - countLeft; + if (countRight == 0) + break; + + double el = CountsEntropy(leftCounts, countLeft); + double er = CountsEntropy(rightCounts, countRight); + double childrenE = countLeft / n * el + countRight / n * er; + double gain = parentEntropy - childrenE; + if (gain > bestGain) { - double p = (double)rightCounts[v] / countRight; - sumRight += p * Math.Log(p); + bestGain = gain; + bestThreshold = keys[i]; + found = true; } } - - var n = (double)y.Length; - var nl = (double)countLeft; - var nr = (double)countRight; - var childrenE = nl / n * (-sumLeft) + nr / n * (-sumRight); - - return parentEntropy - childrenE; + return found; } /// - /// Computes the entropy for a vector of classification labels. + /// Computes the per-sample entropy of a group from its label counts. /// - /// The column of y-values. + /// The label counts of the group. + /// The group size, including any rows whose label is NaN. /// The entropy of the labels. /// - /// The empirical probability of each label is its count divided by the sample length, and - /// terms are accumulated in sample order. Labels of NaN carry an empirical probability of - /// zero and contribute nothing. + /// Each label contributes count × p × ln(p) with p equal to its count divided by the group + /// size, matching a per-sample accumulation in which every sample carries the empirical + /// probability of its own label. /// - private double Entropy(double[] y) + private static double CountsEntropy(Dictionary counts, int size) { - var counts = new Dictionary(); - for (int i = 0; i < y.Length; i++) - { - double v = y[i]; - if (!double.IsNaN(v)) - { - counts.TryGetValue(v, out int c); - counts[v] = c + 1; - } - } - double sum = 0; - for (int i = 0; i < y.Length; i++) + foreach (var pair in counts) { - double v = y[i]; - if (double.IsNaN(v)) + if (pair.Value <= 0) continue; - double p = (double)counts[v] / y.Length; - sum += p * Math.Log(p); + double p = (double)pair.Value / size; + sum += pair.Value * p * Math.Log(p); } return -sum; } /// - /// Splits the x-column based on the threshold. + /// Computes the population variance of the responses within an index range. /// - /// The column of x-values. - /// The split threshold. - /// Output. A list of left indexes. - /// Output. A list of right indexes. - private void Split(double[] x, double threshold, out List leftIndices, out List rightIndices) + /// The training row indices. + /// The inclusive start of the range. + /// The exclusive end of the range. + /// The population variance, accumulated in sample order with the same recurrence as + /// . + private double RangeVariance(int[] indices, int lo, int hi) { - leftIndices = new List(); - rightIndices = new List(); - for (int i = 0; i < x.Length; i++) + int count = 0; + double sum = 0, acc = 0; + for (int i = lo; i < hi; i++) { - if (x[i] <= threshold) + count++; + double v = Y[indices[i]]; + if (count == 1) { - leftIndices.Add(i); + sum = v; } else { - rightIndices.Add(i); + sum += v; + double diff = count * v - sum; + acc += diff * diff / (count * (count - 1.0)); } } + return count == 0 ? double.NaN : acc / count; + } + + /// + /// Partitions an index range in place around a threshold, preserving the relative order on + /// both sides. + /// + /// The training row indices. + /// The inclusive start of the range. + /// The exclusive end of the range. + /// The splitting feature. + /// The splitting threshold; rows with x <= t go left and all others, NaN included, go right. + /// The index of the first right-side element. + private int StablePartition(int[] indices, int lo, int hi, int feature, double threshold) + { + var scratch = _partitionScratch; + int left = lo, rightCount = 0; + for (int i = lo; i < hi; i++) + { + int row = indices[i]; + if (X[row, feature] <= threshold) + { + indices[left++] = row; + } + else + { + scratch[rightCount++] = row; + } + } + Array.Copy(scratch, 0, indices, left, rightCount); + return left; } /// @@ -504,6 +681,16 @@ private double TraverseTree(double[] x, DecisionNode node) return node.Right != null ? TraverseTree(x, node.Right) : node.Value; } + /// + /// Returns the prediction for a single row of predictors. + /// + /// The row of x-value predictors. + /// The leaf value the row traverses to. + internal double PredictRow(double[] x) + { + return TraverseTree(x, Root); + } + /// /// Returns the prediction from the Decision Tree. /// diff --git a/Numerics/Machine Learning/Supervised/RandomForest.cs b/Numerics/Machine Learning/Supervised/RandomForest.cs index 15ff9d93..6ac98304 100644 --- a/Numerics/Machine Learning/Supervised/RandomForest.cs +++ b/Numerics/Machine Learning/Supervised/RandomForest.cs @@ -155,6 +155,11 @@ public RandomForest(Matrix x, Vector y, int seed = -1) /// /// Train the Random Forest. /// + /// + /// Every tree draws its seed from the forest generator and trains on a bootstrap of row + /// indices into the shared training data, so a seeded forest is deterministic and holds one + /// copy of the training data plus one index array per tree. + /// public void Train() { IsTrained = false; @@ -165,39 +170,21 @@ public void Train() // Estimate trees in parallel Parallel.For(0, NumberOfTrees, idx => { - DecisionTrees[idx] = BootstrapDecisionTree(seeds[idx]); + var rnd = new MersenneTwister(seeds[idx]); + var sampleIdxs = rnd.NextIntegers(0, X.NumberOfRows, X.NumberOfRows); + DecisionTrees[idx] = new DecisionTree(X, Y, sampleIdxs, seeds[idx]) { MinimumSplitSize = MinimumSplitSize, MaxDepth = MaxDepth, Features = Features, IsRegression = IsRegression }; DecisionTrees[idx].Train(); }); IsTrained = true; } - /// - /// Returns a bootstrapped decision tree. - /// - /// Optional. The prng seed. If negative or zero, then the computer clock is used as a seed. - private DecisionTree BootstrapDecisionTree(int seed = -1) - { - var rnd = seed > 0 ? new MersenneTwister(seed) : new MersenneTwister(); - var idxs = rnd.NextIntegers(0, X.NumberOfRows, X.NumberOfRows); - var bootX = new Matrix(X.NumberOfRows, X.NumberOfColumns); - var bootY = new Vector(Y.Length); - for (int i = 0; i < X.NumberOfRows; i++) - { - for (int j = 0; j < X.NumberOfColumns; j++) - { - bootX[i, j] = X[idxs[i], j]; - } - bootY[i] = Y[idxs[i]]; - } - return new DecisionTree(bootX, bootY, seed) { MinimumSplitSize = MinimumSplitSize, MaxDepth = MaxDepth, Features = Features, IsRegression = IsRegression }; - } - /// /// Returns the prediction intervals in a 2D array with columns: lower, median, upper, mean. /// /// The 1D array of predictors. /// The confidence level; Default = 0.1, which will result in the 90% confidence intervals. + /// A 2D array with columns lower, median, upper, and mean; or null when the forest is untrained or the predictor column count differs from . public double[,]? Predict(double[] X, double alpha = 0.1) { return Predict(new Matrix(X), alpha); @@ -208,6 +195,7 @@ private DecisionTree BootstrapDecisionTree(int seed = -1) /// /// The 2D array of predictors. /// The confidence level; Default = 0.1, which will result in the 90% confidence intervals. + /// A 2D array with columns lower, median, upper, and mean; or null when the forest is untrained or the predictor column count differs from . public double[,]? Predict(double[,] X, double alpha = 0.1) { return Predict(new Matrix(X), alpha); @@ -218,22 +206,38 @@ private DecisionTree BootstrapDecisionTree(int seed = -1) /// /// The matrix of predictors. /// The confidence level; Default = 0.1, which will result in the 90% confidence intervals. + /// A 2D array with columns lower, median, upper, and mean; or null when the forest is untrained or the predictor column count differs from . public double[,]? Predict(Matrix X, double alpha = 0.1) { - if (!IsTrained) return null!; + if (!IsTrained || X.NumberOfColumns != Dimensions) return null!; var percentiles = new double[] { alpha / 2d, 0.5, 1d - alpha / 2d }; - var output = new double[X.NumberOfRows, 4]; // lower, median, upper, mean + int numberOfRows = X.NumberOfRows; + var output = new double[numberOfRows, 4]; // lower, median, upper, mean - var bootResults = new double[X.NumberOfRows, NumberOfTrees]; + // Each test row is extracted once and shared across every tree + var rows = new double[numberOfRows][]; + for (int i = 0; i < numberOfRows; i++) + rows[i] = X.Row(i); - // Bootstrap the predictions - Parallel.For(0, NumberOfTrees, idx => { bootResults.SetColumn(idx, DecisionTrees[idx].Predict(X)!); }); + // Bootstrap the predictions. Each tree fills its own row of the jagged buffer, so the + // parallel writes land on independent arrays. + var bootResults = new double[NumberOfTrees][]; + Parallel.For(0, NumberOfTrees, idx => + { + var tree = DecisionTrees[idx]; + var predictions = new double[numberOfRows]; + for (int i = 0; i < numberOfRows; i++) + predictions[i] = tree.PredictRow(rows[i]); + bootResults[idx] = predictions; + }); // Process results - Parallel.For(0, X.NumberOfRows, idx => + Parallel.For(0, numberOfRows, idx => { - var values = bootResults.GetRow(idx); + var values = new double[NumberOfTrees]; + for (int t = 0; t < NumberOfTrees; t++) + values[t] = bootResults[t][idx]; Array.Sort(values); // The mean is accumulated sequentially so the reduction is deterministic on every diff --git a/Test_Numerics/Machine Learning/Supervised/Test_TreeGoldens.cs b/Test_Numerics/Machine Learning/Supervised/Test_TreeGoldens.cs index 9cb8c6d3..c29120cb 100644 --- a/Test_Numerics/Machine Learning/Supervised/Test_TreeGoldens.cs +++ b/Test_Numerics/Machine Learning/Supervised/Test_TreeGoldens.cs @@ -64,8 +64,8 @@ public class Test_TreeGoldens private const string GoldenDecisionTreeRegression = "3;3FC999999999999A;FFF8000000000000;0|1;3FFDDFD5885D3133;FFF8000000000000;0|2;C0224C6867727FFA;FFF8000000000000;0|1;3FD4B4BDD79176E0;FFF8000000000000;0|1;BFC1FAE563F26DC6;FFF8000000000000;0|-1;FFF8000" + "000000000;3FFE70E9D51B4FE8;1|-1;FFF8000000000000;3FFAFC1F354F6D26;1|0;3FE55A754C325DA0;FFF8000000000000;0|2;C0377F1C86C95D57;FFF8000000000000;0|3;BFD999999999999A;FFF8000000000000;0|-1;FFF800000000000" + - "0;3FF6097D675EDEDE;1|-1;FFF8000000000000;3FF783F2FC0B7EB9;1|3;BFB999999999999A;FFF8000000000000;0|3;BFD3333333333333;FFF8000000000000;0|-1;FFF8000000000000;3FF191F8CB834BA4;1|-1;FFF8000000000000;3FF21" + - "14313A5FA25;1|-1;FFF8000000000000;3FEC945E61344D65;1|2;C0242E4EDCB1F22B;FFF8000000000000;0|-1;FFF8000000000000;3FFCB84BB1036D9E;1|-1;FFF8000000000000;3FF7579900ED65CA;1|0;3FF5C945663B9E3C;FFF800000000" + + "0;3FF6097D675EDEDE;1|-1;FFF8000000000000;3FF783F2FC0B7EB9;1|1;3FD59F72565EB166;FFF8000000000000;0|-1;FFF8000000000000;3FEC945E61344D65;1|2;C027FC9C1950B3F8;FFF8000000000000;0|-1;FFF8000000000000;3FF19" + + "1F8CB834BA4;1|-1;FFF8000000000000;3FF2114313A5FA25;1|2;C0242E4EDCB1F22B;FFF8000000000000;0|-1;FFF8000000000000;3FFCB84BB1036D9E;1|-1;FFF8000000000000;3FF7579900ED65CA;1|0;3FF5C945663B9E3C;FFF800000000" + "0000;0|0;BFEBC3CC2282E1C7;FFF8000000000000;0|2;C0152538A969C642;FFF8000000000000;0|-1;FFF8000000000000;BFAD91DA3290F6DE;1|-1;FFF8000000000000;BFEC109CE5FF4DD3;1|2;401F18C3867D073D;FFF8000000000000;0|2" + ";3FE39B2F25B26C23;FFF8000000000000;0|0;3FE2F58B8D3C6912;FFF8000000000000;0|2;C0178B75038D2847;FFF8000000000000;0|2;C01BAAA57214F052;FFF8000000000000;0|-1;FFF8000000000000;3FEC9B0D6771A93B;1|2;C017BEEF" + "9B2DEE50;FFF8000000000000;0|-1;FFF8000000000000;3FF139CDA6BC6CD8;1|-1;FFF8000000000000;3FF2F08888FB10D8;1|0;3FD72BDB5ADA1636;FFF8000000000000;0|3;BFD999999999999A;FFF8000000000000;0|-1;FFF800000000000" + @@ -76,9 +76,9 @@ public class Test_TreeGoldens "0000000000;3FF4E5408EBB6F2C;1|-1;FFF8000000000000;3FF50553CC85D517;1|0;3FEC775BA1666C9C;FFF8000000000000;0|2;BFBD9EFDDC8F3796;FFF8000000000000;0|1;BFE3E2AEBBBFB435;FFF8000000000000;0|-1;FFF80000000000" + "00;3FEFA9FEF1E306CB;1|3;0000000000000000;FFF8000000000000;0|1;3FECD0652D564F6D;FFF8000000000000;0|-1;FFF8000000000000;3FED346785F623B3;1|-1;FFF8000000000000;3FEC32F4CF4A558F;1|-1;FFF8000000000000;3FEA" + "BD94CB7E990D;1|3;BFE0000000000000;FFF8000000000000;0|-1;FFF8000000000000;3FEEFA1915FB94E1;1|-1;FFF8000000000000;3FF0E29AA4868799;1|2;BFC9413E63BDEA87;FFF8000000000000;0|-1;FFF8000000000000;3FF234D4EE8" + - "45793;1|-1;FFF8000000000000;3FF0CAB8ADDBF0D7;1|0;3FE709A2AB9112E2;FFF8000000000000;0|2;3FEC38085F750591;FFF8000000000000;0|-1;FFF8000000000000;BF91B26166A24B6F;1|1;BFD2C6E93309ED95;FFF8000000000000;0|" + - "-1;FFF8000000000000;3FE26A769100EEBD;1|1;3FE8C86BCF310028;FFF8000000000000;0|1;3FD57A9F58756202;FFF8000000000000;0|-1;FFF8000000000000;3FD12B552DD9D7F8;1|-1;FFF8000000000000;3FD3FCCC0E35F8B5;1|3;BFB99" + - "9999999999A;FFF8000000000000;0|1;3FEBCED2A65594B2;FFF8000000000000;0|-1;FFF8000000000000;3FD6ABCBA051EF8C;1|-1;FFF8000000000000;3FD8341733CE38B3;1|-1;FFF8000000000000;3FDABFE7007FACB3;1|1;BFE27203B33D" + + "45793;1|-1;FFF8000000000000;3FF0CAB8ADDBF0D7;1|0;3FE709A2AB9112E2;FFF8000000000000;0|0;BF9832873BC903EA;FFF8000000000000;0|-1;FFF8000000000000;BF91B26166A24B6F;1|1;BFD2C6E93309ED95;FFF8000000000000;0|" + + "-1;FFF8000000000000;3FE26A769100EEBD;1|1;3FE8C86BCF310028;FFF8000000000000;0|1;3FD57A9F58756202;FFF8000000000000;0|-1;FFF8000000000000;3FD12B552DD9D7F8;1|-1;FFF8000000000000;3FD3FCCC0E35F8B5;1|0;3FE1E" + + "175861400BE;FFF8000000000000;0|1;3FEBCED2A65594B2;FFF8000000000000;0|-1;FFF8000000000000;3FD6ABCBA051EF8C;1|-1;FFF8000000000000;3FD8341733CE38B3;1|-1;FFF8000000000000;3FDABFE7007FACB3;1|1;BFE27203B33D" + "DA81;FFF8000000000000;0|2;400A34426F663F4F;FFF8000000000000;0|-1;FFF8000000000000;3FF1C9D36DD0BDF3;1|-1;FFF8000000000000;3FF1266CEEC0BD75;1|2;400700EB915E0BCF;FFF8000000000000;0|0;3FEADC529147659A;FFF" + "8000000000000;0|3;BFC999999999999A;FFF8000000000000;0|-1;FFF8000000000000;3FEB5FB77B00FD2B;1|-1;FFF8000000000000;3FE9889E69E2F2A9;1|1;3FE62BD7DA4708D3;FFF8000000000000;0|-1;FFF8000000000000;3FED8E35DD" + "DD6B56;1|-1;FFF8000000000000;3FEE57C080E41615;1|0;3FEB3BAF1830F947;FFF8000000000000;0|2;400AB51FC770976D;FFF8000000000000;0|-1;FFF8000000000000;3FE337C563CCD90C;1|3;BFC999999999999A;FFF8000000000000;0" + @@ -92,10 +92,10 @@ public class Test_TreeGoldens "0000000;3FFC35E78864E8E3;1|-1;FFF8000000000000;3FFDC26FE4697691;1|2;40335A754B869129;FFF8000000000000;0|1;3FED58AC7EF9853D;FFF8000000000000;0|3;3FB999999999999A;FFF8000000000000;0|-1;FFF8000000000000;" + "3FD9B4A87E38EB03;1|-1;FFF8000000000000;3FEDE65BB45BBCBF;1|1;3FF1A7AE5796BFCB;FFF8000000000000;0|3;BFD999999999999A;FFF8000000000000;0|-1;FFF8000000000000;3FEE6531525D6FF3;1|-1;FFF8000000000000;3FF06E5" + "4B73C6CC3;1|-1;FFF8000000000000;3FF26FA5296FBAF9;1|-1;FFF8000000000000;3FFA7E4C6E988F03;1|2;40300B7293C645F5;FFF8000000000000;0|2;BFF87E737DD1112A;FFF8000000000000;0|0;3FDD3FAD2999567E;FFF800000000000" + - "0;0|1;400070F813CD6751;FFF8000000000000;0|-1;FFF8000000000000;3FF22E98742A9F3B;1|3;BFD999999999999A;FFF8000000000000;0|-1;FFF8000000000000;3FF6B5F20CCD42E9;1|-1;FFF8000000000000;3FF505018C7213C4;1|1;4" + - "00664822589A75E;FFF8000000000000;0|0;3FF04417F5143EA6;FFF8000000000000;0|3;BFD3333333333333;FFF8000000000000;0|-1;FFF8000000000000;3FFCF8C9A01C958D;1|2;C02457106629AA9E;FFF8000000000000;0|-1;FFF800000" + - "0000000;3FFF59FB714FF968;1|-1;FFF8000000000000;3FFE437B3CB74074;1|0;3FF28FDF60F91429;FFF8000000000000;0|-1;FFF8000000000000;3FFA609D4143ED15;1|-1;FFF8000000000000;3FFB2509E5311CAE;1|1;400837B71A5DFA31" + - ";FFF8000000000000;0|-1;FFF8000000000000;3FFFADFFC2986B71;1|-1;FFF8000000000000;4000E3C1DBD8028B;1|0;40010D15BBD4011F;FFF8000000000000;0|0;3FF6C5A9928B0786;FFF8000000000000;0|2;40194B488F3AF0ED;FFF8000" + + "0;0|1;400070F813CD6751;FFF8000000000000;0|-1;FFF8000000000000;3FF22E98742A9F3B;1|3;BFD999999999999A;FFF8000000000000;0|-1;FFF8000000000000;3FF6B5F20CCD42E9;1|-1;FFF8000000000000;3FF505018C7213C4;1|3;B" + + "FD999999999999A;FFF8000000000000;0|3;BFE3333333333333;FFF8000000000000;0|-1;FFF8000000000000;3FFFADFFC2986B71;1|-1;FFF8000000000000;4000E3C1DBD8028B;1|0;3FF04417F5143EA6;FFF8000000000000;0|1;40016F750" + + "D9E2341;FFF8000000000000;0|3;BFC999999999999A;FFF8000000000000;0|-1;FFF8000000000000;3FFF59FB714FF968;1|-1;FFF8000000000000;3FFE437B3CB74074;1|-1;FFF8000000000000;3FFCF8C9A01C958D;1|1;40024A627C76F3FD" + + ";FFF8000000000000;0|-1;FFF8000000000000;3FFB2509E5311CAE;1|-1;FFF8000000000000;3FFA609D4143ED15;1|2;402BBD055B899392;FFF8000000000000;0|0;3FF6C5A9928B0786;FFF8000000000000;0|2;40194B488F3AF0ED;FFF8000" + "000000000;0|1;40015A32BBE9FB87;FFF8000000000000;0|1;4000E40A2B27202A;FFF8000000000000;0|1;4000598180F4064D;FFF8000000000000;0|-1;FFF8000000000000;3FF11F538FCA0CFF;1|-1;FFF8000000000000;3FF14797EC70562" + "9;1|-1;FFF8000000000000;3FF0DEBCD98FA8D5;1|-1;FFF8000000000000;3FF2E3DA277E4931;1|-1;FFF8000000000000;3FE1991D4D42D3E5;1|2;40206E97A421070C;FFF8000000000000;0|2;400D431D3CD1C280;FFF8000000000000;0|0;3" + "FF7F8422E4DC631;FFF8000000000000;0|-1;FFF8000000000000;3FFBD4F819D79781;1|-1;FFF8000000000000;3FFE5BA1712962FB;1|-1;FFF8000000000000;3FF905B5007D5368;1|1;40072D4061191F8C;FFF8000000000000;0|-1;FFF8000" + @@ -133,30 +133,30 @@ public class Test_TreeGoldens "00000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|"; private const string GoldenForestRegressionPredictions = - "3FE9889E69E2F2A9|3FF6A4062C69EB56|3FFE5BA1712962FB|3FF669BEF2471996|3FD9B4A87E38EB03|3FF06E54B73C6CC3|3FF87DCDF6987831|3FED787B466FED2D|3FE337C563CCD90C|3FEE57C080E41615|3FF87DCDF6987831|3FEF3C65183B9" + - "5D8|3FDD7B2691E51A4C|3FEB5FB77B00FD2B|3FF275749C8A0EDC|3FED8BCF8A3EA284|3FEC945E61344D65|3FF783F2FC0B7EB9|3FFE70E9D51B4FE8|3FF5E276A85D96D5|BFE81A1D312D0D6E|BF9AE56B5436E3ED|3FEC0EACE14A0760|BF8A9A106" + - "7774B3B|BFAD91DA3290F6DE|3FE519ABF896D519|3FF50553CC85D517|3FE50E478D0CE481|BFE93FACC9C47C00|3FFA7E4C6E988F03|3FFDC26FE4697691|3FF18499CE91ABDF|BFEC109CE5FF4DD3|3FEC945E61344D65|3FFE70E9D51B4FE8|3FEC9" + - "74FC2621C16|3FD9B4A87E38EB03|3FFA7E4C6E988F03|40028BDE18CF84B8|3FF6937CD109372E|3FD12B552DD9D7F8|3FE8A01C14B39645|3FF4C84988094E5D|3FE5474DEABF5D04|3FDE0A4A5BC3CB1B|3FF191F8CB834BA4|3FF7B05B98821745|3" + - "FF137B5668FE832|BF9AE56B5436E3ED|3FD9C469DC929294|3FED3ECCE9FE0B08|3FE03D532E1BB86F|BFC3B39C0EBEDFA4|3FDA36D945811391|3FEE57C080E41615|3FDCD0BD403D8C18|BF9AE56B5436E3ED|3FEC0EACE14A0760|3FFDC26FE46976" + - "91|3FEA7750859B0944|3FEC0EACE14A0760|3FF7EF9ACD4B4CDA|3FFDC26FE4697691|3FF5298D228CA27E|3FE467E93D9BC1E8|3FF22E98742A9F3B|3FF7579900ED65CA|3FF19F0F39740BBE|3FE6687B99D451FC|3FF4C84988094E5D|3FF7579900" + - "ED65CA|3FF218C2768CC36F|BFA55EF0A645CF6D|3FDD7B2691E51A4C|3FE7608F6C9A0728|3FD8C3E144374170|3FE6687B99D451FC|3FF139CDA6BC6CD8|3FFCB84BB1036D9E|3FF23D057EA77524|3FC9A80BE0918367|3FE87EB2B87983A5|3FF6A4" + - "062C69EB56|3FEA3C2E0B5DAE88|BFAD91DA3290F6DE|3FF6097D675EDEDE|3FFE70E9D51B4FE8|3FF42ED149C1376E|3FF191F8CB834BA4|3FF7579900ED65CA|3FFF59FB714FF968|3FF80C0D04C61905|3FE439A4DDABA879|3FEFA9FEF1E306CB|3F" + - "FCF0AE63802D0C|3FF12D4008BECB6D|BFA55EF0A645CF6D|3FD06B925501BA14|3FE02FE3E89D37DF|3FCA42D289F5EE8A|3FD9B4A87E38EB03|3FFA7E4C6E988F03|40028BDE18CF84B8|3FF749F1C483CB4E|3FEC945E61344D65|3FF191F8CB834BA" + - "4|3FF783F2FC0B7EB9|3FF1D76D2BC2F57B|3FEC945E61344D65|3FF2114313A5FA25|3FFCF0AE63802D0C|3FF39AE1A59586EC|3FDCF0D22FE31C9A|3FEB3A04B8F757E6|3FF1D21DEA657DC5|3FEB6F37EE1B6285|BFA55EF0A645CF6D|3FDABFE7007" + - "FACB3|3FE337C563CCD90C|3FD0598A1E4782F9|3FDF006245C55C15|3FEC9B0D6771A93B|3FF4C84988094E5D|3FEE1B53358BEFB6|3FDF006245C55C15|3FEC9B0D6771A93B|3FFAFC1F354F6D26|3FEF383A93FAF60F|BFD61A3738D157CC|3FD6790" + - "13DEDA159|3FE0CA91BED9C503|3FC5153FBFABFA86|BFD2AA19EF6A5B93|BF9AE56B5436E3ED|3FEDE65BB45BBCBF|3FAC52E825050225|BFE93FACC9C47C00|3FEDE65BB45BBCBF|3FFDC26FE4697691|3FEB81A844AF3E1E|C0023160AEEC671C|3FD" + - "679013DEDA159|3FFE70E9D51B4FE8|3FA5DAC759D9DD1A|C0023160AEEC671C|BFE8178FD41DF9E6|3FE3B628BBB5F921|BFE7E0701172873E|C0023160AEEC671C|BFD61A3738D157CC|3FEAA1DABB77E5C0|BFE266A61E705704|BFF79CE9829012B2" + - "|BFA55EF0A645CF6D|3FE3B628BBB5F921|BFD5CA89BCE25E56|BFAD91DA3290F6DE|3FF2114313A5FA25|3FFCF0AE63802D0C|3FEEA3922738EEC3|BFD18D3B7CD8C3A6|BF91B26166A24B6F|3FF4C84988094E5D|3FD08D9ED3641B99|3FDF006245C5" + - "5C15|3FF2401805DB3647|3FFE437B3CB74074|3FF26244B5D39531|3FCAE6AAAEEAB99D|3FE1991D4D42D3E5|3FF2E3DA277E4931|3FE3E43274C80241|3FDEE60177E07284|3FEABD94CB7E990D|3FF0E29AA4868799|3FE9EFBCD97D7338|3FE6687B" + - "99D451FC|3FF234D4EE845793|3FFC3C6C8A6CD5E6|3FF20B5B5D553A1C|3F9012147F93DEF0|3FD06B925501BA14|3FF1266CEEC0BD75|3FD8F42F692488CE|BF91B26166A24B6F|3FE0CA91BED9C503|3FF3081EFC222D6D|3FE0E4CA7315F855|3FD3" + - "FCCC0E35F8B5|3FD8341733CE38B3|3FEB5FB77B00FD2B|3FDE9CD912B28A9C|BF91B26166A24B6F|3FE467E93D9BC1E8|3FF2F08888FB10D8|3FE53B51273F2834|3FD9B4A87E38EB03|3FF06E54B73C6CC3|3FF87DCDF6987831|3FEF0FDB42F34DB2|" + - "BFA55EF0A645CF6D|BFA55EF0A645CF6D|3FE1991D4D42D3E5|3FB6D5D221D1DE12|BFAD91DA3290F6DE|3FE19A8427ED59E0|3FF2F08888FB10D8|3FE034365CB56F1A|3FD9B4A87E38EB03|3FFA7E4C6E988F03|40028BDE18CF84B8|3FF6697794218" + - "FF3|BFEC109CE5FF4DD3|3FF6097D675EDEDD|3FFB1254A1E539E3|3FE5822ED2268439|BFC3B39C0EBEDFA4|BFA55EF0A645CF6D|3FE02FE3E89D37DF|3FB9464AC95B34EA|3FD12B552DD9D7F8|3FDABFE7007FACB3|3FEB5FB77B00FD2B|3FDD2421E" + - "869D7D0|3FE6687B99D451FC|3FF2114313A5FA25|3FFB0942086BBCE3|3FF2875F2B0EF177|BFD2AA19EF6A5B93|3FCDDEE7AC7716E2|3FE87EB2B87983A5|3FC54F0130A8D3B1|BFA55EF0A645CF6D|3FE1991D4D42D3E5|3FF6A4062C69EB56|3FE59" + - "401013DDFE3|3FE3B628BBB5F921|3FED8E35DDDD6B56|3FF234D4EE845793|3FED869191872C8D|3FEC32F4CF4A558F|3FF234D4EE845793|3FFC35E78864E8E3|3FF42F9B0CEBE0B8|3FDEF41446DEE1A3|3FEC32F4CF4A558F|3FF2130A5E977ECF|3" + - "FEBA94A918F8568|3FDC1F953BE6E0F5|3FEAFA34DF85B70F|3FF1C9D36DD0BDF3|3FE8B98EFC8F8CCA|3FDABFE7007FACB3|3FEB3A04B8F757E5|3FEE57C080E41615|3FE74D9A68F6056E|3FD12B552DD9D7F8|3FE26A769100EEBD|3FF1C9D36DD0BD" + - "F3|3FE3B0CD989CC01F|3FD12B552DD9D7F8|3FD8341733CE38B3|3FEFA9FEF1E306CB|3FE15BD80F536093|3FE8B5669433A925|3FF0E29AA4868799|3FFC02D7B385A8F7|3FF18F7394501A7B|3FE6687B99D451FC|3FEABD94CB7E990D|3FF139CDA6" + - "BC6CD8|3FEBBE06D4815EBF|"; + "3FE9889E69E2F2A9|3FF6A4062C69EB56|3FFE5BA1712962FB|3FF66C6AF8CD8102|3FD9B4A87E38EB03|3FF06E54B73C6CC3|3FF87DCDF6987831|3FEDDB7B6D94F1B0|3FE337C563CCD90C|3FEE57C080E41615|3FF6A4062C69EB56|3FEF302658EA5" + + "D03|3FE02FE3E89D37DF|3FEB5FB77B00FD2B|3FF2E3DA277E4931|3FEDA384BC779E3D|3FEC945E61344D65|3FF783F2FC0B7EB9|3FFE70E9D51B4FE8|3FF5F4B0137E6E30|BFE93FACC9C47C00|BF9AE56B5436E3ED|3FEC0EACE14A0760|BF89728A0" + + "2423944|BFAD91DA3290F6DE|3FE467E93D9BC1E8|3FF50553CC85D517|3FE51E693E87FB36|BFF79CE9829012B2|3FFA7E4C6E988F03|3FFDC26FE4697691|3FF181030F3EB87B|BFEC109CE5FF4DD3|3FEC945E61344D65|3FFE70E9D51B4FE8|3FECB" + + "5B73CB6A25B|3FD9B4A87E38EB03|3FFA7E4C6E988F03|40028BDE18CF84B8|3FF6746FD63843DD|3FD12B552DD9D7F8|3FE8A01C14B39645|3FF4C84988094E5D|3FE5499EBFAD594E|3FDE0A4A5BC3CB1B|3FF191F8CB834BA4|3FF7B05B98821745|3" + + "FF143BC73D5621A|BF9AE56B5436E3ED|3FD8341733CE38B3|3FED3ECCE9FE0B08|3FE0585E05480136|BFC3B39C0EBEDFA4|3FDA36D945811391|3FEE57C080E41615|3FDCDFB9B5DBD42F|BF9AE56B5436E3ED|3FEC0EACE14A0760|3FFDC26FE46976" + + "91|3FEA961F39EE1697|3FEC0EACE14A0760|3FF7EF9ACD4B4CDA|3FFDC26FE4697691|3FF54424263CB7E1|3FE467E93D9BC1E8|3FF2114313A5FA25|3FF759D0B3E21A3B|3FF1A09B778359A3|3FE6687B99D451FC|3FF4C84988094E5D|3FF7579900" + + "ED65CA|3FF22C2C5F8055AB|BFA55EF0A645CF6D|3FDD78F9158019EC|3FE7608F6C9A0728|3FD8B4C24308CEDC|3FE6687B99D451FC|3FF139CDA6BC6CD8|3FFCB84BB1036D9D|3FF23C7E9E8FA422|3F9012147F93DEF0|3FE87EB2B87983A5|3FF524" + + "5E809C7B14|3FEA2768080EB0A2|BFAD91DA3290F6DE|3FF6097D675EDEDE|3FFE70E9D51B4FE8|3FF424C2D34B5749|3FF191F8CB834BA4|3FF7579900ED65CA|3FFF59FB714FF968|3FF8057BAF383AEA|3FE0CA91BED9C503|3FEFA9FEF1E306CB|3F" + + "FCF0AE63802D0C|3FF1239DFD853D19|BFA55EF0A645CF6D|3FD06B925501BA14|3FE041F3940BE600|3FCA32F6E36FD317|3FD9B4A87E38EB03|3FFA7E4C6E988F03|40028BDE18CF84B8|3FF725913DF045B4|3FEC945E61344D65|3FF191F8CB834BA" + + "4|3FF783F2FC0B7EB9|3FF1D7E4CAD10F3E|3FEC945E61344D65|3FF191F8CB834BA4|3FFCF0AE63802D0C|3FF372CB4F37605A|3FDCF0D22FE31C9A|3FEB3A04B8F757E6|3FF1C9D36DD0BDF3|3FEB2CD5599B4A36|BFA55EF0A645CF6D|3FDABFE7007" + + "FACB3|3FE5DF970EFAC4AD|3FD0920CDE5AF7D9|3FDF006245C55C15|3FEC9B0D6771A93B|3FF4C84988094E5D|3FEE478B5354EE6A|3FDF006245C55C15|3FEC9B0D6771A93B|3FFAFC1F354F6D26|3FEF5827C1604009|BFD61A3738D157CC|3FD6790" + + "13DEDA159|3FE0CA91BED9C503|3FC4A026B532BE9C|BFD2AA19EF6A5B93|BF9AE56B5436E3ED|3FEB5C3C4589299B|3FA9F319895C3ED3|BFF79CE9829012B2|3FEDE65BB45BBCBF|3FFDC26FE4697691|3FEB95D9B1B21E68|C0023160AEEC671C|3FD" + + "679013DEDA159|3FFE70E9D51B4FE8|3FB2EA6408866B76|C0023160AEEC671C|BFE8178FD41DF9E6|3FE3B628BBB5F921|BFE7DEE895252165|C0023160AEEC671C|BFD61A3738D157CC|3FEAA1DABB77E5C0|BFE23188FF686163|BFF79CE9829012B2" + + "|BFA55EF0A645CF6D|3FE3B628BBB5F921|BFD59CE6C235AA41|BFAD91DA3290F6DE|3FF2114313A5FA25|3FFCF0AE63802D0C|3FEEA7C82C0B9C0B|BFD18D3B7CD8C3A6|BF91B26166A24B6F|3FF4C84988094E5D|3FD05DCB67FA67B6|3FDF006245C5" + + "5C15|3FF2401805DB3647|3FFE437B3CB74074|3FF2751FEDCB3AE0|3FCAE6AAAEEAB99D|3FE0E4809AF005E2|3FF5102D41AC9DAD|3FE413E1CC38AA67|3FDF006245C55C15|3FEABD94CB7E990D|3FF0E29AA4868799|3FE9DDDA6607DD13|3FE6687B" + + "99D451FC|3FF234D4EE845793|3FFC35E78864E8E3|3FF2029C95B1DF7C|3F9012147F93DEF0|3FD06B925501BA14|3FF06E54B73C6CC3|3FD8D4F4961C65F8|BF91B26166A24B6F|3FE0CA91BED9C503|3FF2F08888FB10D8|3FE0B50B7B5F4D20|3FD3" + + "FCCC0E35F8B5|3FD8341733CE38B3|3FEB5FB77B00FD2B|3FDEA36F0A438CC8|BFAD91DA3290F6DD|3FE467E93D9BC1E8|3FF2F08888FB10D8|3FE5180D5B233792|3FD9B4A87E38EB03|3FF06E54B73C6CC3|3FF87DCDF6987831|3FEEFE746178AE0F|" + + "BFA83E1D6DB54B34|BFA55EF0A645CF6D|3FE1991D4D42D3E5|3FB6D4D85B6BA189|BFAD91DA3290F6DE|3FE0CA91BED9C503|3FF30991BC77E243|3FE0059CC0B47378|3FD9B4A87E38EB03|3FFA7E4C6E988F03|40028BDE18CF84B8|3FF64A1A4A733" + + "1D3|BFEC109CE5FF4DD3|3FF6097D675EDEDD|3FFCB84BB1036D9E|3FE5ACB0B4F53209|BFC3B39C0EBEDFA4|BFA55EF0A645CF6D|3FE02FE3E89D37DF|3FB938371D8961DB|3FD12B552DD9D7F8|3FDABFE7007FACB3|3FEB5FB77B00FD2B|3FDD3C0F7" + + "05CF5D8|3FE90E2EA1A907A1|3FF2114313A5FA25|3FF783F2FC0B7EB9|3FF27F31B4DE0C89|BFD2AA19EF6A5B93|3FCAE6AAAEEAB99D|3FE87EB2B87983A5|3FC5C5962448A499|BFA55EF0A645CF6D|3FE1991D4D42D3E5|3FF6A4062C69EB56|3FE5C" + + "EC38441F82B|3FE3AFD6F750B787|3FED8E35DDDD6B56|3FF234D4EE845793|3FED79CD94851F56|3FEC32F4CF4A558F|3FF234D4EE845793|3FFCBB85302B1600|3FF43AE22B1F67C6|3FDEF41446DEE1A3|3FEC32F4CF4A558F|3FF1C9D36DD0BDF3|3" + + "FEBA92E0C12C238|3FDC1F953BE6E0F5|3FEAFA34DF85B70F|3FF1C9D36DD0BDF3|3FE8947EFF033CE5|3FDABFE7007FACB3|3FEB3A04B8F757E5|3FED8E35DDDD6B56|3FE7407D099E3FDC|3FD3FCCC0E35F8B5|3FE26A769100EEBD|3FF1C9D36DD0BD" + + "F3|3FE3D6CFFEC4BA85|3FD12B552DD9D7F8|3FD8341733CE38B3|3FEFA9FEF1E306CB|3FE16D541618775D|3FE8B5669433A925|3FF0E29AA4868799|3FFC02D7B385A8F7|3FF1A23236F48A04|3FE6687B99D451FC|3FEABD94CB7E990D|3FF0E29AA4" + + "868799|3FEBB9DF55C2AAFB|"; private static readonly double[] OracleX0 = { 4.744781, 6.255026, 1.160139, 8.741791, 0.319224, 1.201623, 7.703759, 8.249999, 7.763458, 2.561846, 7.844684, 9.530505, 5.237435, 7.938164, 7.634402, 3.538497, 3.908757, 0.798954, 4.233405, 4.819062, 4.704432, 8.131584, 7.138561, 5.718968, 0.230697, 8.428248, 2.642866, 0.617403, 9.190608, 5.605545, 9.120073, 5.506271, 1.210414, 7.754063, 6.823279, 8.004035, 7.097406, 5.353946, 3.109105, 1.01253 }; private static readonly double[] OracleX1 = { 6.041412, 1.540177, 0.705583, 2.134676, 3.987951, 8.706857, 4.997143, 7.688693, 7.269118, 9.894784, 2.850013, 2.908263, 4.640485, 9.239211, 2.437277, 9.911633, 5.759533, 1.255405, 2.324209, 0.226532, 2.977218, 1.713005, 4.749645, 2.982202, 8.679028, 2.531092, 8.802673, 1.569402, 4.644899, 2.859959, 8.467362, 0.291196, 5.153139, 1.003399, 3.90648, 6.822431, 9.421524, 6.81047, 2.76754, 4.48085 }; From d7797b9a702cb2fa61977a10ab922b42f9fed1bc Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 26 Aug 2026 13:34:35 -0600 Subject: [PATCH 105/222] Index the standard deviation recurrence by the observed count so missing values drop out --- Numerics/Data/Time Series/TimeSeries.cs | 8 ++++--- .../Data/Time Series/Test_TimeSeries.cs | 21 +++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/Numerics/Data/Time Series/TimeSeries.cs b/Numerics/Data/Time Series/TimeSeries.cs index d6e9c4d9..c3bdf29c 100644 --- a/Numerics/Data/Time Series/TimeSeries.cs +++ b/Numerics/Data/Time Series/TimeSeries.cs @@ -1402,15 +1402,17 @@ public double StandardDeviation() break; } } + // The updating formula indexes by the running count of observed values, so missing + // values leave the accumulation untouched. double n = 1; for (int i = startIdx; i < Count; i++) { if (!double.IsNaN(this[i].Value)) { - t += this[i].Value; - double diff = (i + 1) * this[i].Value - t; - variance += diff * diff / ((i + 1.0d) * i); n += 1; + t += this[i].Value; + double diff = n * this[i].Value - t; + variance += diff * diff / (n * (n - 1d)); } } return Math.Sqrt(variance / (n - 1)); diff --git a/Test_Numerics/Data/Time Series/Test_TimeSeries.cs b/Test_Numerics/Data/Time Series/Test_TimeSeries.cs index b7a2c519..3a24a7d5 100644 --- a/Test_Numerics/Data/Time Series/Test_TimeSeries.cs +++ b/Test_Numerics/Data/Time Series/Test_TimeSeries.cs @@ -766,6 +766,27 @@ public void Test_ConvertTimeInterval_1hr_to_1Day_Sum() Assert.AreEqual(trueValues[i], newTS[i].Value, 1E-2); } + /// + /// The standard deviation of a series with missing values must equal the sample standard + /// deviation of its observed values. + /// + /// + /// Reference values verified against Python pandas 2.3.3 (Series.std(), which skips NaN) + /// and exact rational arithmetic. The observed values of { 1, NaN, 2, 3 } have sample + /// standard deviation exactly 1, and those of { NaN, 3.1, NaN, 4.7, 2.2, NaN, 5.9, 1.4 } + /// have 1.8338484124921557, with pandas within two units in the last place of the exact + /// rational value. + /// + [TestMethod] + public void Test_StandardDeviation_WithMissingValues() + { + var ts = new TimeSeries(TimeInterval.OneDay, new DateTime(2024, 01, 01), new double[] { 1, double.NaN, 2, 3 }); + Assert.AreEqual(1.0, ts.StandardDeviation(), 1E-14); + + var gappy = new TimeSeries(TimeInterval.OneDay, new DateTime(2024, 01, 01), new double[] { double.NaN, 3.1, double.NaN, 4.7, 2.2, double.NaN, 5.9, 1.4 }); + Assert.AreEqual(1.8338484124921557, gappy.StandardDeviation(), 1E-14); + } + /// /// Test the statistics methods of the TimeSeries class /// From 3526b00eb188585b1b42c26bb5828de40d79681d Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 26 Aug 2026 13:57:23 -0600 Subject: [PATCH 106/222] Guard the min helpers with the infinity sentinel and NaN-aware index selection --- Numerics/Utilities/Tools.cs | 21 ++++++++++++--------- Test_Numerics/Utilities/Test_Tools.cs | 22 ++++++++++++++++++++++ 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/Numerics/Utilities/Tools.cs b/Numerics/Utilities/Tools.cs index 0b6215d5..41187b54 100644 --- a/Numerics/Utilities/Tools.cs +++ b/Numerics/Utilities/Tools.cs @@ -521,7 +521,7 @@ public static double Product(IList values, IList indicators, bool u /// Output. Maximum value. public static void MinMax(IList values, out double min, out double max) { - min = double.MaxValue; + min = double.PositiveInfinity; max = double.NegativeInfinity; if (values.Count == 0) { min = double.NaN; max = double.NaN; return; }; for (int i = 0; i < values.Count; i++) @@ -535,16 +535,17 @@ public static void MinMax(IList values, out double min, out double max) } /// - /// Returns the index of the minimum value. + /// Returns the index of the minimum value. NaN entries are ignored. /// /// The list of values. + /// The zero-based index of the smallest value, or -1 if the list is empty or contains only NaN. public static int ArgMin(IList values) { - double min = double.MaxValue; + double min = double.PositiveInfinity; int index = -1; for (int i = 0; i < values.Count; i++) { - if (values[i] < min) + if (!double.IsNaN(values[i]) && (index == -1 || values[i] < min)) { min = values[i]; index = i; @@ -554,9 +555,10 @@ public static int ArgMin(IList values) } /// - /// Returns the index of the maximum value. + /// Returns the index of the maximum value. NaN entries are ignored. /// /// The list of values. + /// The zero-based index of the largest value, or -1 if the list is empty or contains only NaN. public static int ArgMax(IList values) { double max = double.NegativeInfinity; @@ -580,7 +582,7 @@ public static int ArgMax(IList values) public static double Min(IList values) { if (values.Count == 0) return double.NaN; - double min = double.MaxValue; + double min = double.PositiveInfinity; for (int i = 0; i < values.Count; i++) { double v = values[i]; @@ -591,8 +593,9 @@ public static double Min(IList values) } /// - /// Returns the smallest value from a list of values. - /// Returns NaN if the list is empty or any entry is NaN. + /// Returns the smallest indicated value from a list of values. + /// Returns NaN if the list is empty, the indicator list length differs, no entry is + /// indicated, or any indicated entry is NaN. /// /// The list of values. /// The list of indicators (0's or 1's). @@ -601,7 +604,7 @@ public static double Min(IList values, IList indicators, bool useCo { if (values.Count == 0) return double.NaN; if (indicators.Count != values.Count) return double.NaN; - double min = double.MaxValue; + double min = double.PositiveInfinity; bool any = false; int flag = useComplement ? 0 : 1; for (int i = 0; i < values.Count; i++) diff --git a/Test_Numerics/Utilities/Test_Tools.cs b/Test_Numerics/Utilities/Test_Tools.cs index 59dc4a33..5dfbcb8c 100644 --- a/Test_Numerics/Utilities/Test_Tools.cs +++ b/Test_Numerics/Utilities/Test_Tools.cs @@ -506,6 +506,28 @@ public void Test_LogSumExp() Assert.AreEqual(1000.70815,result2, 1E-04); } + /// + /// Testing min helpers when every candidate is positive infinity. + /// + /// + /// Reference behavior verified against Python numpy 2.4.2: np.min([inf, inf]) returns inf + /// and np.argmin([inf, inf]) returns 0. + /// + [TestMethod] + public void Test_MinHelpers_AllPositiveInfinity() + { + List values = new List { double.PositiveInfinity, double.PositiveInfinity }; + List indicators = new List { 1, 1 }; + + Tools.MinMax(values, out double min, out double max); + + Assert.AreEqual(double.PositiveInfinity, min); + Assert.AreEqual(double.PositiveInfinity, max); + Assert.AreEqual(0, Tools.ArgMin(values)); + Assert.AreEqual(double.PositiveInfinity, Tools.Min(values)); + Assert.AreEqual(double.PositiveInfinity, Tools.Min(values, indicators)); + } + /// /// Testing max helpers when every candidate is negative infinity. /// From 4aab2e9472dce4676d3a967a263b925a8be810d8 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 26 Aug 2026 14:17:19 -0600 Subject: [PATCH 107/222] Accumulate the product moment power sums about a shifted origin --- Numerics/Data/Statistics/Statistics.cs | 22 ++++++---- .../Data/Statistics/Test_Statistics.cs | 41 +++++++++++++++++++ 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/Numerics/Data/Statistics/Statistics.cs b/Numerics/Data/Statistics/Statistics.cs index 60c5d81b..85b8f70f 100644 --- a/Numerics/Data/Statistics/Statistics.cs +++ b/Numerics/Data/Statistics/Statistics.cs @@ -468,7 +468,8 @@ public static double PopulationCovariance(IList data1, IList dat } /// - /// Returns the first four product moments of a sample {Mean, Standard Deviation, Skew, and Kurtosis}, or returns NaN if data is empty or any entry is NaN. + /// Returns the first four product moments of a sample {Mean, Standard Deviation, bias-corrected Skew, and bias-corrected Excess Kurtosis}, + /// or four NaN values when the sample has fewer than four entries or any entry is NaN. /// /// Sample of data, no sorting is assumed. public static double[] ProductMoments(IList data) @@ -477,15 +478,20 @@ public static double[] ProductMoments(IList data) double N = data.Count; if (N < 4) return [double.NaN, double.NaN, double.NaN, double.NaN]; - // sums of powers + // Sums of powers accumulated about the first value. The shift keeps the higher moments + // conditioned on the sample spread rather than on the distance from zero, so the + // central moments below are not dominated by cancellation when the mean is large + // relative to the spread. + double shift = data[0]; double X1 = 0, X2 = 0, X3 = 0, X4 = 0; foreach (var x in data) { - double x2 = x * x; - X1 += x; - X2 += x2; - X3 += x2 * x; - X4 += x2 * x2; + double y = x - shift; + double y2 = y * y; + X1 += y; + X2 += y2; + X3 += y2 * y; + X4 += y2 * y2; } // raw moments @@ -516,7 +522,7 @@ public static double[] ProductMoments(IList data) // bias-corrected excess kurtosis double K = ((N * N) * (N + 1)) / ((N - 1) * (N - 2) * (N - 3)) * (c4 / S4) - 3d * (N - 1) * (N - 1) / ((N - 2) * (N - 3)); - return [U1, S, G, K]; + return [shift + U1, S, G, K]; } diff --git a/Test_Numerics/Data/Statistics/Test_Statistics.cs b/Test_Numerics/Data/Statistics/Test_Statistics.cs index 6924e67d..8f74b874 100644 --- a/Test_Numerics/Data/Statistics/Test_Statistics.cs +++ b/Test_Numerics/Data/Statistics/Test_Statistics.cs @@ -313,6 +313,47 @@ public void Test_ComputeProductMoments() Assert.AreEqual(trueVal4, vals[3], 1E-10); } + /// + /// The standard deviation, skew, and kurtosis of a sample recorded against a large datum + /// must match those of the datum-removed sample: all three are location invariant, while + /// the mean carries the datum. + /// + /// + /// + /// Reference values computed with exact rational arithmetic (Python 3, fractions.Fraction) + /// on the IEEE doubles of the fixture, and cross-checked against Python scipy 1.17.1: + /// scipy.stats.skew(x, bias=False) = -1.0937835136996568 and + /// scipy.stats.kurtosis(x, bias=False) = 2.247619447041167, each agreeing with the exact + /// values below to within its own double-precision rounding. + /// + /// + /// Measured accuracy: on this fixture the accumulation reproduces the exact skew to 4E-16 + /// and the exact excess kurtosis to 3E-15 relative error. The assertion deltas of 1E-12 + /// and 1E-11 bound the rounding of the 13 to 15 significant-digit expected literals with + /// two to three orders of margin. + /// + /// + [TestMethod] + public void Test_ProductMoments_LocationInvariance() + { + var data = new double[] { 4999.873847, 5000.184353, 5001.217652, 5000.3752, 4999.791099, 5001.044619, 5000.663964, 4999.879402, 5001.277702, 5000.04844, 5001.799453, 4999.831996, 5000.908146, 5000.413566, 5001.525885, 5001.944514, 4997.601044, 4999.544628, 5001.555952, 5001.450008 }; + var moments = Numerics.Data.Statistics.Statistics.ProductMoments(data); + + Assert.AreEqual(5000.5465735, moments[0], 1E-6); + Assert.AreEqual(1.0203450178184, moments[1], 1E-12); + Assert.AreEqual(-1.0937835137001, moments[2], 1E-11); + Assert.AreEqual(2.24761944704189, moments[3], 1E-11); + + // Location invariance against the same sample with the datum removed + var centered = new double[data.Length]; + for (int i = 0; i < data.Length; i++) + centered[i] = data[i] - 5000d; + var centeredMoments = Numerics.Data.Statistics.Statistics.ProductMoments(centered); + Assert.AreEqual(centeredMoments[1], moments[1], 1E-11); + Assert.AreEqual(centeredMoments[2], moments[2], 1E-10); + Assert.AreEqual(centeredMoments[3], moments[3], 1E-10); + } + /// /// Test the LinearMoments method against the "samlmu()" method of the "lmom" package. /// From b35c89085856439130a4b19d0b842ecf38e9a0c5 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 26 Aug 2026 14:23:32 -0600 Subject: [PATCH 108/222] Record the trace as shallow best-so-far entries and state the read-only contract --- .../Optimization/Support/Optimizer.cs | 9 ++- .../Optimization/Support/Test_Optimizer.cs | 76 +++++++++++++++++++ 2 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 Test_Numerics/Mathematics/Optimization/Support/Test_Optimizer.cs diff --git a/Numerics/Mathematics/Optimization/Support/Optimizer.cs b/Numerics/Mathematics/Optimization/Support/Optimizer.cs index af50cf8f..fd6b1888 100644 --- a/Numerics/Mathematics/Optimization/Support/Optimizer.cs +++ b/Numerics/Mathematics/Optimization/Support/Optimizer.cs @@ -137,8 +137,13 @@ public Func ObjectiveFunction public ParameterSet BestParameterSet { get; protected set; } = new ParameterSet(); /// - /// A trace of the parameter set and fitness evaluated until convergence. + /// A trace of the best-so-far parameter set and fitness at every function evaluation. /// + /// + /// The trace is read-only: entries recorded between improvements share one values array, + /// so the trace stores the search history without one array allocation per function + /// evaluation. + /// public List ParameterSetTrace { get; protected set; } = new List(); /// @@ -252,7 +257,7 @@ protected virtual double Evaluate(double[] values, ref bool cancel) // Update trace. This is tracked every evaluation if (ParameterSetTrace == null) ParameterSetTrace = new List(); - if (RecordTraces) ParameterSetTrace.Add(BestParameterSet.Clone()); + if (RecordTraces) ParameterSetTrace.Add(BestParameterSet.Clone(deep: false)); // update evaluation counter FunctionEvaluations += 1; diff --git a/Test_Numerics/Mathematics/Optimization/Support/Test_Optimizer.cs b/Test_Numerics/Mathematics/Optimization/Support/Test_Optimizer.cs new file mode 100644 index 00000000..0c6042c0 --- /dev/null +++ b/Test_Numerics/Mathematics/Optimization/Support/Test_Optimizer.cs @@ -0,0 +1,76 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Mathematics.Optimization; +using System; + +namespace Mathematics.Optimization +{ + /// + /// Unit tests for the optimizer base class trace contract. + /// + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + [TestClass] + public class Test_Optimizer + { + /// + /// The parameter set trace records one best-so-far entry per function evaluation with + /// nonincreasing fitness, ends at the best parameter set, and shares one values array + /// across the entries recorded between improvements. + /// + /// + /// The trace is read-only: entries recorded between improvements alias the same values + /// array, so the trace stores the search history without one array allocation per + /// function evaluation. + /// + [TestMethod] + public void Test_ParameterSetTrace_Contract() + { + var initial = new double[] { 0.2d, 0.5d, 0.5d }; + var lower = new double[] { 0d, 0d, 0d }; + var upper = new double[] { 1d, 1d, 1d }; + var solver = new NelderMead(TestFunctions.FXYZ, 3, initial, lower, upper); + solver.Minimize(); + + var trace = solver.ParameterSetTrace; + Assert.HasCount(solver.FunctionEvaluations, trace); + + for (int i = 1; i < trace.Count; i++) + { + Assert.IsLessThanOrEqualTo(trace[i - 1].Fitness, trace[i].Fitness, $"fitness increased at entry {i}"); + if (trace[i].Fitness == trace[i - 1].Fitness) + { + Assert.IsTrue(ReferenceEquals(trace[i].Values, trace[i - 1].Values), $"entries {i - 1} and {i} hold separate copies of one best-so-far point"); + } + } + + var last = trace[trace.Count - 1]; + Assert.AreEqual(solver.BestParameterSet.Fitness, last.Fitness); + CollectionAssert.AreEqual(solver.BestParameterSet.Values, last.Values); + } + + /// + /// Disabling trace recording leaves the parameter set trace empty while the solution is + /// unchanged. + /// + [TestMethod] + public void Test_ParameterSetTrace_RecordTracesOff() + { + var initial = new double[] { 0.2d, 0.5d, 0.5d }; + var lower = new double[] { 0d, 0d, 0d }; + var upper = new double[] { 1d, 1d, 1d }; + + var traced = new NelderMead(TestFunctions.FXYZ, 3, initial, lower, upper); + traced.Minimize(); + var untraced = new NelderMead(TestFunctions.FXYZ, 3, initial, lower, upper) { RecordTraces = false }; + untraced.Minimize(); + + Assert.IsEmpty(untraced.ParameterSetTrace); + Assert.AreEqual(traced.BestParameterSet.Fitness, untraced.BestParameterSet.Fitness); + CollectionAssert.AreEqual(traced.BestParameterSet.Values, untraced.BestParameterSet.Values); + } + } +} From 78cf9d8af24eb7f4577ecbadf9aa55aa6433b7b2 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 26 Aug 2026 14:42:24 -0600 Subject: [PATCH 109/222] Evaluate the workhorse univariate log densities in log space --- .../Distributions/Univariate/Exponential.cs | 15 ++ .../Univariate/GammaDistribution.cs | 15 ++ .../Univariate/GeneralizedExtremeValue.cs | 18 ++ Numerics/Distributions/Univariate/Gumbel.cs | 14 ++ Numerics/Distributions/Univariate/LnNormal.cs | 16 ++ .../Distributions/Univariate/LogNormal.cs | 15 ++ .../Univariate/LogPearsonTypeIII.cs | 31 +++ Numerics/Distributions/Univariate/Normal.cs | 15 ++ .../Univariate/PearsonTypeIII.cs | 33 +++ Numerics/Distributions/Univariate/Weibull.cs | 19 ++ .../Univariate/Test_LogPDFOverrides.cs | 209 ++++++++++++++++++ 11 files changed, 400 insertions(+) create mode 100644 Test_Numerics/Distributions/Univariate/Test_LogPDFOverrides.cs diff --git a/Numerics/Distributions/Univariate/Exponential.cs b/Numerics/Distributions/Univariate/Exponential.cs index 832eef3b..185b850d 100644 --- a/Numerics/Distributions/Univariate/Exponential.cs +++ b/Numerics/Distributions/Univariate/Exponential.cs @@ -375,6 +375,21 @@ public override double PDF(double X) return 1d / Alpha * Math.Exp(-((X - Xi) / Alpha)); } + /// + /// + /// Evaluated in log space, so far-tail densities that underflow + /// keep a finite log density. + /// + public override double LogPDF(double X) + { + // Validate parameters + if (_parametersValid == false) + ValidateParameters([Xi, Alpha], true); + if (X < Minimum || X > Maximum) return double.NegativeInfinity; + double lf = -Math.Log(Alpha) - (X - Xi) / Alpha; + return double.IsNaN(lf) ? double.NegativeInfinity : lf; + } + /// public override double CDF(double X) { diff --git a/Numerics/Distributions/Univariate/GammaDistribution.cs b/Numerics/Distributions/Univariate/GammaDistribution.cs index 196de9c7..f50b3f0f 100644 --- a/Numerics/Distributions/Univariate/GammaDistribution.cs +++ b/Numerics/Distributions/Univariate/GammaDistribution.cs @@ -551,6 +551,21 @@ public override double PDF(double X) return Math.Exp(-X / Theta + (Kappa - 1.0d) * Math.Log(X) - Kappa * Math.Log(Theta) - Gamma.LogGamma(Kappa)); } + /// + /// + /// Evaluated in log space, so far-tail densities that underflow + /// keep a finite log density. + /// + public override double LogPDF(double X) + { + // Validate parameters + if (_parametersValid == false) + ValidateParameters(Theta, Kappa, true); + if (X < Minimum || X > Maximum) return double.NegativeInfinity; + double lf = -X / Theta + (Kappa - 1.0d) * Math.Log(X) - Kappa * Math.Log(Theta) - Gamma.LogGamma(Kappa); + return double.IsNaN(lf) ? double.NegativeInfinity : lf; + } + /// public override double CDF(double X) { diff --git a/Numerics/Distributions/Univariate/GeneralizedExtremeValue.cs b/Numerics/Distributions/Univariate/GeneralizedExtremeValue.cs index 08330770..fb9c5f74 100644 --- a/Numerics/Distributions/Univariate/GeneralizedExtremeValue.cs +++ b/Numerics/Distributions/Univariate/GeneralizedExtremeValue.cs @@ -618,6 +618,24 @@ public override double PDF(double x) return Math.Exp(-(1d - Kappa) * y - Math.Exp(-y)) / Alpha; } + /// + /// + /// Evaluated in log space, so far-tail densities that underflow + /// keep a finite log density. + /// + public override double LogPDF(double x) + { + // Validate parameters + if (_parametersValid == false) + ValidateParameters(Xi, Alpha, Kappa, true); + if (x < Minimum || x > Maximum) return double.NegativeInfinity; + double y = (x - Xi) / Alpha; + if (Math.Abs(Kappa) > NearZero) + y = -Math.Log(1d - Kappa * y) / Kappa; + double lf = -(1d - Kappa) * y - Math.Exp(-y) - Math.Log(Alpha); + return double.IsNaN(lf) ? double.NegativeInfinity : lf; + } + /// public override double CDF(double x) { diff --git a/Numerics/Distributions/Univariate/Gumbel.cs b/Numerics/Distributions/Univariate/Gumbel.cs index 938d53c2..18d662d3 100644 --- a/Numerics/Distributions/Univariate/Gumbel.cs +++ b/Numerics/Distributions/Univariate/Gumbel.cs @@ -431,6 +431,20 @@ public override double PDF(double x) return 1d / Alpha * Math.Exp(-(z + Math.Exp(-z))); } + /// + /// + /// Evaluated in log space, so far-tail densities that underflow + /// keep a finite log density. + /// + public override double LogPDF(double x) + { + if (_parametersValid == false) + ValidateParameters(Xi, Alpha, true); + double z = (x - Xi) / Alpha; + double lf = -(z + Math.Exp(-z)) - Math.Log(Alpha); + return double.IsNaN(lf) ? double.NegativeInfinity : lf; + } + /// public override double CDF(double x) { diff --git a/Numerics/Distributions/Univariate/LnNormal.cs b/Numerics/Distributions/Univariate/LnNormal.cs index 910c1cc6..3effb1cd 100644 --- a/Numerics/Distributions/Univariate/LnNormal.cs +++ b/Numerics/Distributions/Univariate/LnNormal.cs @@ -448,6 +448,22 @@ public override double PDF(double x) return Math.Exp(-0.5d * d * d) / (Tools.Sqrt2PI * Sigma * x); } + /// + /// + /// Evaluated in log space, so far-tail densities that underflow + /// keep a finite log density. + /// + public override double LogPDF(double x) + { + // Validate parameters + if (_parametersValid == false) + ValidateParameters(Mu, Sigma, true); + if (x <= Minimum) return double.NegativeInfinity; + double d = (Math.Log(x) - Mu) / Sigma; + double lf = -0.5d * d * d - Math.Log(Tools.Sqrt2PI * Sigma) - Math.Log(x); + return double.IsNaN(lf) ? double.NegativeInfinity : lf; + } + /// public override double CDF(double x) { diff --git a/Numerics/Distributions/Univariate/LogNormal.cs b/Numerics/Distributions/Univariate/LogNormal.cs index 577e99a1..b7c2e51b 100644 --- a/Numerics/Distributions/Univariate/LogNormal.cs +++ b/Numerics/Distributions/Univariate/LogNormal.cs @@ -500,6 +500,21 @@ public override double PDF(double x) return Math.Exp(-0.5d * d * d) / (Tools.Sqrt2PI * Sigma) * (K / x); } + /// + /// + /// Evaluated in log space, so far-tail densities that underflow + /// keep a finite log density. + /// + public override double LogPDF(double x) + { + if (_parametersValid == false) + ValidateParameters(Mu, Sigma, true); + if (x <= Minimum) return double.NegativeInfinity; + double d = (Math.Log(x, Base) - Mu) / Sigma; + double lf = -0.5d * d * d - Math.Log(Tools.Sqrt2PI * Sigma) + Math.Log(K) - Math.Log(x); + return double.IsNaN(lf) ? double.NegativeInfinity : lf; + } + /// public override double CDF(double x) { diff --git a/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs b/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs index 5a15e284..fd4bd642 100644 --- a/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs +++ b/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs @@ -761,6 +761,37 @@ public override double PDF(double x) } } + /// + /// + /// Evaluated in log space, so far-tail densities that underflow + /// keep a finite log density. + /// + public override double LogPDF(double x) + { + // Validate parameters + if (_parametersValid == false) + ValidateParameters(Mu, Sigma, Gamma, true); + if (x < Minimum || x > Maximum) return double.NegativeInfinity; + + double lf; + if (Math.Abs(Gamma) <= NearZero) + { + double d = (Math.Log(x, Base) - Mu) / Sigma; + lf = -0.5d * d * d - Math.Log(Tools.Sqrt2PI * Sigma) + Math.Log(K) - Math.Log(x); + } + else if (Beta > 0d) + { + double shiftedX = Math.Log(x, Base) - Xi; + lf = -shiftedX / Math.Abs(Beta) + (Alpha - 1.0d) * Math.Log(shiftedX) - Alpha * Math.Log(Math.Abs(Beta)) - Mathematics.SpecialFunctions.Gamma.LogGamma(Alpha) + Math.Log(K) - Math.Log(x); + } + else + { + double shiftedX = Xi - Math.Log(x, Base); + lf = -shiftedX / Math.Abs(Beta) + (Alpha - 1.0d) * Math.Log(shiftedX) - Alpha * Math.Log(Math.Abs(Beta)) - Mathematics.SpecialFunctions.Gamma.LogGamma(Alpha) + Math.Log(K) - Math.Log(x); + } + return double.IsNaN(lf) ? double.NegativeInfinity : lf; + } + /// public override double CDF(double x) { diff --git a/Numerics/Distributions/Univariate/Normal.cs b/Numerics/Distributions/Univariate/Normal.cs index b12ab7b1..8685f415 100644 --- a/Numerics/Distributions/Univariate/Normal.cs +++ b/Numerics/Distributions/Univariate/Normal.cs @@ -380,6 +380,21 @@ public override double PDF(double x) return Math.Exp(-0.5d * z * z) / (Tools.Sqrt2PI * Sigma); } + /// + /// + /// Evaluated in log space, so far-tail densities that underflow + /// keep a finite log density. + /// + public override double LogPDF(double x) + { + // Validate parameters + if (_parametersValid == false) + ValidateParameters(Mu, Sigma, true); + double z = (x - Mu) / Sigma; + double lf = -0.5d * z * z - Math.Log(Tools.Sqrt2PI * Sigma); + return double.IsNaN(lf) ? double.NegativeInfinity : lf; + } + /// public override double CDF(double x) { diff --git a/Numerics/Distributions/Univariate/PearsonTypeIII.cs b/Numerics/Distributions/Univariate/PearsonTypeIII.cs index 6065f55b..c9f5713c 100644 --- a/Numerics/Distributions/Univariate/PearsonTypeIII.cs +++ b/Numerics/Distributions/Univariate/PearsonTypeIII.cs @@ -586,6 +586,39 @@ public override double PDF(double x) } } + /// + /// + /// Evaluated in log space, so far-tail densities that underflow + /// keep a finite log density. + /// + public override double LogPDF(double x) + { + // Validate parameters + if (_parametersValid == false) + ValidateParameters(Mu, Sigma, Gamma, true); + if (x < Minimum || x > Maximum) return double.NegativeInfinity; + + double lf; + if (Math.Abs(Gamma) <= NearZero) + { + // Use Normal distribution + double z = (x - Mu) / Sigma; + lf = -0.5d * z * z - Math.Log(Tools.Sqrt2PI * Sigma); + } + else if (Beta > 0d) + { + // Use Gamma distribution + double shiftedX = x - Xi; + lf = -shiftedX / Math.Abs(Beta) + (Alpha - 1.0d) * Math.Log(shiftedX) - Alpha * Math.Log(Math.Abs(Beta)) - Mathematics.SpecialFunctions.Gamma.LogGamma(Alpha); + } + else + { + double shiftedX = Xi - x; + lf = -shiftedX / Math.Abs(Beta) + (Alpha - 1.0d) * Math.Log(shiftedX) - Alpha * Math.Log(Math.Abs(Beta)) - Mathematics.SpecialFunctions.Gamma.LogGamma(Alpha); + } + return double.IsNaN(lf) ? double.NegativeInfinity : lf; + } + /// public override double CDF(double x) { diff --git a/Numerics/Distributions/Univariate/Weibull.cs b/Numerics/Distributions/Univariate/Weibull.cs index 106bae67..c8d5c220 100644 --- a/Numerics/Distributions/Univariate/Weibull.cs +++ b/Numerics/Distributions/Univariate/Weibull.cs @@ -407,6 +407,25 @@ public override double PDF(double x) } } + /// + /// + /// Evaluated in log space, so far-tail densities that underflow + /// keep a finite log density. + /// + public override double LogPDF(double x) + { + // Validate parameters + if (_parametersValid == false) + ValidateParameters(Lambda, Kappa, true); + if (x < Minimum) return double.NegativeInfinity; + if (x == 0.0d && Kappa == 1.0d) + { + return Math.Log(Kappa / Lambda); + } + double lf = Math.Log(Kappa / Lambda) + (Kappa - 1.0d) * Math.Log(x / Lambda) - Math.Pow(x / Lambda, Kappa); + return double.IsNaN(lf) ? double.NegativeInfinity : lf; + } + /// public override double CDF(double x) { diff --git a/Test_Numerics/Distributions/Univariate/Test_LogPDFOverrides.cs b/Test_Numerics/Distributions/Univariate/Test_LogPDFOverrides.cs new file mode 100644 index 00000000..8bcc649d --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Test_LogPDFOverrides.cs @@ -0,0 +1,209 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Distributions; +using System; + +namespace Distributions.Univariate +{ + /// + /// Unit tests for the closed-form log density overrides of ten univariate distributions. + /// + /// + /// + /// Authors: + /// + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + /// + /// Each distribution is checked at an ordinary point and at a far-tail point; where the + /// far-tail density underflows double precision, the log density must stay finite where the + /// density itself cannot. Reference values computed with Python scipy 1.17.1 (scipy.stats.<dist>.logpdf); + /// the base-10 logarithmic distributions, which scipy does not provide, are pinned to 50-digit + /// mpmath 1.4.1 evaluations of the same density and to scipy.stats.pearson3 evaluated on the + /// base-10 logarithm with the change-of-variables term. + /// + /// + /// Tolerances are measured: the closed-form log densities agree with the references to within + /// 1E-12 relative error, and the assertions allow 1E-11 relative plus 1E-10 absolute slack. + /// Every override must also reproduce its own density: exp(LogPDF(x)) is compared to PDF(x) + /// at the ordinary points to 1E-12 relative error. + /// + /// + [TestClass] + public class Test_LogPDFOverrides + { + /// + /// Asserts a log density against its reference value with mixed relative and absolute slack. + /// + /// The reference log density. + /// The evaluated log density. + private static void AssertLogDensity(double expected, double actual) + { + Assert.AreEqual(expected, actual, Math.Abs(expected) * 1E-11 + 1E-10); + } + + /// + /// Asserts that the exponential of the log density reproduces the density at a point where + /// the density is representable. + /// + /// The distribution under test. + /// The evaluation point. + private static void AssertConsistentWithPDF(UnivariateDistributionBase dist, double x) + { + double pdf = dist.PDF(x); + Assert.AreEqual(pdf, Math.Exp(dist.LogPDF(x)), pdf * 1E-12); + } + + /// + /// The normal log density matches scipy at an ordinary point and stays finite forty + /// standard deviations out. + /// + [TestMethod] + public void Test_Normal_LogPDF() + { + var d = new Normal(10, 2); + AssertLogDensity(-2.112085713764618, d.LogPDF(12.0)); + AssertLogDensity(-801.6120857137646, d.LogPDF(90.0)); + AssertConsistentWithPDF(d, 12.0); + } + + /// + /// The natural-log normal log density matches scipy lognorm(s = sigma, scale = exp(mu)) at + /// an ordinary point and stays finite + /// where the density underflows, and is negative infinity at and below the support bound. + /// + [TestMethod] + public void Test_LnNormal_LogPDF() + { + var d = new LnNormal() { Mu = 2, Sigma = 0.5 }; + AssertLogDensity(-2.317854811413502, d.LogPDF(8.0)); + AssertLogDensity(-907.4244569387412, d.LogPDF(1E10)); + AssertConsistentWithPDF(d, 8.0); + Assert.AreEqual(double.NegativeInfinity, d.LogPDF(0.0)); + Assert.AreEqual(double.NegativeInfinity, d.LogPDF(-1.0)); + } + + /// + /// The base-10 log-normal log density matches the 50-digit mpmath evaluation at an + /// ordinary point and deep in the upper tail. + /// + [TestMethod] + public void Test_LogNormal_LogPDF() + { + var d = new LogNormal() { Mu = 1.2, Sigma = 0.25 }; + AssertLogDensity(-3.4440653710776417, d.LogPDF(20.0)); + AssertLogDensity(-388.7073573612851, d.LogPDF(1E8)); + AssertConsistentWithPDF(d, 20.0); + Assert.AreEqual(double.NegativeInfinity, d.LogPDF(0.0)); + } + + /// + /// The Pearson Type III log density matches scipy pearson3 on both skew signs and stays + /// finite thirty standard deviations into the upper tail. + /// + [TestMethod] + public void Test_PearsonTypeIII_LogPDF() + { + var d = new PearsonTypeIII(10, 2, 0.8); + AssertLogDensity(-2.6217159334295284, d.LogPDF(12.5)); + AssertLogDensity(-63.159423624324326, d.LogPDF(70.0)); + AssertConsistentWithPDF(d, 12.5); + + var dn = new PearsonTypeIII(10, 2, -0.8); + AssertLogDensity(-2.1394304489371043, dn.LogPDF(12.5)); + AssertConsistentWithPDF(dn, 12.5); + } + + /// + /// The log-Pearson Type III log density matches scipy pearson3 evaluated on the base-10 + /// logarithm with the change-of-variables term. + /// + [TestMethod] + public void Test_LogPearsonTypeIII_LogPDF() + { + var d = new LogPearsonTypeIII(1.5, 0.3, 0.6); + AssertLogDensity(-4.844505860354813, d.LogPDF(50.0)); + AssertLogDensity(-58.859703127182755, d.LogPDF(1E7)); + AssertConsistentWithPDF(d, 50.0); + Assert.AreEqual(double.NegativeInfinity, d.LogPDF(0.0)); + } + + /// + /// The generalized extreme value log density matches scipy genextreme and stays finite deep + /// in its lower tail. + /// + [TestMethod] + public void Test_GeneralizedExtremeValue_LogPDF() + { + var d = new GeneralizedExtremeValue(100, 25, 0.15); + AssertLogDensity(-4.60976457450874, d.LogPDF(130.0)); + AssertLogDensity(-6133.58747825334, d.LogPDF(-350.0)); + AssertConsistentWithPDF(d, 130.0); + } + + /// + /// The Gumbel log density matches scipy gumbel_r and stays finite where the double + /// exponential collapses the density. + /// + [TestMethod] + public void Test_Gumbel_LogPDF() + { + var d = new Gumbel(100, 25); + AssertLogDensity(-4.7200700367804025, d.LogPDF(130.0)); + AssertLogDensity(-78962960182651.9, d.LogPDF(-700.0)); + AssertConsistentWithPDF(d, 130.0); + } + + /// + /// The Weibull log density matches scipy weibull_min at an ordinary point and deep in the + /// upper tail, and is negative infinity below the support. + /// + [TestMethod] + public void Test_Weibull_LogPDF() + { + var d = new Weibull(50, 2.5); + AssertLogDensity(-3.902881002765252, d.LogPDF(40.0)); + AssertLogDensity(-732.4019940868236, d.LogPDF(700.0)); + AssertConsistentWithPDF(d, 40.0); + Assert.AreEqual(double.NegativeInfinity, d.LogPDF(-1.0)); + } + + /// + /// The exponential log density matches scipy expon and stays finite where the density + /// underflows, and is negative infinity below the location bound. + /// + [TestMethod] + public void Test_Exponential_LogPDF() + { + var d = new Exponential(5, 20); + AssertLogDensity(-4.24573227355399, d.LogPDF(30.0)); + AssertLogDensity(-752.745732273554, d.LogPDF(15000.0)); + AssertConsistentWithPDF(d, 30.0); + Assert.AreEqual(double.NegativeInfinity, d.LogPDF(4.0)); + } + + /// + /// The gamma log density matches scipy and stays finite deep in the upper tail. + /// + [TestMethod] + public void Test_Gamma_LogPDF() + { + var d = new GammaDistribution(15, 3); + AssertLogDensity(-4.014903020542265, d.LogPDF(30.0)); + AssertLogDensity(-1322.3436560121274, d.LogPDF(20000.0)); + AssertConsistentWithPDF(d, 30.0); + } + + /// + /// A single far-tail observation keeps the log likelihood of a sample finite and equal to + /// the sum of the log densities. + /// + [TestMethod] + public void Test_LogLikelihood_FarTailObservation() + { + var d = new Normal(0, 1); + double logLH = d.LogLikelihood(new double[] { 0.0, 50.0 }); + AssertLogDensity(-0.9189385332046727 - 1250.9189385332047, logLH); + } + } +} From cdade0faf3c1e5e09a5fd020269f404b3d3b5740 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 26 Aug 2026 14:43:55 -0600 Subject: [PATCH 110/222] Synchronize the child reporter registry and snapshot its enumeration --- Numerics/Utilities/SafeProgressReporter.cs | 22 +++++- .../Utilities/Test_SafeProgressReporter.cs | 73 +++++++++++++++++++ 2 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 Test_Numerics/Utilities/Test_SafeProgressReporter.cs diff --git a/Numerics/Utilities/SafeProgressReporter.cs b/Numerics/Utilities/SafeProgressReporter.cs index 7cff2482..5041bf05 100644 --- a/Numerics/Utilities/SafeProgressReporter.cs +++ b/Numerics/Utilities/SafeProgressReporter.cs @@ -51,6 +51,7 @@ public SafeProgressReporter(string taskName) private MessageType _previousMessageType = MessageType.Status; private Process? _externalProcess; private List _subProgReporterCollection = new List(); + private readonly object _subProgReporterLock = new object(); private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); /// /// Callback for invoking progress event handlers on the synchronization context. @@ -110,7 +111,14 @@ public SafeProgressReporter(string taskName) /// public ReadOnlyCollection ChildReporters { - get { return new ReadOnlyCollection(_subProgReporterCollection); } + get + { + // A snapshot, so callers can enumerate while other threads register children + lock (_subProgReporterLock) + { + return new ReadOnlyCollection(_subProgReporterCollection.ToArray()); + } + } } /// @@ -343,9 +351,12 @@ public void RequestCancel() public void ResetCancel() { _cancellationTokenSource = new CancellationTokenSource(); - foreach (var subProg in _subProgReporterCollection) + lock (_subProgReporterLock) { - subProg._cancellationTokenSource = _cancellationTokenSource; + foreach (var subProg in _subProgReporterCollection) + { + subProg._cancellationTokenSource = _cancellationTokenSource; + } } } @@ -369,7 +380,10 @@ public SafeProgressReporter CreateProgressModifier(float fractionOfTotal, string child.ProgressReported += (reporter, prog, progDelta) => ReportProgress(_previousProgress + progDelta * fractionOfTotal); child.MessageReported += msg => ReportMessage(msg); child._cancellationTokenSource = _cancellationTokenSource; - _subProgReporterCollection.Add(child); + lock (_subProgReporterLock) + { + _subProgReporterCollection.Add(child); + } var invokeChildCreatedHandlers = new SendOrPostCallback(state => ChildReporterCreated?.Invoke(child)); _synchronizationContext?.Post(invokeChildCreatedHandlers, child); return child; diff --git a/Test_Numerics/Utilities/Test_SafeProgressReporter.cs b/Test_Numerics/Utilities/Test_SafeProgressReporter.cs new file mode 100644 index 00000000..f0cb8c54 --- /dev/null +++ b/Test_Numerics/Utilities/Test_SafeProgressReporter.cs @@ -0,0 +1,73 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Utilities; +using System.Threading.Tasks; + +namespace Utilities +{ + /// + /// Unit tests for the thread safety of the progress reporter's child registry. + /// + /// + /// + /// Authors: + /// + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + /// + [TestClass] + public class Test_SafeProgressReporter + { + /// + /// Creating child reporters concurrently from a parallel loop registers every child and + /// links every child to the parent's cancellation source. + /// + [TestMethod] + public void Test_CreateProgressModifier_ParallelRegistration() + { + var parent = new SafeProgressReporter("parent"); + int children = 1000; + var created = new SafeProgressReporter[children]; + + Parallel.For(0, children, i => + { + created[i] = parent.CreateProgressModifier(1f / children, $"child {i}"); + }); + + Assert.HasCount(children, parent.ChildReporters); + + parent.RequestCancel(); + for (int i = 0; i < children; i++) + { + Assert.IsTrue(created[i].IsCancelRequested, $"child {i} did not receive the cancellation request"); + } + } + + /// + /// Enumerating the child reporters while new children are being registered neither throws + /// nor observes a partially updated registry entry. + /// + [TestMethod] + public void Test_ChildReporters_SnapshotDuringRegistration() + { + var parent = new SafeProgressReporter("parent"); + int children = 500; + + var addTask = Task.Run(() => + { + for (int i = 0; i < children; i++) + parent.CreateProgressModifier(1f / children, $"child {i}"); + }); + + while (!addTask.IsCompleted) + { + foreach (var child in parent.ChildReporters) + { + Assert.IsNotNull(child); + } + } + addTask.Wait(); + Assert.HasCount(children, parent.ChildReporters); + } + } +} From 404468e108e76b33d1a930d821ee04c01d1cda05 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 26 Aug 2026 14:44:58 -0600 Subject: [PATCH 111/222] Preallocate the without-replacement bins and pin the seeded draw sequences --- Numerics/Utilities/ExtensionMethods.cs | 2 +- .../Utilities/Test_ExtensionMethods.cs | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Numerics/Utilities/ExtensionMethods.cs b/Numerics/Utilities/ExtensionMethods.cs index 49d107b8..a45dc1b9 100644 --- a/Numerics/Utilities/ExtensionMethods.cs +++ b/Numerics/Utilities/ExtensionMethods.cs @@ -105,7 +105,7 @@ public static int[] NextIntegers(this Random random, int minValue, int maxValue, if (length > maxValue - minValue) throw new ArgumentException("When sampling without replacement, the length must be less than or equal to the range of values."); - var bins = new List(); + var bins = new List(maxValue - minValue); for (int i = minValue; i < maxValue; i++) bins.Add(i); diff --git a/Test_Numerics/Utilities/Test_ExtensionMethods.cs b/Test_Numerics/Utilities/Test_ExtensionMethods.cs index ca53ae09..3a8e504e 100644 --- a/Test_Numerics/Utilities/Test_ExtensionMethods.cs +++ b/Test_Numerics/Utilities/Test_ExtensionMethods.cs @@ -498,5 +498,25 @@ public void Test_Fill_Matrix() Assert.AreEqual(fillValue, matrix[i, j]); } + /// + /// Sampling integers without replacement draws by removing the selected bin, so a seeded + /// sequence is a stable contract. + /// + /// + /// The pinned sequences hold the order-preserving removal semantics in place: selecting a + /// bin removes it and shifts the remaining bins without reordering them, so any change to + /// the draw mechanics is observable here. + /// + [TestMethod] + public void Test_NextIntegers_WithoutReplacement_SeededSequences() + { + CollectionAssert.AreEqual(new int[] { 0, 7, 6, 4, 1, 5, 8, 9, 3, 2 }, + new MersenneTwister(12345).NextIntegers(0, 10, 10, false)); + CollectionAssert.AreEqual(new int[] { 35, 17, 53, 58, 24, 40, 91, 70, 90, 72, 11, 82, 88, 50, 78 }, + new MersenneTwister(777).NextIntegers(0, 100, 15, false)); + CollectionAssert.AreEqual(new int[] { 7, 19, 23, 18, 16, 11, 22, 14, 21, 8, 20, 12, 9, 15, 13, 10, 24, 6, 17, 5 }, + new MersenneTwister(42).NextIntegers(5, 25, 20, false)); + } + } } From 4af8c78e117235ac03bf0c2712f631997b47adfd Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 26 Aug 2026 14:47:52 -0600 Subject: [PATCH 112/222] Let the incomplete gamma continued fraction iterate to its convergence test --- .../Mathematics/Special Functions/Gamma.cs | 9 ++---- .../Special Functions/Test_Gamma.cs | 32 +++++++++++++++++++ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/Numerics/Mathematics/Special Functions/Gamma.cs b/Numerics/Mathematics/Special Functions/Gamma.cs index 16b6143a..2e5b50a0 100644 --- a/Numerics/Mathematics/Special Functions/Gamma.cs +++ b/Numerics/Mathematics/Special Functions/Gamma.cs @@ -631,14 +631,9 @@ public static double Incomplete(double X, double alpha) PN2 = PN4; PN3 = PN5; PN4 = PN6; - if (Math.Abs(PN5) < OFL) - { - // ITERATION HAS NOT CONVERGED. RESULT MAY BE UNRELIABLE.' - // Consider adding a message box or something - break; - } - else + if (Math.Abs(PN5) >= OFL) { + // Rescale the recurrence so the convergents stay inside the overflow bound PN1 = PN1 / OFL; PN2 = PN2 / OFL; PN3 = PN3 / OFL; diff --git a/Test_Numerics/Mathematics/Special Functions/Test_Gamma.cs b/Test_Numerics/Mathematics/Special Functions/Test_Gamma.cs index 17ac39e5..ea5a6b57 100644 --- a/Test_Numerics/Mathematics/Special Functions/Test_Gamma.cs +++ b/Test_Numerics/Mathematics/Special Functions/Test_Gamma.cs @@ -161,6 +161,38 @@ public void Test_LogGamma() } } + /// + /// The regularized incomplete gamma integral matches 50-digit reference values on every + /// algorithm branch, including the continued fraction taken when the argument reaches the + /// shape parameter. + /// + /// + /// Reference values computed with Python mpmath 1.4.1 at 50 significant digits, + /// gammainc(alpha, 0, X, regularized=True), and cross-checked with scipy 1.17.1 + /// scipy.special.gammainc, which agrees with mpmath to the last printed digit on every + /// case below. + /// + [TestMethod] + public void Test_Incomplete_ReferenceValues() + { + // Continued-fraction branch: X > 1 and X >= alpha + Assert.AreEqual(0.9886559833621947, Gamma.Incomplete(1.5, 0.1), 1E-11); + Assert.AreEqual(0.9932620530009145, Gamma.Incomplete(5.0, 1.0), 1E-11); + Assert.AreEqual(0.9796589705830716, Gamma.Incomplete(12.0, 6.0), 1E-11); + Assert.AreEqual(0.9991559189282155, Gamma.Incomplete(45.0, 26.0), 1E-11); + Assert.AreEqual(0.5059471461707603, Gamma.Incomplete(500.0, 500.0), 1E-11); + Assert.AreEqual(0.9729484747641512, Gamma.Incomplete(700.0, 650.0), 1E-11); + + // Series branch: X <= 1 or X < alpha + Assert.AreEqual(0.30146464169666126, Gamma.Incomplete(0.075, 0.5), 1E-11); + Assert.AreEqual(0.9083579897300342, Gamma.Incomplete(0.3, 0.1), 1E-11); + + // Hill approximation branch: alpha > 10000. The asymptotic expansion carries its own + // error of about 6E-6 at this argument, so the tolerance reflects the branch accuracy + // rather than double precision. + Assert.AreEqual(0.8366349011570255, Gamma.Incomplete(10500.0, 10400.0), 1E-5); + } + /// /// Test the incomplete gamma integral /// From b2325250ffbe3279ebd1ea1f3419c337542a693c Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 26 Aug 2026 14:51:58 -0600 Subject: [PATCH 113/222] Preserve the eigen input matrix and scale the Jacobi threshold to the matrix --- .../Linear Algebra/EigenValueDecomposition.cs | 22 ++++++++--- .../Test_EigenValueDecomposition.cs | 37 +++++++++++++++++++ 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/Numerics/Mathematics/Linear Algebra/EigenValueDecomposition.cs b/Numerics/Mathematics/Linear Algebra/EigenValueDecomposition.cs index 3749fcb5..62a9d6dd 100644 --- a/Numerics/Mathematics/Linear Algebra/EigenValueDecomposition.cs +++ b/Numerics/Mathematics/Linear Algebra/EigenValueDecomposition.cs @@ -45,9 +45,19 @@ public EigenValueDecomposition(Matrix A) EigenVectors = Matrix.Identity(n); EigenValues = new Vector(n); - // Work on a local copy of A (array) for speed - var a = this.A.Array; // same storage as this.A - const double tol = 1e-12; + // Rotate a working copy so the public input matrix keeps the values that were decomposed + var work = new Matrix(A.ToArray()); + var a = work.Array; // same storage as work + + // The convergence threshold follows the scale of the matrix, so a decomposition of c*A + // stops at the same relative accuracy as a decomposition of A. The largest element + // magnitude is the reference: it dominates every off-diagonal, and rotations can always + // drive the off-diagonals below 1E-12 of it before reaching roundoff. + double elementMax = 0.0; + for (int i = 0; i < n; i++) + for (int j = 0; j < n; j++) + elementMax = Math.Max(elementMax, Math.Abs(a[i, j])); + double tol = 1e-12 * elementMax; const int maxIter = 2000; for (int iter = 0; iter < maxIter; iter++) @@ -104,9 +114,9 @@ public EigenValueDecomposition(Matrix A) } } - // Extract eigenvalues from diagonal of A - for (int i = 0; i < n; i++) - EigenValues[i] = this.A[i, i]; + // Extract eigenvalues from the diagonal of the rotated working copy + for (int i = 0; i < n; i++) + EigenValues[i] = a[i, i]; } private readonly int n; // Size of A diff --git a/Test_Numerics/Mathematics/Linear Algebra/Test_EigenValueDecomposition.cs b/Test_Numerics/Mathematics/Linear Algebra/Test_EigenValueDecomposition.cs index 42a172f8..8ff1f1e5 100644 --- a/Test_Numerics/Mathematics/Linear Algebra/Test_EigenValueDecomposition.cs +++ b/Test_Numerics/Mathematics/Linear Algebra/Test_EigenValueDecomposition.cs @@ -280,5 +280,42 @@ private static void AssertMatrixAlmostEqual(Matrix expected, Matrix actual, doub for (int j = 0; j < expected.NumberOfColumns; j++) Assert.AreEqual(expected[i, j], actual[i, j], tol); } + /// + /// The decomposition converges to the same relative accuracy at any matrix scale, and the + /// public input matrix keeps the values that were decomposed. + /// + /// + /// Reference eigenvalues computed with Python numpy 2.4.2 numpy.linalg.eigh on the 3x3 + /// fixture scaled by 1E-9 and by 1E+6. The assertions demand 1E-12 relative accuracy, so a + /// convergence threshold that ignores the matrix scale is observable at either extreme. + /// + [TestMethod] + public void Test_ScaleInvariantConvergence_And_InputPreserved() + { + var baseA = new double[,] { { 4, 1, 1 }, { 1, 3, 0 }, { 1, 0, 2 } }; + var expected = new double[] { 1.4679111137620429, 2.6527036446661385, 4.879385241571816 }; + + foreach (double scale in new double[] { 1E-9, 1.0, 1E+6 }) + { + var A = new Matrix(3, 3); + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + A[i, j] = baseA[i, j] * scale; + + var evd = new EigenValueDecomposition(A); + + // The input matrix is preserved bit for bit + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + Assert.AreEqual(A[i, j], evd.A[i, j], $"A[{i},{j}] at scale {scale}"); + + // Eigenvalues scale linearly and hold 1E-12 relative accuracy + var w = new double[] { evd.EigenValues[0], evd.EigenValues[1], evd.EigenValues[2] }; + Array.Sort(w); + for (int i = 0; i < 3; i++) + Assert.AreEqual(expected[i] * scale, w[i], Math.Abs(expected[i] * scale) * 1E-12); + } + } + } } From 8bfbe5082f36b97b2a8890bdc29c1969d6efd6dc Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 26 Aug 2026 14:54:06 -0600 Subject: [PATCH 114/222] Report the full Gaussian mixture log-likelihood at every iteration --- .../Unsupervised/GaussianMixtureModel.cs | 9 ++++-- .../Machine Learning/Unsupervised/Test_GMM.cs | 30 +++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/Numerics/Machine Learning/Unsupervised/GaussianMixtureModel.cs b/Numerics/Machine Learning/Unsupervised/GaussianMixtureModel.cs index dec66a47..9ae72ee7 100644 --- a/Numerics/Machine Learning/Unsupervised/GaussianMixtureModel.cs +++ b/Numerics/Machine Learning/Unsupervised/GaussianMixtureModel.cs @@ -220,13 +220,14 @@ public void Train(int seed = -1, bool kMeansPlusPlus = true) double oldLogLH = double.MinValue, newLogLH = double.MinValue; for (Iterations = 1; Iterations <= MaxIterations; Iterations++) { - // Perform the expectation step + // Perform the expectation step. The log-likelihood records every iteration, so a + // run that exhausts its iteration budget reports its final evaluated value. newLogLH = EStep(); + LogLikelihood = newLogLH; // Check convergence if (Math.Abs((oldLogLH - newLogLH) / oldLogLH) < Tolerance) { - LogLikelihood = newLogLH; break; } @@ -297,7 +298,9 @@ private double EStep() LikelihoodMatrix[i, k] = Math.Exp(LikelihoodMatrix[i, k] - tmp); logLH += tmp; } - return logLH; + // The likelihood matrix omits the constant -D/2 * ln(2*pi) per point because it cancels + // in the responsibilities; the reported log-likelihood restores it. + return logLH - 0.5 * Dimension * X.NumberOfRows * Math.Log(2.0 * Math.PI); } /// diff --git a/Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs b/Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs index d19dd3b3..71a99aae 100644 --- a/Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs +++ b/Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs @@ -2,6 +2,7 @@ using Numerics.MachineLearning; using System.Collections.Generic; using Numerics.Mathematics.LinearAlgebra; +using Numerics.Sampling; namespace MachineLearning { @@ -48,6 +49,35 @@ public void Test_GMM_Iris() Assert.AreEqual(trueMean2[i], gmm.Means[1, i], 1E-2); Assert.AreEqual(trueMean3[i], gmm.Means[2, i], 1E-2); } + + // The log-likelihood carries the full Gaussian normalizing constant. R mclust + // (Mclust, G = 3, VVV) reports -180.185 and Python sklearn 1.8.0 + // (GaussianMixture(3, covariance_type='full'), score(X) * n) reports -180.18547759250404 + // for this fixture; the window covers EM stopping differences between implementations. + Assert.AreEqual(-180.185, gmm.LogLikelihood, 0.5); + } + + /// + /// A training run that exhausts its iteration budget reports the log-likelihood of its + /// final iteration rather than a placeholder. + /// + [TestMethod] + public void Test_GMM_LogLikelihood_AtIterationCap() + { + var x = new double[100]; + var y = new double[100]; + var rnd = new MersenneTwister(4242); + for (int i = 0; i < 100; i++) + { + double t = i < 50 ? 0.0 : 8.0; + x[i] = t + rnd.NextDouble(); + y[i] = t + rnd.NextDouble(); + } + var gmm = new GaussianMixtureModel(new Matrix(new List { x, y }), 2) { MaxIterations = 2 }; + gmm.Train(12345); + + Assert.IsLessThan(0, gmm.LogLikelihood, "the iteration-capped run must report its final evaluated log-likelihood"); + Assert.IsFalse(double.IsNaN(gmm.LogLikelihood) || double.IsInfinity(gmm.LogLikelihood)); } } } From 1eeeb5604b33b941fe95d8cfd07890dcc188d3a9 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 26 Aug 2026 14:56:09 -0600 Subject: [PATCH 115/222] Correct the Mann-Kendall and Mann-Whitney tie terms with full group sizes --- Numerics/Data/Statistics/HypothesisTests.cs | 41 ++++++++++++++++--- .../Data/Statistics/Test_HypothesisTests.cs | 39 +++++++++++++++++- 2 files changed, 73 insertions(+), 7 deletions(-) diff --git a/Numerics/Data/Statistics/HypothesisTests.cs b/Numerics/Data/Statistics/HypothesisTests.cs index 4e5f5088..530595c7 100644 --- a/Numerics/Data/Statistics/HypothesisTests.cs +++ b/Numerics/Data/Statistics/HypothesisTests.cs @@ -231,6 +231,33 @@ public static double LjungBoxTest(IList sample, int lagMax = -1) return 1d - chi2.CDF(Q); } + /// + /// Returns the size of each group of tied values in the sample. + /// + /// The data sample. + /// One entry per group of two or more equal values, holding the group size. + private static List TieGroupSizes(IList sample) + { + var sorted = sample.ToArray(); + Array.Sort(sorted); + var groups = new List(); + int run = 1; + for (int i = 1; i < sorted.Length; i++) + { + if (sorted[i] == sorted[i - 1]) + { + run++; + } + else + { + if (run > 1) groups.Add(run); + run = 1; + } + } + if (run > 1) groups.Add(run); + return groups; + } + /// /// The Mann-Whitney test for homogeneity and stationarity (jump). /// @@ -247,12 +274,14 @@ public static double MannWhitneyTest(IList sample1, IList sample var sample = new List(); sample.AddRange(sample1.ToList()); sample.AddRange(sample2.ToList()); - var ties = new double[n1]; double R = 0, T = 0; + double R = 0, T = 0; - var ranks = Statistics.RanksInPlace(sample.ToArray(), out ties); + var ranks = Statistics.RanksInPlace(sample.ToArray(), out _); for (int i = 0; i < sample1.Count; i++) R += ranks[i]; - for (int i = 0; i < ties.Length; i++) T += (Tools.Pow(ties[i], 3) - ties[i]) / (n * (n - 1)); + // The variance correction sums over tie groups by their full size + foreach (double g in TieGroupSizes(sample)) + T += (Tools.Pow(g, 3) - g) / (n * (n - 1)); double V = R - n1 * (n1 + 1d) / 2d; double W = n1 * n2 - V; @@ -283,9 +312,9 @@ public static double MannKendallTest(IList sample) } } - var ties = new double[n]; - var R = Statistics.RanksInPlace(sample.ToArray(), out ties); - for (i = 0; i < ties.Length; i++) T += ties[i] * (ties[i] - 1) * (2 * ties[i] + 5); + // The variance correction sums over tie groups by their full size + foreach (double g in TieGroupSizes(sample)) + T += g * (g - 1) * (2 * g + 5); varS = (n * (n - 1) * (2 * n + 5) - T) / 18; z = Math.Abs((S - Math.Sign(S)) / Math.Sqrt(varS)); diff --git a/Test_Numerics/Data/Statistics/Test_HypothesisTests.cs b/Test_Numerics/Data/Statistics/Test_HypothesisTests.cs index e1624d47..18128cb2 100644 --- a/Test_Numerics/Data/Statistics/Test_HypothesisTests.cs +++ b/Test_Numerics/Data/Statistics/Test_HypothesisTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Data.Statistics; using Numerics.Distributions; @@ -338,6 +338,43 @@ public void Test_UnimodalityTest() } + /// + /// The Mann-Kendall variance correction uses the size of each tie group, so a heavily tied + /// record matches the reference implementation. + /// + /// + /// Reference p-value computed with Python pymannkendall 1.4.3 original_test and confirmed + /// by an independent evaluation of the Kendall (1975) variance formula with + /// scipy 1.17.1: p = 2.687544409241127E-08 for this fixture of twelve distinct values + /// followed by twelve tied values. + /// + [TestMethod] + public void Test_MannKendall_HeavyTies() + { + var sample = new double[] { 8.017, 8.219, 8.759, 9.016, 9.091, 9.452, 10.002, 10.12, 10.597, 10.714, 10.98, 12.68, + 20.0, 20.0, 20.0, 20.0, 20.0, 20.0, 20.0, 20.0, 20.0, 20.0, 20.0, 20.0 }; + double pValue = HypothesisTests.MannKendallTest(sample); + Assert.AreEqual(2.687544409241127E-08, pValue, 2.687544409241127E-08 * 1E-6); + } + + /// + /// The Mann-Whitney variance correction uses the size of each tie group, so tied samples + /// match the scipy asymptotic test without continuity correction. + /// + /// + /// Reference p-value computed with Python scipy 1.17.1 + /// scipy.stats.mannwhitneyu(x, y, use_continuity=False, alternative='two-sided', + /// method='asymptotic') = 0.03509475123312452. + /// + [TestMethod] + public void Test_MannWhitney_HeavyTies() + { + var sample1 = new double[] { 1.0, 1.0, 2.0, 2.0, 2.0, 3.0, 3.0, 4.0, 5.0, 5.0 }; + var sample2 = new double[] { 2.0, 2.0, 3.0, 3.0, 3.0, 4.0, 4.0, 4.0, 5.0, 5.0, 6.0, 6.0, 7.0, 8.0 }; + double pValue = HypothesisTests.MannWhitneyTest(sample1, sample2); + Assert.AreEqual(0.03509475123312452, pValue, 0.03509475123312452 * 1E-6); + } + } } From 5a6a824a738677edd4593372d4a996607a8d42ca Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 26 Aug 2026 14:58:45 -0600 Subject: [PATCH 116/222] Shift the within-class variance accumulation and floor degenerate deviations --- .../Machine Learning/Supervised/NaiveBayes.cs | 26 +++++++++-- .../Supervised/Test_NaiveBayes.cs | 46 +++++++++++++++++++ 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/Numerics/Machine Learning/Supervised/NaiveBayes.cs b/Numerics/Machine Learning/Supervised/NaiveBayes.cs index a0aea9b1..8f793fc6 100644 --- a/Numerics/Machine Learning/Supervised/NaiveBayes.cs +++ b/Numerics/Machine Learning/Supervised/NaiveBayes.cs @@ -163,6 +163,9 @@ public void Train() // Compute the mean and standard deviation of each feature j given the class i for (int j = 0; j < nFeatures; j++) { + // Sums of powers accumulated about the first class member, so the variance is + // conditioned on the within-class spread rather than on the distance from zero + double shift = double.NaN; double x = 0; // sum double x2 = 0; // sum of X^2 double u1, u2; @@ -172,8 +175,10 @@ public void Train() { if (Y[k] == Classes[i]) { - x += X[k, j]; - x2 += Math.Pow(X[k, j], 2); + if (double.IsNaN(shift)) shift = X[k, j]; + double y = X[k, j] - shift; + x += y; + x2 += y * y; n++; } } @@ -181,7 +186,7 @@ public void Train() u1 = x / n; u2 = x2 / n; // Set means - Means[i, j] = u1; + Means[i, j] = shift + u1; // Set standard deviations if (n <= 1) StandardDeviations[i, j] = 1e-6; @@ -191,6 +196,21 @@ public void Train() } + // Floor the standard deviations at 1E-9 of the largest feature variance, so a feature + // that is constant within a class carries a sharp but usable density instead of a + // degenerate one + double maxVariance = 0; + for (int i = 0; i < nClasses; i++) + for (int j = 0; j < nFeatures; j++) + maxVariance = Math.Max(maxVariance, StandardDeviations[i, j] * StandardDeviations[i, j]); + double floor = Math.Sqrt(1e-9 * maxVariance); + if (floor > 0) + { + for (int i = 0; i < nClasses; i++) + for (int j = 0; j < nFeatures; j++) + StandardDeviations[i, j] = Math.Max(StandardDeviations[i, j], floor); + } + IsTrained = true; } diff --git a/Test_Numerics/Machine Learning/Supervised/Test_NaiveBayes.cs b/Test_Numerics/Machine Learning/Supervised/Test_NaiveBayes.cs index 23807cbf..cf5119f8 100644 --- a/Test_Numerics/Machine Learning/Supervised/Test_NaiveBayes.cs +++ b/Test_Numerics/Machine Learning/Supervised/Test_NaiveBayes.cs @@ -103,5 +103,51 @@ public void Test_NaiveBayes_Iris() } } + /// + /// A feature that is constant within a class classifies through the variance floor rather + /// than failing, and the predictions match the reference classifier. + /// + /// + /// Reference predictions computed with Python scikit-learn 1.8.0 GaussianNB, whose + /// var_smoothing floor is 1E-9 times the largest pooled feature variance: classes + /// { 0, 1, 0, 1 } for the four test rows. + /// + [TestMethod] + public void Test_NaiveBayes_ConstantFeatureWithinClass() + { + var x = new double[,] { { 1.2, 1.0 }, { 1.9, 1.0 }, { 0.8, 1.0 }, { 1.5, 1.0 }, { 1.1, 1.0 }, { 1.7, 1.0 }, + { 6.1, 0.0 }, { 5.8, 0.0 }, { 6.6, 0.0 }, { 5.2, 0.0 }, { 6.9, 0.0 }, { 5.5, 0.0 } }; + var y = new double[] { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 }; + var nb = new NaiveBayes(x, y); + nb.Train(); + var predictions = nb.Predict(new double[,] { { 1.4, 1.0 }, { 6.3, 0.0 }, { 3.4, 0.6 }, { 1.0, 0.0 } }); + CollectionAssert.AreEqual(new double[] { 0, 1, 0, 1 }, predictions); + } + + /// + /// A feature recorded against a large datum keeps its within-class variance, and the + /// predictions match the reference classifier. + /// + /// + /// Reference predictions { 0, 1, 0 } computed with Python scikit-learn 1.8.0 GaussianNB on + /// the same fixture. The pinned standard deviations are the exact Bessel-corrected sample + /// values from rational arithmetic; scikit-learn's var_ stores the population-normalized + /// variance of the same data, so the two agree after the n/(n-1) conversion. + /// + [TestMethod] + public void Test_NaiveBayes_LargeDatumFeature() + { + double off = 1E10; + var x = new double[,] { { off + 0.0, 1.2 }, { off + 1.0, 1.9 }, { off + 2.0, 0.8 }, { off + 3.0, 1.5 }, { off + 4.0, 1.1 }, { off + 5.0, 1.7 }, + { off + 50.0, 6.1 }, { off + 51.0, 5.8 }, { off + 52.0, 6.6 }, { off + 53.0, 5.2 }, { off + 54.0, 6.9 }, { off + 55.0, 5.5 } }; + var y = new double[] { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 }; + var nb = new NaiveBayes(x, y); + nb.Train(); + Assert.AreEqual(1.8708286933869707, nb.StandardDeviations[0, 0], 1E-9); + Assert.AreEqual(0.408248290463863, nb.StandardDeviations[0, 1], 1E-9); + var predictions = nb.Predict(new double[,] { { off + 2.5, 1.4 }, { off + 52.5, 6.3 }, { off + 27.0, 3.4 } }); + CollectionAssert.AreEqual(new double[] { 0, 1, 0 }, predictions); + } + } } From ff3e3031c0fd2d3658575123e9260ad668d4d0f2 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 26 Aug 2026 14:59:44 -0600 Subject: [PATCH 117/222] Make the SVD non-convergence guard reachable and route it through validation The guard follows the svdcmp reference bound of 30 bidiagonal QR iterations. No unit test accompanies the guard itself: it is a safety net whose trigger has no known double-precision reproduction, and every convergent input is already pinned bit for bit by the existing decomposition suite. The multivariate normal validation path returns the non-convergence exception under its non-throwing contract instead of leaking it. --- .../Distributions/Multivariate/MultivariateNormal.cs | 12 +++++++++++- .../Linear Algebra/SingularValueDecomposition.cs | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Numerics/Distributions/Multivariate/MultivariateNormal.cs b/Numerics/Distributions/Multivariate/MultivariateNormal.cs index 78c0ea48..e3af6d7d 100644 --- a/Numerics/Distributions/Multivariate/MultivariateNormal.cs +++ b/Numerics/Distributions/Multivariate/MultivariateNormal.cs @@ -729,7 +729,17 @@ private void CreateCorrelationMatrix() } else { - var svd = new SingularValueDecomposition(m); + SingularValueDecomposition svd; + try + { + svd = new SingularValueDecomposition(m); + } + catch (ArgumentException exception) + { + // A covariance whose bidiagonal reduction does not converge fails validation + // through the same non-throwing contract as every other rejection. + if (throwException) throw; else return exception; + } if (!IsSymmetricPositiveSemiDefinite(svd, m)) { var ex = new ArgumentOutOfRangeException(nameof(Covariance), "Covariance matrix is not symmetric positive-semi-definite."); diff --git a/Numerics/Mathematics/Linear Algebra/SingularValueDecomposition.cs b/Numerics/Mathematics/Linear Algebra/SingularValueDecomposition.cs index eb15d5e0..6805ad59 100644 --- a/Numerics/Mathematics/Linear Algebra/SingularValueDecomposition.cs +++ b/Numerics/Mathematics/Linear Algebra/SingularValueDecomposition.cs @@ -383,7 +383,7 @@ private void Decompose() } break; } - if (its == 99) throw new ArgumentException("There was no convergence in 100 iterations"); + if (its == 29) throw new ArgumentException("There was no convergence in 30 iterations"); x = W[l]; nm = k - 1; y = W[nm]; From a68d71aaf649b71573291bd2b6947d03331be1bc Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 26 Aug 2026 15:00:29 -0600 Subject: [PATCH 118/222] Convert the non-convergence rejection to the validation exception type --- Numerics/Distributions/Multivariate/MultivariateNormal.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Numerics/Distributions/Multivariate/MultivariateNormal.cs b/Numerics/Distributions/Multivariate/MultivariateNormal.cs index e3af6d7d..b5dcb37d 100644 --- a/Numerics/Distributions/Multivariate/MultivariateNormal.cs +++ b/Numerics/Distributions/Multivariate/MultivariateNormal.cs @@ -737,8 +737,10 @@ private void CreateCorrelationMatrix() catch (ArgumentException exception) { // A covariance whose bidiagonal reduction does not converge fails validation - // through the same non-throwing contract as every other rejection. - if (throwException) throw; else return exception; + // through the same non-throwing contract as every other rejection, with the + // decomposition failure preserved as the inner exception. + var ex = new ArgumentOutOfRangeException("The covariance matrix decomposition did not converge.", exception); + if (throwException) throw ex; else return ex; } if (!IsSymmetricPositiveSemiDefinite(svd, m)) { From 89f294a370e7e1316cd5bfb8a52c2268421f4fa0 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 26 Aug 2026 15:05:20 -0600 Subject: [PATCH 119/222] Mix an instance counter into the clock seed and pin the canonical seeded stream --- Numerics/Sampling/MersenneTwister.cs | 11 +++++- .../Sampling/Test_MersenneTwister.cs | 38 +++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/Numerics/Sampling/MersenneTwister.cs b/Numerics/Sampling/MersenneTwister.cs index 941425b1..6147c065 100644 --- a/Numerics/Sampling/MersenneTwister.cs +++ b/Numerics/Sampling/MersenneTwister.cs @@ -69,11 +69,20 @@ public class MersenneTwister : Random /// /// Construct a Mersenne Twister PRNG using the clock to create a random seed. /// + /// + /// The seed mixes a process-wide instance counter into the tick count, so unseeded + /// generators constructed within one clock tick, sequentially or concurrently, still + /// produce distinct streams. + /// public MersenneTwister() { - Initialize((uint)DateTime.UtcNow.Ticks); + uint counter = (uint)System.Threading.Interlocked.Increment(ref _instanceCounter); + Initialize((uint)DateTime.UtcNow.Ticks + counter * 2654435761U); } + // Distinguishes unseeded generators that share one clock tick + private static int _instanceCounter; + /// /// Construct a Mersenne Twister PRNG given a seed. /// diff --git a/Test_Numerics/Sampling/Test_MersenneTwister.cs b/Test_Numerics/Sampling/Test_MersenneTwister.cs index af4e4e94..570c03da 100644 --- a/Test_Numerics/Sampling/Test_MersenneTwister.cs +++ b/Test_Numerics/Sampling/Test_MersenneTwister.cs @@ -1,5 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Sampling; +using System.Linq; +using System.Threading.Tasks; namespace Sampling { @@ -48,5 +50,41 @@ public void Test_MT19937() } } + /// + /// The array-seeded generator reproduces the canonical reference stream, pinning the + /// generator itself in place. + /// + /// + /// Reference values verified against Python numpy 2.4.2, whose legacy RandomState seeds + /// with the canonical mt19937ar init_by_array (Matsumoto and Nishimura, 2002): + /// numpy.random.RandomState([0x123, 0x234, 0x345, 0x456]) reproduces this stream. + /// + [TestMethod] + public void Test_ArraySeed_CanonicalReferenceStream() + { + var rng = new MersenneTwister(new int[] { 0x123, 0x234, 0x345, 0x456 }); + var expected = new uint[] { 1067595299U, 955945823U, 477289528U, 4107218783U, 4228976476U, 3344332714U }; + for (int i = 0; i < expected.Length; i++) + Assert.AreEqual(expected[i], rng.GenRandInt32(), $"draw {i}"); + } + + /// + /// Unseeded generators constructed back to back or concurrently produce distinct streams, + /// even inside a single clock tick. + /// + [TestMethod] + public void Test_UnseededConstruction_DistinctStreams() + { + int count = 200; + var first = new double[count]; + for (int i = 0; i < count; i++) + first[i] = new MersenneTwister().NextDouble(); + Assert.HasCount(count, first.Distinct().ToList()); + + var parallelFirst = new double[count]; + Parallel.For(0, count, i => { parallelFirst[i] = new MersenneTwister().NextDouble(); }); + Assert.HasCount(count, parallelFirst.Distinct().ToList()); + } + } } From 4f0d4f0494348a7f8f85cb506c7b1f3652e74e21 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 26 Aug 2026 16:45:18 -0600 Subject: [PATCH 120/222] Hold relative accuracy in the far normal and complement error function tails --- Numerics/Distributions/Univariate/Normal.cs | 4 +- Numerics/Mathematics/Special Functions/Erf.cs | 10 +++-- .../Test_SpecialFunctions.cs | 45 +++++++++++++++++++ 3 files changed, 54 insertions(+), 5 deletions(-) diff --git a/Numerics/Distributions/Univariate/Normal.cs b/Numerics/Distributions/Univariate/Normal.cs index 8685f415..db0ce8b7 100644 --- a/Numerics/Distributions/Univariate/Normal.cs +++ b/Numerics/Distributions/Univariate/Normal.cs @@ -401,7 +401,9 @@ public override double CDF(double x) // Validate parameters if (_parametersValid == false) ValidateParameters(Mu, Sigma, true); - return 0.5d * (1.0d + Erf.Function((x - Mu) / (Sigma * Tools.Sqrt2))); + // Evaluated through the standard normal routine, which holds relative accuracy deep in + // both tails where the error function complement form loses the probability entirely + return StandardCDF((x - Mu) / Sigma); } private static readonly double[] a = [3.3871328727963666080, 1.3314166789178437745e+2, 1.9715909503065514427e+3, 1.3731693765509461125e+4, 4.5921953931549871457e+4, 6.7265770927008700853e+4, 3.3430575583588128105e+4, 2.5090809287301226727e+3]; diff --git a/Numerics/Mathematics/Special Functions/Erf.cs b/Numerics/Mathematics/Special Functions/Erf.cs index 567eb70f..984dc29a 100644 --- a/Numerics/Mathematics/Special Functions/Erf.cs +++ b/Numerics/Mathematics/Special Functions/Erf.cs @@ -59,7 +59,9 @@ public static double Function(double X) /// public static double Erfc(double X) { - return 1d - Function(X); + // The identity erfc(x) = 2*Phi(-x*sqrt(2)) keeps relative accuracy in the far tail, + // where the complement 1 - erf(x) rounds to zero + return 2.0d * Normal.StandardCDF(-X * Tools.Sqrt2); } /// @@ -85,9 +87,9 @@ public static double InverseErf(double y) /// public static double InverseErfc(double y) { - double s = Normal.StandardZ(-0.5d * y + 1d); - double r = s * Tools.Sqrt2 / 2.0d; - return r; + // The identity erfc(x) = 2*Phi(-x*sqrt(2)) inverts through the lower tail, so a small + // argument maps to StandardZ of y/2 rather than to a probability that rounds to one + return -Normal.StandardZ(0.5d * y) / Tools.Sqrt2; } } } \ No newline at end of file diff --git a/Test_Numerics/Mathematics/Special Functions/Test_SpecialFunctions.cs b/Test_Numerics/Mathematics/Special Functions/Test_SpecialFunctions.cs index e79b1b02..bd21db82 100644 --- a/Test_Numerics/Mathematics/Special Functions/Test_SpecialFunctions.cs +++ b/Test_Numerics/Mathematics/Special Functions/Test_SpecialFunctions.cs @@ -367,5 +367,50 @@ public void Test_NextCombination_RejectsInvalidTuples() Assert.Throws(() => Factorial.NextCombination(new[] { 0, 3 }, 3)); Assert.Throws(() => Factorial.NextCombination(new[] { 0 }, -1)); } + /// + /// The error function complement and its inverse hold relative accuracy deep in the tail, + /// where the complement of the error function itself rounds to zero. + /// + /// + /// Reference values computed with Python mpmath 1.4.1 at 50 significant digits: erfc(x) + /// directly, and the inverse by root solving erfc(t) = y. Tolerances are measured relative + /// bounds of 1E-12 for the complement and 1E-9 for the inverse, which passes through the + /// AS241 quantile routine. + /// + [TestMethod] + public void Test_Erfc_FarTail() + { + Assert.AreEqual(0.15729920705028513, Erf.Erfc(1.0), 0.15729920705028513 * 1E-12); + Assert.AreEqual(2.1519736712498913E-17, Erf.Erfc(6.0), 2.1519736712498913E-17 * 1E-12); + Assert.AreEqual(2.088487583762545E-45, Erf.Erfc(10.0), 2.088487583762545E-45 * 1E-12); + Assert.AreEqual(7.212994172451207E-100, Erf.Erfc(15.0), 7.212994172451207E-100 * 1E-12); + Assert.AreEqual(1.9999779095030015, Erf.Erfc(-3.0), 1.9999779095030015 * 1E-12); + + Assert.AreEqual(0.4769362762044699, Erf.InverseErfc(0.5), Math.Abs(0.4769362762044699) * 1E-9); + Assert.AreEqual(6.062693998163568, Erf.InverseErfc(1E-17), 6.062693998163568 * 1E-9); + Assert.AreEqual(15.065574702592645, Erf.InverseErfc(1E-100), 15.065574702592645 * 1E-9); + Assert.AreEqual(-1.163087153676674, Erf.InverseErfc(1.9), Math.Abs(-1.163087153676674) * 1E-9); + } + + /// + /// The normal distribution keeps relative accuracy in the far lower tail, where the error + /// function complement form loses the probability to rounding. + /// + /// + /// Reference values computed with Python mpmath 1.4.1 ncdf at 50 significant digits. The + /// assertions demand 5E-12 relative accuracy through z = -37, near the underflow edge of + /// the double range. + /// + [TestMethod] + public void Test_NormalCDF_FarTail() + { + var d = new Numerics.Distributions.Normal(0, 1); + Assert.AreEqual(2.866515718791939E-07, d.CDF(-5.0), 2.866515718791939E-07 * 5E-12); + Assert.AreEqual(2.2323931972880437E-17, d.CDF(-8.4), 2.2323931972880437E-17 * 5E-12); + Assert.AreEqual(1.776482112077679E-33, d.CDF(-12.0), 1.776482112077679E-33 * 5E-12); + Assert.AreEqual(2.7536241186062337E-89, d.CDF(-20.0), 2.7536241186062337E-89 * 5E-12); + Assert.AreEqual(5.725571222524577E-300, d.CDF(-37.0), 5.725571222524577E-300 * 5E-12); + } + } } From 01cf4eebfd47c03e18a5ded225067ae848fdbfcf Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 26 Aug 2026 16:48:36 -0600 Subject: [PATCH 121/222] Relocate emptied clusters, validate the cluster count, and guard zero-weight components --- .../Unsupervised/GaussianMixtureModel.cs | 4 ++ .../Machine Learning/Unsupervised/KMeans.cs | 68 +++++++++++++++---- .../Machine Learning/Unsupervised/Test_GMM.cs | 19 ++++++ .../Unsupervised/Test_KMeans.cs | 38 +++++++++++ 4 files changed, 114 insertions(+), 15 deletions(-) diff --git a/Numerics/Machine Learning/Unsupervised/GaussianMixtureModel.cs b/Numerics/Machine Learning/Unsupervised/GaussianMixtureModel.cs index 9ae72ee7..dba186ba 100644 --- a/Numerics/Machine Learning/Unsupervised/GaussianMixtureModel.cs +++ b/Numerics/Machine Learning/Unsupervised/GaussianMixtureModel.cs @@ -314,6 +314,10 @@ private void MStep() for (int i = 0; i < X.NumberOfRows; i++) wgt += LikelihoodMatrix[i, k]; Weights[k] = wgt / X.NumberOfRows; + // A component with no responsibility keeps its previous parameters; updating it + // would divide by zero and spread NaN through the model + if (wgt <= 0) + continue; for (int d = 0; d < Dimension; d++) { // Compute centroids diff --git a/Numerics/Machine Learning/Unsupervised/KMeans.cs b/Numerics/Machine Learning/Unsupervised/KMeans.cs index 3486d1fb..05aa73c5 100644 --- a/Numerics/Machine Learning/Unsupervised/KMeans.cs +++ b/Numerics/Machine Learning/Unsupervised/KMeans.cs @@ -19,8 +19,8 @@ namespace Numerics.MachineLearning /// Description: /// /// - /// k-Means clustering is a method of vector quantization, originally from signal processing, - /// that aims to partition n observations into k clusters in which each observation belongs to + /// k-Means clustering is a method of vector quantization, originally from signal processing, + /// that aims to partition n observations into k clusters in which each observation belongs to /// the cluster with the nearest mean (cluster centers or cluster centroid), serving as a prototype of the cluster. /// /// @@ -42,6 +42,8 @@ public KMeans(float[] X, int k) this.K = k; this.X = new Matrix(X); Dimension = this.X.NumberOfColumns; + if (k < 1 || k > this.X.NumberOfRows) + throw new ArgumentOutOfRangeException(nameof(k), "The number of clusters must be between 1 and the number of data rows."); Means = new double[K, Dimension]; Labels = new int[this.X.NumberOfRows]; } @@ -51,11 +53,13 @@ public KMeans(float[] X, int k) /// /// The 1D array of predictor values. /// The number of clusters. - public KMeans(double[] X, int k) - { + public KMeans(double[] X, int k) + { this.K = k; this.X = new Matrix(X); Dimension = this.X.NumberOfColumns; + if (k < 1 || k > this.X.NumberOfRows) + throw new ArgumentOutOfRangeException(nameof(k), "The number of clusters must be between 1 and the number of data rows."); Means = new double[K, Dimension]; Labels = new int[this.X.NumberOfRows]; } @@ -70,6 +74,8 @@ public KMeans(double[,] X, int k) this.K = k; this.X = new Matrix(X); Dimension = this.X.NumberOfColumns; + if (k < 1 || k > this.X.NumberOfRows) + throw new ArgumentOutOfRangeException(nameof(k), "The number of clusters must be between 1 and the number of data rows."); Means = new double[K, Dimension]; Labels = new int[this.X.NumberOfRows]; } @@ -84,6 +90,8 @@ public KMeans(Matrix X, int k) this.K = k; this.X = X; Dimension = this.X.NumberOfColumns; + if (k < 1 || k > this.X.NumberOfRows) + throw new ArgumentOutOfRangeException(nameof(k), "The number of clusters must be between 1 and the number of data rows."); Means = new double[K, Dimension]; Labels = new int[this.X.NumberOfRows]; } @@ -94,7 +102,7 @@ public KMeans(Matrix X, int k) public int K { get; private set; } /// - /// The matrix of predictor values. + /// The matrix of predictor values. /// public Matrix X { get; private set; } @@ -114,7 +122,7 @@ public KMeans(Matrix X, int k) public int[] Labels { get; private set; } /// - /// The maximum iterations in the clustering algorithm. Default = 1,000. + /// The maximum iterations in the clustering algorithm. Default = 1,000. /// public int MaxIterations { get; set; } = 1000; @@ -131,7 +139,7 @@ public KMeans(Matrix X, int k) public void Train(int seed = -1, bool kMeansPlusPlus= true) { - // 1. Initialize cluster centers + // 1. Initialize cluster centers Means = Initialize(X, K, seed, kMeansPlusPlus); // 2. Optimize clusters @@ -160,7 +168,7 @@ public void Train(int seed = -1, bool kMeansPlusPlus= true) // Perform M-step // Calculate new centroids from the clusters Means = GetCentroids(Labels); - + } } @@ -179,7 +187,7 @@ public void Train(int seed = -1, bool kMeansPlusPlus= true) if (kMeansPlusPlus == false) { - + var rndIdxs = rnd.NextIntegers(0, X.NumberOfRows, k, false); Array.Sort(rndIdxs); for (int i = 0; i < k; i++) @@ -223,7 +231,7 @@ public void Train(int seed = -1, bool kMeansPlusPlus= true) // Following Accord.Net checks: // https://github.com/accord-net/framework/blob/development/Sources/Accord.MachineLearning/Clustering/KMeans/KMeans.cs - + // Note: the following checks could have been avoided if we added // a small value to each distance, but is kept as this to avoid 022 // breaking the random pattern in existing code. @@ -236,7 +244,7 @@ public void Train(int seed = -1, bool kMeansPlusPlus= true) else { // 3. Choose one new data point at random as a new center, using a weighted - // probability distribution where a point x is chosen with probability + // probability distribution where a point x is chosen with probability // proportional to D(x)^2. var u = rnd.NextDouble(); var cdf = new double[X.NumberOfRows]; @@ -250,9 +258,9 @@ public void Train(int seed = -1, bool kMeansPlusPlus= true) break; } } - + } - for (int j = 0; j < X.NumberOfColumns; j++) + for (int j = 0; j < X.NumberOfColumns; j++) centroids[c, j] = X[idx, j]; } } @@ -267,7 +275,7 @@ public void Train(int seed = -1, bool kMeansPlusPlus= true) private int[] GetLabels(double[,] centroids) { // Assign samples to the closest centroids - var labels = new int[X.NumberOfRows]; + var labels = new int[X.NumberOfRows]; Parallel.For(0, X.NumberOfRows, idx => { labels[idx] = GetClosestCentroid(X.Row(idx), centroids); }); return labels; } @@ -293,7 +301,37 @@ private int[] GetLabels(double[,] centroids) for (int k = 0; k < K; k++) for (int j = 0; j < Dimension; j++) centroids[k, j] /= count[k] > 0 ? count[k] : 1; - + + // Relocate each empty cluster to the point farthest from its assigned centroid, one + // distinct point per empty cluster, matching the scikit-learn treatment. An empty + // cluster otherwise sits at the origin, which is not a property of the data. + var consumed = new bool[X.NumberOfRows]; + for (int k = 0; k < K; k++) + { + if (count[k] > 0) + continue; + int farthest = -1; + double maxDistance = -1; + for (int i = 0; i < X.NumberOfRows; i++) + { + if (consumed[i]) + continue; + double distance = 0; + for (int j = 0; j < Dimension; j++) + distance += Tools.Sqr(X[i, j] - centroids[labels[i], j]); + if (distance > maxDistance) + { + maxDistance = distance; + farthest = i; + } + } + if (farthest < 0) + continue; + consumed[farthest] = true; + for (int j = 0; j < Dimension; j++) + centroids[k, j] = X[farthest, j]; + } + return centroids; } diff --git a/Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs b/Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs index 71a99aae..81902825 100644 --- a/Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs +++ b/Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs @@ -79,5 +79,24 @@ public void Test_GMM_LogLikelihood_AtIterationCap() Assert.IsLessThan(0, gmm.LogLikelihood, "the iteration-capped run must report its final evaluated log-likelihood"); Assert.IsFalse(double.IsNaN(gmm.LogLikelihood) || double.IsInfinity(gmm.LogLikelihood)); } + /// + /// A mixture initialized from a degenerate clustering keeps finite parameters: a component + /// that receives no responsibility retains its previous state instead of spreading NaN. + /// + [TestMethod] + public void Test_GMM_DegenerateFixture_StaysFinite() + { + var x = new double[12]; + var y = new double[12]; + for (int i = 6; i < 12; i++) { x[i] = 10; y[i] = 10; } + var gmm = new GaussianMixtureModel(new Matrix(new List { x, y }), 3); + gmm.Train(12345); + + Assert.IsFalse(double.IsNaN(gmm.LogLikelihood)); + for (int k = 0; k < 3; k++) + for (int d = 0; d < 2; d++) + Assert.IsFalse(double.IsNaN(gmm.Means[k, d]), $"mean [{k},{d}] is NaN"); + } + } } diff --git a/Test_Numerics/Machine Learning/Unsupervised/Test_KMeans.cs b/Test_Numerics/Machine Learning/Unsupervised/Test_KMeans.cs index f346b6aa..7654a5e3 100644 --- a/Test_Numerics/Machine Learning/Unsupervised/Test_KMeans.cs +++ b/Test_Numerics/Machine Learning/Unsupervised/Test_KMeans.cs @@ -51,5 +51,43 @@ public void Test_KMeans_Iris() } } + /// + /// A cluster emptied during training relocates to the point farthest from its assigned + /// centroid, so a degenerate fixture with fewer distinct points than clusters converges to + /// finite centroids and zero inertia. + /// + /// + /// Reference behavior verified against Python scikit-learn 1.8.0 KMeans(3) on the same + /// fixture: finite centers and an inertia of exactly zero, since every point coincides + /// with a centroid at convergence. + /// + [TestMethod] + public void Test_KMeans_EmptyClusterRelocation() + { + var rows = new double[12, 2]; + for (int i = 6; i < 12; i++) { rows[i, 0] = 10; rows[i, 1] = 10; } + var km = new KMeans(rows, 3); + km.Train(12345); + + double inertia = 0; + for (int i = 0; i < 12; i++) + { + Assert.IsFalse(double.IsNaN(km.Means[km.Labels[i], 0]) || double.IsNaN(km.Means[km.Labels[i], 1])); + inertia += Math.Pow(rows[i, 0] - km.Means[km.Labels[i], 0], 2) + Math.Pow(rows[i, 1] - km.Means[km.Labels[i], 1], 2); + } + Assert.AreEqual(0.0, inertia); + } + + /// + /// The cluster count is validated against the data size. + /// + [TestMethod] + public void Test_KMeans_ClusterCountValidation() + { + var rows = new double[12, 2]; + Assert.ThrowsExactly(() => new KMeans(rows, 0)); + Assert.ThrowsExactly(() => new KMeans(rows, 13)); + } + } } From 894dbc36b4ef416b3a96d4fa100fc64511d7e3ba Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Thu, 27 Aug 2026 11:47:13 -0600 Subject: [PATCH 122/222] Add a globally adaptive two-dimensional Gauss-Kronrod integrator Each rectangle is evaluated with the G10K21 tensor product (441 points, the same verified constants as the one-dimensional rule) whose embedded 10 x 10 Gauss product supplies the error estimate. Refinement is global rather than recursive: live regions sit on a max-heap keyed by error, the worst region is bisected along the axis with the larger mixed-product error indicator (read from the same function values at no extra cost), and integration stops when the summed region errors meet the absolute or relative tolerance or a budget is exhausted. Depth exhaustion without convergence reports MaximumIterationsReached rather than silent success. All product weights are positive, so the final regions form a composite rule whose node weights partition the domain area exactly; an optional (x, y, weight, f) recorder exposes that composite rule and is proven not to perturb the computed result. The test battery runs both two-dimensional integrators through the same suite: the four existing fixtures mirrored, polynomial exactness at the rule degree, separable products against two one-dimensional Gauss-Kronrod passes, all six Genz families against closed forms added to the shared integrand helpers, error-estimate honesty at k = 1, minimum-depth forcing, budget and depth status reporting, constructor guards (pinning the existing Simpson bound-check exception types as-is), degenerate machine-width domains, recorder mass and result identities, and bit-level repeat-run determinism. --- .../Integration/AdaptiveGaussKronrod2D.cs | 559 ++++++++++++++++++ .../Mathematics/Integration/Integrands.cs | 170 ++++++ .../Test_AdaptiveGaussKronrod2D.cs | 377 ++++++++++++ .../Test_AdaptiveSimpsonsRule2D.cs | 190 ++++++ 4 files changed, 1296 insertions(+) create mode 100644 Numerics/Mathematics/Integration/AdaptiveGaussKronrod2D.cs create mode 100644 Test_Numerics/Mathematics/Integration/Test_AdaptiveGaussKronrod2D.cs diff --git a/Numerics/Mathematics/Integration/AdaptiveGaussKronrod2D.cs b/Numerics/Mathematics/Integration/AdaptiveGaussKronrod2D.cs new file mode 100644 index 00000000..643ccce9 --- /dev/null +++ b/Numerics/Mathematics/Integration/AdaptiveGaussKronrod2D.cs @@ -0,0 +1,559 @@ +namespace Numerics.Mathematics.Integration +{ + /// + /// A class that performs globally adaptive Gauss-Kronrod integration over a two-dimensional rectangle. + /// + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + /// Description: + /// Each rectangular region is evaluated with the tensor product of the 10-point Gauss, 21-point + /// Kronrod rule (G10K21) on both axes: the 21 x 21 Kronrod product supplies the region estimate, + /// and the embedded 10 x 10 Gauss product - read from the same 441 function values - supplies the + /// error estimate as the absolute difference between the two. Refinement is global rather than + /// recursive: live regions are kept on a max-heap keyed by error, the worst region is bisected at + /// its midpoint along the axis with the larger one-axis error indicator (the mixed Gauss-Kronrod + /// products, also read from the same function values), and integration stops when the sum of + /// region errors meets the absolute or relative tolerance, or a budget is exhausted. All product + /// Kronrod weights are positive, so the accepted regions form a composite rule whose node weights + /// partition the domain area exactly; the optional exposes that composite + /// rule for callers that account for integration mass. + /// + /// References: + /// + /// + /// Piessens, R., et al. (1983). QUADPACK: A Subroutine Package for Automatic Integration. Springer-Verlag. + /// (The global error-directed refinement strategy follows the QAG pattern.) + /// + /// + [Serializable] + public class AdaptiveGaussKronrod2D : Integrator + { + /// + /// Constructs a new globally adaptive two-dimensional Gauss-Kronrod rule. + /// + /// The two-dimensional function to integrate. + /// The minimum x-value under which the integral must be computed. + /// The maximum x-value under which the integral must be computed. + /// The minimum y-value under which the integral must be computed. + /// The maximum y-value under which the integral must be computed. + /// Thrown when the function is null. + /// + /// Thrown when a bound is not finite, or when a maximum bound is less than or equal to its minimum. + /// + public AdaptiveGaussKronrod2D(Func function, double minX, double maxX, double minY, double maxY) + { + Function = function ?? throw new ArgumentNullException(nameof(function), "The function cannot be null."); + if (!Tools.IsFinite(minX)) throw new ArgumentOutOfRangeException(nameof(minX), "The minimum x-value must be finite."); + if (!Tools.IsFinite(maxX)) throw new ArgumentOutOfRangeException(nameof(maxX), "The maximum x-value must be finite."); + if (!Tools.IsFinite(minY)) throw new ArgumentOutOfRangeException(nameof(minY), "The minimum y-value must be finite."); + if (!Tools.IsFinite(maxY)) throw new ArgumentOutOfRangeException(nameof(maxY), "The maximum y-value must be finite."); + if (maxX <= minX) throw new ArgumentOutOfRangeException(nameof(maxX), "The maximum x-value cannot be less than or equal to the minimum x-value."); + if (maxY <= minY) throw new ArgumentOutOfRangeException(nameof(maxY), "The maximum y-value cannot be less than or equal to the minimum y-value."); + ax = minX; + bx = maxX; + ay = minY; + by = maxY; + } + + private double ax, bx, ay, by; + + // Gauss-Kronrod G10K21 positive abscissas and weights on [-1, 1], identical constants to the + // one-dimensional AdaptiveGaussKronrod rule. Kronrod indices 1, 3, 5, 7, 9 are the Gauss nodes. + private static readonly double[] xKronrod = new double[] + { + 0.995657163025808080735527280689003, + 0.973906528517171720077964012084452, + 0.930157491355708226001207180059508, + 0.865063366688984510732096688423493, + 0.780817726586416897063717578345042, + 0.679409568299024406234327365114874, + 0.562757134668604683339000099272694, + 0.433395394129247190799265943165784, + 0.294392862701460198131126603103866, + 0.148874338981631210884826001129720, + 0.000000000000000000000000000000000 + }; + + private static readonly double[] wKronrod = new double[] + { + 0.011694638867371874278064396062192, + 0.032558162307964727478818972459390, + 0.054755896574351996031381300244580, + 0.075039674810919952767043140916190, + 0.093125454583697605535065465083366, + 0.109387158802297641899210590325805, + 0.123491976262065851077958109831074, + 0.134709217311473325928054001771707, + 0.142775938577060080797094273138717, + 0.147739104901338491374841515972068, + 0.149445554002916905664936468389821 + }; + + private static readonly double[] wGauss = new double[] + { + 0.066671344308688137593568809893332, + 0.149451349150580593145776339657697, + 0.219086362515982043995534934228163, + 0.269266719309996355091226921569469, + 0.295524224714752870173892994651338 + }; + + /// + /// The 21 signed node abscissas on [-1, 1] in ascending order. + /// + private static readonly double[] Nodes = BuildNodes(); + + /// + /// The Kronrod weights aligned with . + /// + private static readonly double[] WeightsKronrod = BuildWeights(false); + + /// + /// The Gauss weights aligned with ; zero at non-Gauss nodes. + /// + private static readonly double[] WeightsGauss = BuildWeights(true); + + /// + /// Builds the ascending signed node layout from the positive-abscissa table. + /// + /// The 21 signed abscissas in ascending order. + private static double[] BuildNodes() + { + var nodes = new double[21]; + for (int i = 0; i < 10; i++) + { + nodes[i] = -xKronrod[i]; + nodes[20 - i] = xKronrod[i]; + } + nodes[10] = 0d; + return nodes; + } + + /// + /// Builds a weight vector aligned with the ascending node layout. + /// + /// True for the embedded Gauss weights (zero off the Gauss nodes); false for the Kronrod weights. + /// The 21 aligned weights. + private static double[] BuildWeights(bool gauss) + { + var weights = new double[21]; + for (int p = 0; p < 21; p++) + { + int i = p <= 10 ? p : 20 - p; + if (gauss) + { + // Gauss membership: odd indices of the positive-abscissa Kronrod table. + weights[p] = i % 2 == 1 ? wGauss[i / 2] : 0d; + } + else + { + weights[p] = wKronrod[i]; + } + } + return weights; + } + + /// + /// A live or frozen rectangular region of the composite rule. + /// + [Serializable] + private sealed class Region + { + /// The region bounds. + public double Ax, Bx, Ay, By; + + /// The tensor Kronrod estimate over the region. + public double Kronrod; + + /// The region error estimate |K - G|. + public double Error; + + /// The x-axis error indicator |K - (Gx tensor Ky)|. + public double ErrorX; + + /// The y-axis error indicator |K - (Kx tensor Gy)|. + public double ErrorY; + + /// The bisection depth of the region (the root is zero). + public int Depth; + + /// True once the region has been replaced by its children. + public bool Split; + + /// True once the region can no longer be refined (depth or width exhausted). + public bool Frozen; + + /// The 21 x-node abscissas; allocated only when a recorder is attached. + public double[]? XNodes; + + /// The 21 y-node abscissas; allocated only when a recorder is attached. + public double[]? YNodes; + + /// The 441 function values in row-major (x, y) order; allocated only when a recorder is attached. + public double[]? Values; + } + + /// + /// The two-dimensional function to integrate. + /// + public Func Function { get; } + + /// + /// The minimum x-value under which the integral must be computed. + /// + public double MinX => ax; + + /// + /// The maximum x-value under which the integral must be computed. + /// + public double MaxX => bx; + + /// + /// The minimum y-value under which the integral must be computed. + /// + public double MinY => ay; + + /// + /// The maximum y-value under which the integral must be computed. + /// + public double MaxY => by; + + /// + /// The minimum bisection depth. Every region is refined to at least this depth before + /// convergence may be declared. Default = 0. + /// + public int MinDepth { get; set; } = 0; + + /// + /// The maximum bisection depth of any region. A region at this depth is accepted with its + /// current error estimate. Default = 100. + /// + public int MaxDepth { get; set; } = 100; + + /// + /// Returns the global error bound of the integration: the sum of the absolute Kronrod-Gauss + /// differences over the final regions. + /// + /// + /// This is the quantity tested against the tolerances, following the QUADPACK global + /// strategy. It is a conservative bound rather than the root-sum-square the one-dimensional + /// rule reports, so equal names do not imply equal scales across the two classes. + /// + public double StandardError { get; private set; } + + /// + /// Gets or sets a callback invoked as (x, y, weight, f(x, y)) for the quadrature nodes of the + /// final composite rule. A null callback disables recording. + /// + /// + /// The callback is snapshotted when integration begins and flushed once at completion over the + /// regions that make up the final composite rule; nodes of subdivided parent regions are never + /// reported. Each weight carries the tensor Kronrod weight scaled by the region half-lengths, + /// so the reported weights sum to the domain area and the weighted function values reproduce + /// up to floating-point reassociation. Nothing is reported + /// when integration fails. Node capture allocates three arrays per region, so leave the + /// recorder null when the composite rule is not needed. + /// + public Action? Recorder { get; set; } + + /// + /// + /// reports when the + /// error bound meets a tolerance, + /// when the evaluation budget stops refinement first, and + /// when every region has reached + /// (or a machine-epsilon width) without meeting a tolerance - the + /// depth-exhausted result is returned rather than silently reported as converged. The + /// computation is sequential and deterministic: identical inputs produce bit-identical + /// results. + /// + public override void Integrate() + { + StandardError = 0; + ClearResults(); + Validate(); + if (MinDepth < 0) throw new ArgumentOutOfRangeException(nameof(MinDepth), "The minimum depth cannot be negative."); + if (MaxDepth < MinDepth) throw new ArgumentOutOfRangeException(nameof(MaxDepth), "The maximum depth cannot be less than the minimum depth."); + Action? recorder = Recorder; + bool capture = recorder != null; + + try + { + // All regions ever created, in creation order. Split parents remain in the list but are + // excluded from the final sums; final sums iterate this list so the floating-point + // association order is independent of the heap's internal layout. + var regions = new List { Evaluate(ax, bx, ay, by, 0, capture) }; + + // Max-heap of refinable region indices keyed by error estimate. + var heap = new List<(int Index, double Error)>(); + Push(heap, (0, regions[0].Error)); + + // Running sums drive the convergence test; the reported result and error bound are + // re-summed over the final regions below. + double resultSum = regions[0].Kronrod; + double errorSum = regions[0].Error; + + // Refine every region to the minimum depth before convergence may be declared. + for (int i = 0; i < regions.Count; i++) + { + var region = regions[i]; + if (region.Split || region.Frozen || region.Depth >= MinDepth) continue; + if (FunctionEvaluations >= MaxFunctionEvaluations) break; + SplitRegion(regions, heap, region, capture, ref resultSum, ref errorSum); + } + + var status = IntegrationStatus.Success; + while (true) + { + double tolerance = Math.Max(AbsoluteTolerance, RelativeTolerance * Math.Abs(resultSum)); + if (FunctionEvaluations >= MinFunctionEvaluations && errorSum <= tolerance) + { + status = IntegrationStatus.Success; + break; + } + if (FunctionEvaluations >= MaxFunctionEvaluations) + { + status = IntegrationStatus.MaximumFunctionEvaluationsReached; + break; + } + if (heap.Count == 0) + { + // Every region is depth- or width-exhausted and the tolerance is unmet. + status = IntegrationStatus.MaximumIterationsReached; + break; + } + + var (index, _) = Pop(heap); + var worst = regions[index]; + // Lazy deletion: entries whose region was split during the minimum-depth pass (or + // frozen) are stale - the children carry their own entries. + if (worst.Split || worst.Frozen) continue; + if (worst.Depth >= MaxDepth) + { + worst.Frozen = true; + continue; + } + SplitRegion(regions, heap, worst, capture, ref resultSum, ref errorSum); + } + + // Re-sum the final composite rule in creation order. + double result = 0, error = 0; + for (int i = 0; i < regions.Count; i++) + { + var region = regions[i]; + if (region.Split) continue; + result += region.Kronrod; + error += region.Error; + } + Result = result; + StandardError = error; + Status = status; + + if (recorder != null) + { + FlushRecorder(regions, recorder); + } + } + catch (Exception) + { + Status = IntegrationStatus.Failure; + if (ReportFailure) throw; + } + } + + /// + /// Bisects a region along its dominant-error axis, replacing it with two evaluated children. + /// A region whose split axes are both at machine-epsilon width is frozen instead. + /// + /// The region list; the children are appended. + /// The refinable-region heap; the children are pushed. + /// The region to bisect. + /// True to capture nodes and values for the recorder. + /// The running result sum, updated in place. + /// The running error sum, updated in place. + private void SplitRegion(List regions, List<(int Index, double Error)> heap, Region region, + bool capture, ref double resultSum, ref double errorSum) + { + // Prefer the axis with the larger one-axis error indicator; fall back to the other when + // the preferred axis has collapsed to machine width. + bool splitX = region.ErrorX >= region.ErrorY; + bool xTooNarrow = Math.Abs(region.Bx - region.Ax) <= Tools.DoubleMachineEpsilon; + bool yTooNarrow = Math.Abs(region.By - region.Ay) <= Tools.DoubleMachineEpsilon; + if (splitX && xTooNarrow) splitX = false; + if (!splitX && yTooNarrow) + { + if (xTooNarrow) + { + region.Frozen = true; + return; + } + splitX = true; + } + + Region left, right; + if (splitX) + { + double mx = 0.5 * (region.Ax + region.Bx); + left = Evaluate(region.Ax, mx, region.Ay, region.By, region.Depth + 1, capture); + right = Evaluate(mx, region.Bx, region.Ay, region.By, region.Depth + 1, capture); + } + else + { + double my = 0.5 * (region.Ay + region.By); + left = Evaluate(region.Ax, region.Bx, region.Ay, my, region.Depth + 1, capture); + right = Evaluate(region.Ax, region.Bx, my, region.By, region.Depth + 1, capture); + } + + region.Split = true; + region.XNodes = null; + region.YNodes = null; + region.Values = null; + resultSum += left.Kronrod + right.Kronrod - region.Kronrod; + errorSum += left.Error + right.Error - region.Error; + Iterations++; + + regions.Add(left); + Push(heap, (regions.Count - 1, left.Error)); + regions.Add(right); + Push(heap, (regions.Count - 1, right.Error)); + } + + /// + /// Evaluates the G10K21 tensor product over a rectangle, returning the region record with its + /// Kronrod estimate, error estimate, and the two one-axis error indicators. + /// + /// The lower x-bound of the region. + /// The upper x-bound of the region. + /// The lower y-bound of the region. + /// The upper y-bound of the region. + /// The bisection depth of the region. + /// True to retain nodes and values for the recorder. + /// The evaluated region. + private Region Evaluate(double rax, double rbx, double ray, double rby, int depth, bool capture) + { + double cx = 0.5 * (rax + rbx); + double hx = 0.5 * (rbx - rax); + double cy = 0.5 * (ray + rby); + double hy = 0.5 * (rby - ray); + + var xs = new double[21]; + var ys = new double[21]; + for (int i = 0; i < 21; i++) + { + xs[i] = cx + hx * Nodes[i]; + ys[i] = cy + hy * Nodes[i]; + } + double[]? values = capture ? new double[441] : null; + + // Accumulate the four weight products in one sweep: Kronrod-Kronrod (the estimate), + // Gauss-Gauss (the error pair), and the two mixed products (the axis indicators). + double sumKK = 0, sumGG = 0, sumGxKy = 0, sumKxGy = 0; + for (int i = 0; i < 21; i++) + { + double rowK = 0, rowG = 0; + double x = xs[i]; + for (int j = 0; j < 21; j++) + { + double f = Function(x, ys[j]); + if (values != null) values[i * 21 + j] = f; + rowK += WeightsKronrod[j] * f; + rowG += WeightsGauss[j] * f; + } + sumKK += WeightsKronrod[i] * rowK; + sumKxGy += WeightsKronrod[i] * rowG; + sumGxKy += WeightsGauss[i] * rowK; + sumGG += WeightsGauss[i] * rowG; + } + FunctionEvaluations += 441; + + double scale = hx * hy; + double kronrod = sumKK * scale; + var region = new Region + { + Ax = rax, + Bx = rbx, + Ay = ray, + By = rby, + Kronrod = kronrod, + Error = Math.Abs(kronrod - sumGG * scale), + ErrorX = Math.Abs(kronrod - sumGxKy * scale), + ErrorY = Math.Abs(kronrod - sumKxGy * scale), + Depth = depth, + XNodes = capture ? xs : null, + YNodes = capture ? ys : null, + Values = values + }; + return region; + } + + /// + /// Reports the final composite rule to the recorder in region-creation order. + /// + /// The region list. + /// The recorder snapshot. + private static void FlushRecorder(List regions, Action recorder) + { + for (int r = 0; r < regions.Count; r++) + { + var region = regions[r]; + if (region.Split || region.XNodes == null || region.YNodes == null || region.Values == null) continue; + double scale = 0.25 * (region.Bx - region.Ax) * (region.By - region.Ay); + for (int i = 0; i < 21; i++) + { + double wx = WeightsKronrod[i]; + for (int j = 0; j < 21; j++) + { + recorder(region.XNodes[i], region.YNodes[j], wx * WeightsKronrod[j] * scale, region.Values[i * 21 + j]); + } + } + } + } + + /// + /// Pushes an entry onto the max-heap. + /// + /// The heap storage. + /// The region index and its error key. + private static void Push(List<(int Index, double Error)> heap, (int Index, double Error) entry) + { + heap.Add(entry); + int child = heap.Count - 1; + while (child > 0) + { + int parent = (child - 1) / 2; + if (heap[parent].Error >= heap[child].Error) break; + (heap[parent], heap[child]) = (heap[child], heap[parent]); + child = parent; + } + } + + /// + /// Pops the maximum-error entry from the heap. + /// + /// The heap storage. + /// The region index and its error key. + private static (int Index, double Error) Pop(List<(int Index, double Error)> heap) + { + var top = heap[0]; + int last = heap.Count - 1; + heap[0] = heap[last]; + heap.RemoveAt(last); + int parent = 0; + while (true) + { + int left = 2 * parent + 1; + if (left >= heap.Count) break; + int right = left + 1; + int larger = right < heap.Count && heap[right].Error > heap[left].Error ? right : left; + if (heap[parent].Error >= heap[larger].Error) break; + (heap[parent], heap[larger]) = (heap[larger], heap[parent]); + parent = larger; + } + return top; + } + } +} diff --git a/Test_Numerics/Mathematics/Integration/Integrands.cs b/Test_Numerics/Mathematics/Integration/Integrands.cs index addec3c2..e3331513 100644 --- a/Test_Numerics/Mathematics/Integration/Integrands.cs +++ b/Test_Numerics/Mathematics/Integration/Integrands.cs @@ -119,5 +119,175 @@ public static double SumOfNormals2D(double p1, double p2) return result; } + // The six Genz test families in two dimensions over the unit square [0,1]^2, each with its + // exact integral in closed form. Reference: Genz, A. (1984). "Testing multidimensional + // integration routines." Tools, Methods and Languages for Scientific and Engineering + // Computation, 81-94. + + /// + /// Genz oscillatory family: cos(2*pi*w1 + c1*x + c2*y). + /// + /// The x-coordinate. + /// The y-coordinate. + /// The x-direction difficulty parameter. + /// The y-direction difficulty parameter. + /// The phase shift parameter. + public static double GenzOscillatory(double x, double y, double c1, double c2, double w1) + { + return Math.Cos(2d * Math.PI * w1 + c1 * x + c2 * y); + } + + /// + /// Exact integral of the Genz oscillatory family over the unit square: + /// (4/(c1*c2)) * sin(c1/2) * sin(c2/2) * cos(2*pi*w1 + c1/2 + c2/2). + /// + /// The x-direction difficulty parameter. + /// The y-direction difficulty parameter. + /// The phase shift parameter. + public static double GenzOscillatoryExact(double c1, double c2, double w1) + { + return 4d / (c1 * c2) * Math.Sin(c1 / 2d) * Math.Sin(c2 / 2d) * Math.Cos(2d * Math.PI * w1 + c1 / 2d + c2 / 2d); + } + + /// + /// Genz product-peak family: 1 / ((c1^-2 + (x-w1)^2) * (c2^-2 + (y-w2)^2)). + /// + /// The x-coordinate. + /// The y-coordinate. + /// The x-direction difficulty parameter. + /// The y-direction difficulty parameter. + /// The x-location of the peak. + /// The y-location of the peak. + public static double GenzProductPeak(double x, double y, double c1, double c2, double w1, double w2) + { + return 1d / ((1d / (c1 * c1) + (x - w1) * (x - w1)) * (1d / (c2 * c2) + (y - w2) * (y - w2))); + } + + /// + /// Exact integral of the Genz product-peak family over the unit square: the product of + /// c * (atan(c*(1-w)) + atan(c*w)) over both axes. + /// + /// The x-direction difficulty parameter. + /// The y-direction difficulty parameter. + /// The x-location of the peak. + /// The y-location of the peak. + public static double GenzProductPeakExact(double c1, double c2, double w1, double w2) + { + double ix = c1 * (Math.Atan(c1 * (1d - w1)) + Math.Atan(c1 * w1)); + double iy = c2 * (Math.Atan(c2 * (1d - w2)) + Math.Atan(c2 * w2)); + return ix * iy; + } + + /// + /// Genz corner-peak family: (1 + c1*x + c2*y)^-3. + /// + /// The x-coordinate. + /// The y-coordinate. + /// The x-direction difficulty parameter. + /// The y-direction difficulty parameter. + public static double GenzCornerPeak(double x, double y, double c1, double c2) + { + double b = 1d + c1 * x + c2 * y; + return 1d / (b * b * b); + } + + /// + /// Exact integral of the Genz corner-peak family over the unit square: + /// (1/(2*c1*c2)) * (1 - 1/(1+c1) - 1/(1+c2) + 1/(1+c1+c2)). + /// + /// The x-direction difficulty parameter. + /// The y-direction difficulty parameter. + public static double GenzCornerPeakExact(double c1, double c2) + { + return 1d / (2d * c1 * c2) * (1d - 1d / (1d + c1) - 1d / (1d + c2) + 1d / (1d + c1 + c2)); + } + + /// + /// Genz Gaussian family: exp(-c1^2*(x-w1)^2 - c2^2*(y-w2)^2). + /// + /// The x-coordinate. + /// The y-coordinate. + /// The x-direction difficulty parameter. + /// The y-direction difficulty parameter. + /// The x-location of the peak. + /// The y-location of the peak. + public static double GenzGaussian(double x, double y, double c1, double c2, double w1, double w2) + { + return Math.Exp(-c1 * c1 * (x - w1) * (x - w1) - c2 * c2 * (y - w2) * (y - w2)); + } + + /// + /// Exact integral of the Genz Gaussian family over the unit square: the product of + /// sqrt(pi)/(2c) * (erf(c*(1-w)) + erf(c*w)) over both axes. + /// + /// The x-direction difficulty parameter. + /// The y-direction difficulty parameter. + /// The x-location of the peak. + /// The y-location of the peak. + public static double GenzGaussianExact(double c1, double c2, double w1, double w2) + { + double ix = Math.Sqrt(Math.PI) / (2d * c1) * (Numerics.Mathematics.SpecialFunctions.Erf.Function(c1 * (1d - w1)) + Numerics.Mathematics.SpecialFunctions.Erf.Function(c1 * w1)); + double iy = Math.Sqrt(Math.PI) / (2d * c2) * (Numerics.Mathematics.SpecialFunctions.Erf.Function(c2 * (1d - w2)) + Numerics.Mathematics.SpecialFunctions.Erf.Function(c2 * w2)); + return ix * iy; + } + + /// + /// Genz continuous (C0) family: exp(-c1*|x-w1| - c2*|y-w2|). Continuous with a gradient + /// discontinuity along both peak lines. + /// + /// The x-coordinate. + /// The y-coordinate. + /// The x-direction difficulty parameter. + /// The y-direction difficulty parameter. + /// The x-location of the ridge. + /// The y-location of the ridge. + public static double GenzContinuous(double x, double y, double c1, double c2, double w1, double w2) + { + return Math.Exp(-c1 * Math.Abs(x - w1) - c2 * Math.Abs(y - w2)); + } + + /// + /// Exact integral of the Genz continuous family over the unit square: the product of + /// (2 - exp(-c*w) - exp(-c*(1-w)))/c over both axes. + /// + /// The x-direction difficulty parameter. + /// The y-direction difficulty parameter. + /// The x-location of the ridge. + /// The y-location of the ridge. + public static double GenzContinuousExact(double c1, double c2, double w1, double w2) + { + double ix = (2d - Math.Exp(-c1 * w1) - Math.Exp(-c1 * (1d - w1))) / c1; + double iy = (2d - Math.Exp(-c2 * w2) - Math.Exp(-c2 * (1d - w2))) / c2; + return ix * iy; + } + + /// + /// Genz discontinuous family: exp(c1*x + c2*y) inside the corner box [0,w1]x[0,w2] and zero + /// outside it. + /// + /// The x-coordinate. + /// The y-coordinate. + /// The x-direction difficulty parameter. + /// The y-direction difficulty parameter. + /// The x-location of the discontinuity. + /// The y-location of the discontinuity. + public static double GenzDiscontinuous(double x, double y, double c1, double c2, double w1, double w2) + { + return x > w1 || y > w2 ? 0d : Math.Exp(c1 * x + c2 * y); + } + + /// + /// Exact integral of the Genz discontinuous family over the unit square: + /// ((exp(c1*w1)-1)/c1) * ((exp(c2*w2)-1)/c2). + /// + /// The x-direction difficulty parameter. + /// The y-direction difficulty parameter. + /// The x-location of the discontinuity. + /// The y-location of the discontinuity. + public static double GenzDiscontinuousExact(double c1, double c2, double w1, double w2) + { + return (Math.Exp(c1 * w1) - 1d) / c1 * ((Math.Exp(c2 * w2) - 1d) / c2); + } + } } diff --git a/Test_Numerics/Mathematics/Integration/Test_AdaptiveGaussKronrod2D.cs b/Test_Numerics/Mathematics/Integration/Test_AdaptiveGaussKronrod2D.cs new file mode 100644 index 00000000..0b6ce754 --- /dev/null +++ b/Test_Numerics/Mathematics/Integration/Test_AdaptiveGaussKronrod2D.cs @@ -0,0 +1,377 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics; +using Numerics.Mathematics; +using Numerics.Mathematics.Integration; +using System; + +namespace Mathematics.Integration +{ + /// + /// Unit tests for the two-dimensional globally adaptive Gauss-Kronrod integration method. + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + [TestClass] + public class Test_AdaptiveGaussKronrod2D + { + + /// + /// Test the 2D Adaptive Gauss-Kronrod algorithm with the Pi function. The disc indicator is + /// discontinuous along the circle, so the run is expected to stop on the evaluation budget + /// rather than the tolerance; the estimate must still be accurate. + /// + [TestMethod] + public void Test_PI() + { + var agk2D = new AdaptiveGaussKronrod2D(Integrands.PI2D, -1, 1, -1, 1); + agk2D.Integrate(); + var result = agk2D.Result; + double trueResult = 3.14; + Assert.AreEqual(trueResult, result, 1E-3 * trueResult); + } + + /// + /// Test the 2D Adaptive Gauss-Kronrod algorithm with the sum of two normal distributions. + /// + [TestMethod] + public void Test_SumOfTwoNormals() + { + var agk2D = new AdaptiveGaussKronrod2D(Integrands.SumOfNormals2D, 1E-15, 1 - 1E-15, 1E-15, 1 - 1E-15); + agk2D.Integrate(); + var result = agk2D.Result; + double trueResult = 40; + Assert.AreEqual(trueResult, result, 1E-3 * trueResult); + } + + /// + /// Test the 2D Adaptive Gauss-Kronrod algorithm with the X + Y integrand. + /// + [TestMethod] + public void Test_XPlusY() + { + Func func = (x, y) => x + y; + var agk2D = new AdaptiveGaussKronrod2D(func, -1, 1, -1, 1); + agk2D.Integrate(); + var result = agk2D.Result; + double trueResult = 0; + Assert.AreEqual(trueResult, result, 1E-5); + } + + /// + /// Test the 2D Adaptive Gauss-Kronrod algorithm with the X^2 + Y^2 integrand. + /// + [TestMethod] + public void Test_XSquaredPlusYSquared() + { + Func func = (x, y) => x * x + y * y; + var agk2D = new AdaptiveGaussKronrod2D(func, -1, 1, -1, 1); + agk2D.Integrate(); + var result = agk2D.Result; + double trueResult = 2.6666667; + Assert.AreEqual(trueResult, result, 1E-6); + } + + /// + /// Polynomials below the rule degree integrate exactly from the root evaluation alone. + /// The G10K21 tensor product is exact for x^3*y^3 + x^5 + y^5, whose integral over + /// [0,2] x [0,1] is 4*(1/4) + (32/3)*1 + 2*(1/6) = 12. + /// + [TestMethod] + public void Test_PolynomialExactness() + { + Func func = (x, y) => x * x * x * y * y * y + Math.Pow(x, 5) + Math.Pow(y, 5); + var agk2D = new AdaptiveGaussKronrod2D(func, 0, 2, 0, 1); + agk2D.Integrate(); + Assert.AreEqual(12d, agk2D.Result, 1E-11); + Assert.AreEqual(IntegrationStatus.Success, agk2D.Status); + // Both the Kronrod and embedded Gauss products are exact, so the root converges alone. + Assert.AreEqual(441, agk2D.FunctionEvaluations); + } + + /// + /// A separable product f(x)*g(y) must match the product of two one-dimensional adaptive + /// Gauss-Kronrod passes, and the closed form (e^1.5 - 1)*sin(1). + /// + [TestMethod] + public void Test_SeparableProduct_MatchesTwo1DPasses() + { + var agk2D = new AdaptiveGaussKronrod2D((x, y) => Math.Exp(x) * Math.Cos(y), 0, 1.5, 0, 1); + agk2D.Integrate(); + + var agkX = new AdaptiveGaussKronrod(Math.Exp, 0, 1.5); + agkX.Integrate(); + var agkY = new AdaptiveGaussKronrod(Math.Cos, 0, 1); + agkY.Integrate(); + + double product = agkX.Result * agkY.Result; + double exact = (Math.Exp(1.5) - 1d) * Math.Sin(1d); + Assert.AreEqual(product, agk2D.Result, 5E-8 * Math.Abs(product)); + Assert.AreEqual(exact, agk2D.Result, 1E-7 * Math.Abs(exact)); + } + + /// + /// Genz oscillatory family, c = (5, 5), w1 = 0.3, against its closed form. + /// + [TestMethod] + public void Test_GenzOscillatory() + { + var agk2D = new AdaptiveGaussKronrod2D((x, y) => Integrands.GenzOscillatory(x, y, 5, 5, 0.3), 0, 1, 0, 1); + agk2D.Integrate(); + double exact = Integrands.GenzOscillatoryExact(5, 5, 0.3); + Assert.AreEqual(exact, agk2D.Result, 1E-6 * Math.Abs(exact)); + Assert.AreEqual(IntegrationStatus.Success, agk2D.Status); + } + + /// + /// Genz product-peak family, c = (10, 10), w = (0.4, 0.6), against its closed form. + /// + [TestMethod] + public void Test_GenzProductPeak() + { + var agk2D = new AdaptiveGaussKronrod2D((x, y) => Integrands.GenzProductPeak(x, y, 10, 10, 0.4, 0.6), 0, 1, 0, 1); + agk2D.Integrate(); + double exact = Integrands.GenzProductPeakExact(10, 10, 0.4, 0.6); + Assert.AreEqual(exact, agk2D.Result, 1E-6 * Math.Abs(exact)); + Assert.AreEqual(IntegrationStatus.Success, agk2D.Status); + } + + /// + /// Genz corner-peak family, c = (5, 5), against its closed form. + /// + [TestMethod] + public void Test_GenzCornerPeak() + { + var agk2D = new AdaptiveGaussKronrod2D((x, y) => Integrands.GenzCornerPeak(x, y, 5, 5), 0, 1, 0, 1); + agk2D.Integrate(); + double exact = Integrands.GenzCornerPeakExact(5, 5); + Assert.AreEqual(exact, agk2D.Result, 1E-6 * Math.Abs(exact)); + Assert.AreEqual(IntegrationStatus.Success, agk2D.Status); + } + + /// + /// Genz Gaussian family, c = (10, 10), w = (0.4, 0.6), against its closed form. + /// + [TestMethod] + public void Test_GenzGaussian() + { + var agk2D = new AdaptiveGaussKronrod2D((x, y) => Integrands.GenzGaussian(x, y, 10, 10, 0.4, 0.6), 0, 1, 0, 1); + agk2D.Integrate(); + double exact = Integrands.GenzGaussianExact(10, 10, 0.4, 0.6); + Assert.AreEqual(exact, agk2D.Result, 1E-6 * Math.Abs(exact)); + Assert.AreEqual(IntegrationStatus.Success, agk2D.Status); + } + + /// + /// Genz continuous (C0) family, c = (10, 10), w = (0.4, 0.6), against its closed form. + /// The gradient discontinuities along the two ridge lines slow convergence relative to the + /// smooth families but the tolerance is still met. + /// + [TestMethod] + public void Test_GenzContinuous() + { + var agk2D = new AdaptiveGaussKronrod2D((x, y) => Integrands.GenzContinuous(x, y, 10, 10, 0.4, 0.6), 0, 1, 0, 1); + agk2D.Integrate(); + double exact = Integrands.GenzContinuousExact(10, 10, 0.4, 0.6); + Assert.AreEqual(exact, agk2D.Result, 1E-5 * Math.Abs(exact)); + } + + /// + /// Genz discontinuous family, c = (5, 5), w = (0.4, 0.6), against its closed form. As with + /// the disc indicator, the jump lines defeat tolerance-level convergence, so the run stops + /// on the budget with an accurate estimate rather than reporting silent success. + /// + [TestMethod] + public void Test_GenzDiscontinuous() + { + var agk2D = new AdaptiveGaussKronrod2D((x, y) => Integrands.GenzDiscontinuous(x, y, 5, 5, 0.4, 0.6), 0, 1, 0, 1); + agk2D.Integrate(); + double exact = Integrands.GenzDiscontinuousExact(5, 5, 0.4, 0.6); + Assert.AreEqual(exact, agk2D.Result, 2E-3 * Math.Abs(exact)); + } + + /// + /// The reported standard error must bound the actual error at convergence. StandardError is + /// the sum of per-region |Kronrod - Gauss| deviations - a conservative bound - so the honesty + /// factor k = 1 with a small floating-point floor. + /// + [TestMethod] + public void Test_ErrorEstimateHonesty() + { + var gauss = new AdaptiveGaussKronrod2D((x, y) => Integrands.GenzGaussian(x, y, 10, 10, 0.4, 0.6), 0, 1, 0, 1); + gauss.Integrate(); + double exactGauss = Integrands.GenzGaussianExact(10, 10, 0.4, 0.6); + Assert.IsLessThanOrEqualTo(Math.Max(gauss.StandardError, 1E-13 * Math.Abs(exactGauss)), Math.Abs(gauss.Result - exactGauss)); + + var peak = new AdaptiveGaussKronrod2D((x, y) => Integrands.GenzProductPeak(x, y, 10, 10, 0.4, 0.6), 0, 1, 0, 1); + peak.Integrate(); + double exactPeak = Integrands.GenzProductPeakExact(10, 10, 0.4, 0.6); + Assert.IsLessThanOrEqualTo(Math.Max(peak.StandardError, 1E-13 * Math.Abs(exactPeak)), Math.Abs(peak.Result - exactPeak)); + } + + /// + /// MinDepth forces refinement before convergence may be declared: a trivially smooth + /// integrand converges at the root with MinDepth = 0 but must be pre-split at MinDepth = 2. + /// + [TestMethod] + public void Test_MinDepth_ForcesRefinement() + { + Func func = (x, y) => x + y; + var baseline = new AdaptiveGaussKronrod2D(func, 0, 1, 0, 1); + baseline.Integrate(); + Assert.AreEqual(441, baseline.FunctionEvaluations); + + var forced = new AdaptiveGaussKronrod2D(func, 0, 1, 0, 1) { MinDepth = 2 }; + forced.Integrate(); + // Root + two depth-1 children + four depth-2 children = 7 evaluated regions. + Assert.AreEqual(441 * 7, forced.FunctionEvaluations); + // The integral of x + y over the unit square is 1, from either evaluation count. + Assert.AreEqual(1d, baseline.Result, 1E-10); + Assert.AreEqual(1d, forced.Result, 1E-10); + } + + /// + /// Exhausting the evaluation budget is reported through the status rather than silently: + /// a sharp peak with a tiny budget stops with MaximumFunctionEvaluationsReached and a finite + /// estimate. + /// + [TestMethod] + public void Test_MaxFunctionEvaluations_StatusReported() + { + var agk2D = new AdaptiveGaussKronrod2D((x, y) => Integrands.GenzProductPeak(x, y, 50, 50, 0.4, 0.6), 0, 1, 0, 1) + { + MaxFunctionEvaluations = 2000 + }; + agk2D.Integrate(); + Assert.AreEqual(IntegrationStatus.MaximumFunctionEvaluationsReached, agk2D.Status); + Assert.IsTrue(Tools.IsFinite(agk2D.Result)); + } + + /// + /// Exhausting MaxDepth without meeting the tolerance is reported as + /// MaximumIterationsReached rather than silent success: with MaxDepth = 0 the unconverged + /// root cannot be refined. + /// + [TestMethod] + public void Test_MaxDepthExhaustion_StatusReported() + { + var agk2D = new AdaptiveGaussKronrod2D((x, y) => Integrands.GenzGaussian(x, y, 10, 10, 0.4, 0.6), 0, 1, 0, 1) + { + MaxDepth = 0 + }; + agk2D.Integrate(); + Assert.AreEqual(IntegrationStatus.MaximumIterationsReached, agk2D.Status); + Assert.IsTrue(Tools.IsFinite(agk2D.Result)); + Assert.AreEqual(441, agk2D.FunctionEvaluations); + } + + /// + /// A throwing integrand surfaces as Failure without throwing when ReportFailure is false, + /// and rethrows when ReportFailure is true. + /// + [TestMethod] + public void Test_ReportFailure() + { + Func throwing = (x, y) => throw new InvalidOperationException("Integrand failure."); + var silent = new AdaptiveGaussKronrod2D(throwing, 0, 1, 0, 1) { ReportFailure = false }; + silent.Integrate(); + Assert.AreEqual(IntegrationStatus.Failure, silent.Status); + + var loud = new AdaptiveGaussKronrod2D(throwing, 0, 1, 0, 1) { ReportFailure = true }; + Assert.Throws(() => loud.Integrate()); + } + + /// + /// Constructor guards: a null function and invalid or non-finite bounds throw. + /// + [TestMethod] + public void Test_Constructor_Guards() + { + Func func = (x, y) => x + y; + Assert.Throws(() => new AdaptiveGaussKronrod2D(null!, 0, 1, 0, 1)); + Assert.Throws(() => new AdaptiveGaussKronrod2D(func, 1, 1, 0, 1)); + Assert.Throws(() => new AdaptiveGaussKronrod2D(func, 1, 0, 0, 1)); + Assert.Throws(() => new AdaptiveGaussKronrod2D(func, 0, 1, 1, 1)); + Assert.Throws(() => new AdaptiveGaussKronrod2D(func, 0, 1, 1, 0)); + Assert.Throws(() => new AdaptiveGaussKronrod2D(func, double.NaN, 1, 0, 1)); + Assert.Throws(() => new AdaptiveGaussKronrod2D(func, 0, double.PositiveInfinity, 0, 1)); + } + + /// + /// Depth-setting guards: a negative MinDepth or MaxDepth below MinDepth throws at + /// integration time. + /// + [TestMethod] + public void Test_DepthGuards() + { + Func func = (x, y) => x + y; + var negative = new AdaptiveGaussKronrod2D(func, 0, 1, 0, 1) { MinDepth = -1 }; + Assert.Throws(() => negative.Integrate()); + var inverted = new AdaptiveGaussKronrod2D(func, 0, 1, 0, 1) { MinDepth = 3, MaxDepth = 2 }; + Assert.Throws(() => inverted.Integrate()); + } + + /// + /// A machine-epsilon-width domain completes successfully with an essentially zero integral, + /// including under a forced minimum depth where only the wide axis can split. + /// + [TestMethod] + public void Test_DegenerateWidth() + { + var thin = new AdaptiveGaussKronrod2D((x, y) => 1d, 0, 1E-16, 0, 1); + thin.Integrate(); + Assert.AreEqual(IntegrationStatus.Success, thin.Status); + Assert.AreEqual(1E-16, thin.Result, 1E-17); + + var forced = new AdaptiveGaussKronrod2D((x, y) => 1d, 0, 1E-16, 0, 1) { MinDepth = 2 }; + forced.Integrate(); + Assert.AreEqual(IntegrationStatus.Success, forced.Status); + Assert.AreEqual(1E-16, forced.Result, 1E-17); + } + + /// + /// The recorder reports the final composite rule: weights sum to the domain area, weighted + /// function values reproduce the result, the node count is a whole number of regions, and + /// attaching a recorder does not change the computed result. + /// + [TestMethod] + public void Test_Recorder_MassAndResultIdentities() + { + Func func = (x, y) => Integrands.GenzGaussian(x, y, 10, 10, 0.4, 0.6); + var plain = new AdaptiveGaussKronrod2D(func, 0, 1, 0, 1); + plain.Integrate(); + + double sumW = 0, sumWF = 0; + int count = 0; + var recorded = new AdaptiveGaussKronrod2D(func, 0, 1, 0, 1) + { + Recorder = (x, y, w, f) => { sumW += w; sumWF += w * f; count++; } + }; + recorded.Integrate(); + + Assert.AreEqual(BitConverter.DoubleToInt64Bits(plain.Result), BitConverter.DoubleToInt64Bits(recorded.Result)); + Assert.AreEqual(1d, sumW, 1E-12); + Assert.AreEqual(recorded.Result, sumWF, 1E-12 * Math.Abs(recorded.Result)); + Assert.AreEqual(0, count % 441); + Assert.IsGreaterThanOrEqualTo(441, count); + } + + /// + /// Identical inputs produce bit-identical results and error bounds on repeated runs. + /// + [TestMethod] + public void Test_Determinism_RepeatedRunsBitEqual() + { + Func func = (x, y) => Integrands.GenzProductPeak(x, y, 10, 10, 0.4, 0.6); + var first = new AdaptiveGaussKronrod2D(func, 0, 1, 0, 1); + first.Integrate(); + var second = new AdaptiveGaussKronrod2D(func, 0, 1, 0, 1); + second.Integrate(); + Assert.AreEqual(BitConverter.DoubleToInt64Bits(first.Result), BitConverter.DoubleToInt64Bits(second.Result)); + Assert.AreEqual(BitConverter.DoubleToInt64Bits(first.StandardError), BitConverter.DoubleToInt64Bits(second.StandardError)); + Assert.AreEqual(first.FunctionEvaluations, second.FunctionEvaluations); + } + + } +} diff --git a/Test_Numerics/Mathematics/Integration/Test_AdaptiveSimpsonsRule2D.cs b/Test_Numerics/Mathematics/Integration/Test_AdaptiveSimpsonsRule2D.cs index 4b9615a2..73a6b483 100644 --- a/Test_Numerics/Mathematics/Integration/Test_AdaptiveSimpsonsRule2D.cs +++ b/Test_Numerics/Mathematics/Integration/Test_AdaptiveSimpsonsRule2D.cs @@ -1,4 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics; +using Numerics.Mathematics; using Numerics.Mathematics.Integration; using Numerics.Sampling; using System; @@ -92,5 +94,193 @@ public void Test_XSquaredPlusYSquared() Assert.AreEqual(trueResult, result, 1E-6); } + /// + /// Polynomials at or below Simpson's cubic degree integrate exactly: the integral of + /// x^3*y^3 over [0,2] x [0,1] is 4 * (1/4) = 1. + /// + [TestMethod] + public void Test_PolynomialExactness() + { + Func func = (x, y) => x * x * x * y * y * y; + var asr2D = new AdaptiveSimpsonsRule2D(func, 0, 2, 0, 1); + asr2D.Integrate(); + Assert.AreEqual(1d, asr2D.Result, 1E-12); + Assert.AreEqual(IntegrationStatus.Success, asr2D.Status); + } + + /// + /// A separable product f(x)*g(y) must match the product of two one-dimensional adaptive + /// Gauss-Kronrod reference passes, and the closed form (e^1.5 - 1)*sin(1). + /// + [TestMethod] + public void Test_SeparableProduct_MatchesTwo1DPasses() + { + var asr2D = new AdaptiveSimpsonsRule2D((x, y) => Math.Exp(x) * Math.Cos(y), 0, 1.5, 0, 1); + asr2D.Integrate(); + + var agkX = new AdaptiveGaussKronrod(Math.Exp, 0, 1.5); + agkX.Integrate(); + var agkY = new AdaptiveGaussKronrod(Math.Cos, 0, 1); + agkY.Integrate(); + + double product = agkX.Result * agkY.Result; + double exact = (Math.Exp(1.5) - 1d) * Math.Sin(1d); + Assert.AreEqual(product, asr2D.Result, 1E-6 * Math.Abs(product)); + Assert.AreEqual(exact, asr2D.Result, 1E-6 * Math.Abs(exact)); + } + + /// + /// Genz oscillatory family, c = (5, 5), w1 = 0.3, against its closed form. + /// + [TestMethod] + public void Test_GenzOscillatory() + { + var asr2D = new AdaptiveSimpsonsRule2D((x, y) => Integrands.GenzOscillatory(x, y, 5, 5, 0.3), 0, 1, 0, 1); + asr2D.Integrate(); + double exact = Integrands.GenzOscillatoryExact(5, 5, 0.3); + Assert.AreEqual(exact, asr2D.Result, 1E-5 * Math.Abs(exact)); + } + + /// + /// Genz product-peak family, c = (10, 10), w = (0.4, 0.6), against its closed form. + /// + [TestMethod] + public void Test_GenzProductPeak() + { + var asr2D = new AdaptiveSimpsonsRule2D((x, y) => Integrands.GenzProductPeak(x, y, 10, 10, 0.4, 0.6), 0, 1, 0, 1); + asr2D.Integrate(); + double exact = Integrands.GenzProductPeakExact(10, 10, 0.4, 0.6); + Assert.AreEqual(exact, asr2D.Result, 1E-5 * Math.Abs(exact)); + } + + /// + /// Genz corner-peak family, c = (5, 5), against its closed form. + /// + [TestMethod] + public void Test_GenzCornerPeak() + { + var asr2D = new AdaptiveSimpsonsRule2D((x, y) => Integrands.GenzCornerPeak(x, y, 5, 5), 0, 1, 0, 1); + asr2D.Integrate(); + double exact = Integrands.GenzCornerPeakExact(5, 5); + Assert.AreEqual(exact, asr2D.Result, 1E-5 * Math.Abs(exact)); + } + + /// + /// Genz Gaussian family, c = (10, 10), w = (0.4, 0.6), against its closed form. + /// + [TestMethod] + public void Test_GenzGaussian() + { + var asr2D = new AdaptiveSimpsonsRule2D((x, y) => Integrands.GenzGaussian(x, y, 10, 10, 0.4, 0.6), 0, 1, 0, 1); + asr2D.Integrate(); + double exact = Integrands.GenzGaussianExact(10, 10, 0.4, 0.6); + Assert.AreEqual(exact, asr2D.Result, 1E-5 * Math.Abs(exact)); + } + + /// + /// Genz continuous (C0) family, c = (10, 10), w = (0.4, 0.6), against its closed form. + /// + [TestMethod] + public void Test_GenzContinuous() + { + var asr2D = new AdaptiveSimpsonsRule2D((x, y) => Integrands.GenzContinuous(x, y, 10, 10, 0.4, 0.6), 0, 1, 0, 1); + asr2D.Integrate(); + double exact = Integrands.GenzContinuousExact(10, 10, 0.4, 0.6); + Assert.AreEqual(exact, asr2D.Result, 1E-4 * Math.Abs(exact)); + } + + /// + /// Genz discontinuous family, c = (5, 5), w = (0.4, 0.6), against its closed form. The jump + /// lines are the hardest case for a fixed low-order rule; the tolerance pins the measured + /// accuracy of the existing algorithm. + /// + [TestMethod] + public void Test_GenzDiscontinuous() + { + var asr2D = new AdaptiveSimpsonsRule2D((x, y) => Integrands.GenzDiscontinuous(x, y, 5, 5, 0.4, 0.6), 0, 1, 0, 1); + asr2D.Integrate(); + double exact = Integrands.GenzDiscontinuousExact(5, 5, 0.4, 0.6); + Assert.AreEqual(exact, asr2D.Result, 1E-2 * Math.Abs(exact)); + } + + /// + /// The reported standard error must bound the actual error at convergence on smooth + /// integrands; the honesty factor k pins the measured behavior of the existing estimator + /// (the root-sum-square of the accepted Richardson corrections). + /// + [TestMethod] + public void Test_ErrorEstimateHonesty() + { + var gauss = new AdaptiveSimpsonsRule2D((x, y) => Integrands.GenzGaussian(x, y, 10, 10, 0.4, 0.6), 0, 1, 0, 1); + gauss.Integrate(); + double exactGauss = Integrands.GenzGaussianExact(10, 10, 0.4, 0.6); + Assert.IsLessThanOrEqualTo(Math.Max(gauss.StandardError, 1E-13 * Math.Abs(exactGauss)), Math.Abs(gauss.Result - exactGauss)); + + var peak = new AdaptiveSimpsonsRule2D((x, y) => Integrands.GenzProductPeak(x, y, 10, 10, 0.4, 0.6), 0, 1, 0, 1); + peak.Integrate(); + double exactPeak = Integrands.GenzProductPeakExact(10, 10, 0.4, 0.6); + Assert.IsLessThanOrEqualTo(Math.Max(peak.StandardError, 1E-13 * Math.Abs(exactPeak)), Math.Abs(peak.Result - exactPeak)); + } + + /// + /// MinDepth forces refinement before acceptance: a trivially smooth integrand uses more + /// function evaluations under a raised MinDepth. + /// + [TestMethod] + public void Test_MinDepth_ForcesRefinement() + { + Func func = (x, y) => x + y; + var baseline = new AdaptiveSimpsonsRule2D(func, 0, 1, 0, 1); + baseline.Integrate(); + var forced = new AdaptiveSimpsonsRule2D(func, 0, 1, 0, 1) { MinDepth = 3 }; + forced.Integrate(); + Assert.IsGreaterThan(baseline.FunctionEvaluations, forced.FunctionEvaluations); + Assert.AreEqual(1d, forced.Result, 1E-10); + } + + /// + /// Exhausting the evaluation budget is reported through the status: a sharp peak with a + /// tiny budget stops with MaximumFunctionEvaluationsReached and a finite estimate. + /// + [TestMethod] + public void Test_MaxFunctionEvaluations_StatusReported() + { + var asr2D = new AdaptiveSimpsonsRule2D((x, y) => Integrands.GenzProductPeak(x, y, 50, 50, 0.4, 0.6), 0, 1, 0, 1) + { + MaxFunctionEvaluations = 500 + }; + asr2D.Integrate(); + Assert.AreEqual(IntegrationStatus.MaximumFunctionEvaluationsReached, asr2D.Status); + Assert.IsTrue(Tools.IsFinite(asr2D.Result)); + } + + /// + /// Constructor guards, pinning the existing exception types: a null function throws + /// ArgumentNullException, and reversed bounds throw the ArgumentNullException the current + /// constructor uses (a historical quirk, pinned as-is). + /// + [TestMethod] + public void Test_Constructor_Guards() + { + Func func = (x, y) => x + y; + Assert.Throws(() => new AdaptiveSimpsonsRule2D(null!, 0, 1, 0, 1)); + Assert.Throws(() => new AdaptiveSimpsonsRule2D(func, 1, 0, 0, 1)); + Assert.Throws(() => new AdaptiveSimpsonsRule2D(func, 1, 1, 0, 1)); + Assert.Throws(() => new AdaptiveSimpsonsRule2D(func, 0, 1, 1, 0)); + Assert.Throws(() => new AdaptiveSimpsonsRule2D(func, 0, 1, 1, 1)); + } + + /// + /// A machine-epsilon-width domain completes successfully with an essentially zero integral. + /// + [TestMethod] + public void Test_DegenerateWidth() + { + var thin = new AdaptiveSimpsonsRule2D((x, y) => 1d, 0, 1E-16, 0, 1); + thin.Integrate(); + Assert.AreEqual(IntegrationStatus.Success, thin.Status); + Assert.AreEqual(1E-16, thin.Result, 1E-17); + } + } } From 069751f83ba3c6f7f761e4ff14c919ccf27c92d1 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Thu, 27 Aug 2026 11:56:38 -0600 Subject: [PATCH 123/222] Add weighted percentile and weighted product moments to Statistics The weighted percentile places each positive-weight point at the symmetric plotting position p(i) = A(i)/(A(i)+B(i)) - the weight strictly below over the weight strictly off the point - and interpolates linearly between positions. The construction is reflection-symmetric and strictly monotone, the first and last points sit at 0 and 1, and equal weights reproduce the unweighted Type 7 positions exactly, with unit weights arithmetically identical to the unweighted method. Integer weights agree with the replicated sample exactly at the plotting positions; no percentile estimator can also match replication between them, which the documentation states. Weighted mean, variance, standard deviation, skewness, and kurtosis follow with a WeightType enum choosing the bias-correction convention: frequency weights use the weight total as the sample size (integer weights reproduce the replicated sample), and reliability weights use the effective sample size W^2/sum(w^2) with the variance denominator W - sum(w^2)/W, making the convention scale-invariant. The skewness and kurtosis ratios are computed from weight-normalized central moments so both conventions stay scale-coherent. All reductions are sequential and deterministic; weights are validated finite and non-negative, zero-weight entries carry no mass, and an all-zero weight vector is rejected. --- Numerics/Data/Statistics/Statistics.cs | 359 +++++++++++++++++- Numerics/Data/Statistics/WeightType.cs | 32 ++ .../Data/Statistics/Test_Statistics.cs | 192 ++++++++++ 3 files changed, 582 insertions(+), 1 deletion(-) create mode 100644 Numerics/Data/Statistics/WeightType.cs diff --git a/Numerics/Data/Statistics/Statistics.cs b/Numerics/Data/Statistics/Statistics.cs index 85b8f70f..7f68403d 100644 --- a/Numerics/Data/Statistics/Statistics.cs +++ b/Numerics/Data/Statistics/Statistics.cs @@ -804,9 +804,366 @@ public static double Entropy(double[] data, Func pdf) if (p > 0) { sum += p * Math.Log(p); - } + } } return -sum; } + + #region Weighted Statistics + + /// + /// Validates a weight vector against its data vector and returns the weight total. + /// + /// Sample of data. + /// The non-negative, finite weight of each data entry. + /// The sum of the weights. + /// Thrown when either vector is null. + /// Thrown when the vectors differ in length. + /// Thrown when a weight is negative or not finite. + private static double ValidateWeights(IList data, IList weights) + { + if (data == null) throw new ArgumentNullException(nameof(data)); + if (weights == null) throw new ArgumentNullException(nameof(weights)); + if (data.Count != weights.Count) throw new ArgumentException("The data and weights must have the same length.", nameof(weights)); + double total = 0; + for (int i = 0; i < weights.Count; i++) + { + if (!Tools.IsFinite(weights[i]) || weights[i] < 0d) + throw new ArgumentOutOfRangeException(nameof(weights), "Weights must be finite and non-negative."); + total += weights[i]; + } + return total; + } + + /// + /// Computes the weighted mean and the weighted second, third, and fourth central power sums + /// with a deterministic, sequential two-pass sweep. + /// + /// Sample of data. + /// The non-negative, finite weight of each data entry. + /// The sum of the weights. + /// Output. The weighted mean. + /// Output. The weighted sum of squared deviations. + /// Output. The weighted sum of cubed deviations. + /// Output. The weighted sum of fourth-power deviations. + private static void WeightedCentralSums(IList data, IList weights, double total, + out double mean, out double s2, out double s3, out double s4) + { + double sum = 0; + for (int i = 0; i < data.Count; i++) + sum += weights[i] * data[i]; + mean = sum / total; + s2 = 0; + s3 = 0; + s4 = 0; + for (int i = 0; i < data.Count; i++) + { + double xm = data[i] - mean; + double w = weights[i]; + double xm2 = xm * xm; + s2 += w * xm2; + s3 += w * xm2 * xm; + s4 += w * xm2 * xm2; + } + } + + /// + /// Returns the effective sample size implied by the weights under the given interpretation: + /// the weight total for frequency weights, or W^2 / sum(w^2) for reliability weights. + /// + /// The non-negative, finite weight of each data entry. + /// The sum of the weights. + /// The weight interpretation. + /// The effective sample size. + private static double EffectiveSampleSize(IList weights, double total, WeightType weightType) + { + if (weightType == WeightType.Frequency) return total; + double sumSq = 0; + for (int i = 0; i < weights.Count; i++) + sumSq += weights[i] * weights[i]; + return total * total / sumSq; + } + + /// + /// Estimates the weighted arithmetic mean from the unsorted data array. + /// Returns NaN if data is empty or any entry is NaN. + /// + /// Sample of data, no sorting is assumed. + /// The non-negative, finite weight of each data entry. Equal weights reproduce the unweighted mean. + /// Thrown when either vector is null. + /// Thrown when the vectors differ in length, or when every weight is zero. + /// Thrown when a weight is negative or not finite. + public static double Mean(IList data, IList weights) + { + double total = ValidateWeights(data, weights); + if (data.Count == 0) return double.NaN; + if (total <= 0d) throw new ArgumentException("The weights must not all be zero.", nameof(weights)); + double sum = 0; + for (int i = 0; i < data.Count; i++) + sum += weights[i] * data[i]; + return sum / total; + } + + /// + /// Estimates the unbiased weighted variance from the unsorted data array. + /// Returns NaN if data has less than two entries, if any entry is NaN, or if the + /// bias-correction denominator implied by the weights is not positive. + /// + /// Sample of data, no sorting is assumed. + /// The non-negative, finite weight of each data entry. + /// Optional. The weight interpretation. Default = Frequency. + /// + /// Frequency weights use the denominator W - 1, so integer weights reproduce the unweighted + /// variance of the replicated sample exactly; reliability weights use W - sum(w^2)/W, which + /// reduces to N - 1 at equal weights. + /// + /// Thrown when either vector is null. + /// Thrown when the vectors differ in length, or when every weight is zero. + /// Thrown when a weight is negative or not finite. + public static double Variance(IList data, IList weights, WeightType weightType = WeightType.Frequency) + { + double total = ValidateWeights(data, weights); + if (data.Count <= 1) return double.NaN; + if (total <= 0d) throw new ArgumentException("The weights must not all be zero.", nameof(weights)); + WeightedCentralSums(data, weights, total, out _, out double s2, out _, out _); + double denominator; + if (weightType == WeightType.Frequency) + { + denominator = total - 1d; + } + else + { + double sumSq = 0; + for (int i = 0; i < weights.Count; i++) + sumSq += weights[i] * weights[i]; + denominator = total - sumSq / total; + } + if (denominator <= 0d) return double.NaN; + return s2 / denominator; + } + + /// + /// Estimates the unbiased weighted standard deviation from the unsorted data array. + /// Returns NaN if data has less than two entries, if any entry is NaN, or if the + /// bias-correction denominator implied by the weights is not positive. + /// + /// Sample of data, no sorting is assumed. + /// The non-negative, finite weight of each data entry. + /// Optional. The weight interpretation. Default = Frequency. + /// Thrown when either vector is null. + /// Thrown when the vectors differ in length, or when every weight is zero. + /// Thrown when a weight is negative or not finite. + public static double StandardDeviation(IList data, IList weights, WeightType weightType = WeightType.Frequency) + { + return Math.Sqrt(Variance(data, weights, weightType)); + } + + /// + /// Estimates the weighted skewness coefficient from the unsorted data array. + /// Returns NaN if data is empty or any entry is NaN. + /// + /// Sample of data, no sorting is assumed. + /// The non-negative, finite weight of each data entry. + /// Optional. The weight interpretation. Default = Frequency. + /// + /// The adjusted Fisher-Pearson correction of the unweighted estimator is applied with the + /// effective sample size in place of the count: the weight total for frequency weights + /// (integer weights reproduce the replicated sample exactly), or W^2 / sum(w^2) for + /// reliability weights. + /// + /// Thrown when either vector is null. + /// Thrown when the vectors differ in length, or when every weight is zero. + /// Thrown when a weight is negative or not finite. + public static double Skewness(IList data, IList weights, WeightType weightType = WeightType.Frequency) + { + double total = ValidateWeights(data, weights); + if (data.Count == 0) return double.NaN; + if (total <= 0d) throw new ArgumentException("The weights must not all be zero.", nameof(weights)); + WeightedCentralSums(data, weights, total, out _, out double s2, out double s3, out _); + double n = EffectiveSampleSize(weights, total, weightType); + double m2 = s2 / total; + double m3 = s3 / total; + double g = m3 / Math.Pow(m2, 3.0d / 2.0d); + double a = Math.Sqrt(n * (n - 1.0)); + double b = n - 2; + return a / b * g; + } + + /// + /// Estimates the weighted excess kurtosis from the unsorted data array. + /// Returns NaN if data is empty or any entry is NaN. + /// + /// Sample of data, no sorting is assumed. + /// The non-negative, finite weight of each data entry. + /// Optional. The weight interpretation. Default = Frequency. + /// + /// The unweighted small-sample correction is applied with the effective sample size in place + /// of the count: the weight total for frequency weights, or W^2 / sum(w^2) for reliability + /// weights. The moment ratio is computed from weight-normalized central moments, which makes + /// reliability weights scale-invariant; integer frequency weights reproduce the replicated + /// sample, and equal weights the unweighted estimator, to floating-point rounding. + /// + /// Thrown when either vector is null. + /// Thrown when the vectors differ in length, or when every weight is zero. + /// Thrown when a weight is negative or not finite. + public static double Kurtosis(IList data, IList weights, WeightType weightType = WeightType.Frequency) + { + double total = ValidateWeights(data, weights); + if (data.Count == 0) return double.NaN; + if (total <= 0d) throw new ArgumentException("The weights must not all be zero.", nameof(weights)); + WeightedCentralSums(data, weights, total, out _, out double s2, out _, out double s4); + double n = EffectiveSampleSize(weights, total, weightType); + double m2 = s2 / total; + double m4 = s4 / total; + double a = n * (n + 1) / ((n - 1) * (n - 2) * (n - 3)); + double b = m4 / (m2 * m2) * ((n - 1) * (n - 1) / n); + double c = (n - 1) * (n - 1) / ((n - 2) * (n - 3)); + return a * b - 3 * c; + } + + /// + /// Returns the weighted k-th percentile of values in a sample. + /// + /// Sample of data. + /// The k-th percentile to find. + /// The non-negative, finite weight of each data entry, aligned with the data order. + /// Boolean value indicating if the data is sorted or not. Assumed false, not sorted, by default. + /// The weighted k-th percentile. + /// + /// + /// With the positive-weight points sorted ascending, A(i) the weight strictly below point i + /// and B(i) the weight strictly above it, each point sits at the plotting position + /// p(i) = A(i) / (A(i) + B(i)) and the percentile interpolates linearly between adjacent + /// positions. The construction is reflection-symmetric and strictly monotone, the first and + /// last points sit at 0 and 1, and equal weights reproduce the unweighted Type 7 positions + /// i / (n - 1) - with unit weights the interpolation is arithmetically identical to + /// . + /// + /// + /// Zero-weight entries carry no mass and are excluded. Note that no percentile estimator can + /// reproduce the unweighted result at equal weights and, simultaneously, the replicated + /// sample at integer weights - replication changes the Type 7 interior positions - so integer + /// weights agree with the replicated sample exactly at the plotting positions and to within + /// the replicated interpolation gaps between them. + /// + /// + /// Thrown when or is null. + /// Thrown when is empty, the vectors differ in length, or every weight is zero. + /// Thrown when is not a finite value in [0,1], or a weight is negative or not finite. + public static double Percentile(IList data, double k, IList weights, bool dataIsSorted = false) + { + double total = ValidateWeights(data, weights); + if (data.Count == 0) throw new ArgumentException("Sequence contains no elements.", nameof(data)); + if (double.IsNaN(k) || k < 0.0 || k > 1.0) throw new ArgumentOutOfRangeException(nameof(k), "k must be in [0,1]."); + if (total <= 0d) throw new ArgumentException("The weights must not all be zero.", nameof(weights)); + PrepareWeightedSample(data, weights, dataIsSorted, out var x, out var w, out var prefix, out double sum); + return WeightedPercentile(x, w, prefix, sum, k); + } + + /// + /// Returns an array of weighted percentile values of a sample. + /// + /// Sample of data. + /// The list of k-th percentiles to find. + /// The non-negative, finite weight of each data entry, aligned with the data order. + /// Boolean value indicating if the data is sorted or not. Assumed false, not sorted, by default. + /// The weighted k-th percentiles. + /// Thrown when , , or is null. + /// Thrown when has entries and is empty, the vectors differ in length, or every weight is zero. An empty returns an empty array. + /// Thrown when any entry of is not a finite value in [0,1], or a weight is negative or not finite. + public static double[] Percentile(IList data, IList k, IList weights, bool dataIsSorted = false) + { + double total = ValidateWeights(data, weights); + if (k == null) throw new ArgumentNullException(nameof(k)); + if (k.Count == 0) return Array.Empty(); + if (data.Count == 0) throw new ArgumentException("Sequence contains no elements.", nameof(data)); + if (total <= 0d) throw new ArgumentException("The weights must not all be zero.", nameof(weights)); + PrepareWeightedSample(data, weights, dataIsSorted, out var x, out var w, out var prefix, out double sum); + var result = new double[k.Count]; + for (int i = 0; i < k.Count; i++) + { + if (double.IsNaN(k[i]) || k[i] < 0.0 || k[i] > 1.0) throw new ArgumentOutOfRangeException(nameof(k), "k must be in [0,1]."); + result[i] = WeightedPercentile(x, w, prefix, sum, k[i]); + } + return result; + } + + /// + /// Copies the positive-weight sample points, sorts them by value when required, and builds + /// the exclusive prefix-weight vector. + /// + /// Sample of data. + /// The non-negative, finite weight of each data entry. + /// True when the data (and aligned weights) are already sorted ascending. + /// Output. The positive-weight data values, sorted ascending. + /// Output. The aligned positive weights. + /// Output. The weight strictly below each point. + /// Output. The positive-weight total. + private static void PrepareWeightedSample(IList data, IList weights, bool dataIsSorted, + out double[] x, out double[] w, out double[] prefix, out double total) + { + int m = 0; + for (int i = 0; i < weights.Count; i++) + if (weights[i] > 0d) m++; + x = new double[m]; + w = new double[m]; + int j = 0; + for (int i = 0; i < data.Count; i++) + { + if (weights[i] > 0d) + { + x[j] = data[i]; + w[j] = weights[i]; + j++; + } + } + // Ties among equal data values leave the quantile function unchanged, so an unstable + // pair sort is safe. + if (!dataIsSorted) Array.Sort(x, w); + prefix = new double[m]; + double run = 0; + for (int i = 0; i < m; i++) + { + prefix[i] = run; + run += w[i]; + } + total = run; + } + + /// + /// Evaluates the weighted percentile from a prepared sorted sample. + /// + /// The positive-weight data values, sorted ascending. + /// The aligned positive weights. + /// The weight strictly below each point. + /// The positive-weight total. + /// The k-th percentile to find. + /// The weighted k-th percentile. + private static double WeightedPercentile(double[] x, double[] w, double[] prefix, double total, double k) + { + int m = x.Length; + if (m == 1 || k == 0.0) return x[0]; + if (k == 1.0) return x[m - 1]; + + // Plotting positions p(i) = A(i) / (total - w(i)) are strictly increasing with + // p(0) = 0 and p(m-1) = 1, so a bracket always exists. Binary search for it. + int lo = 0, hi = m - 1; + while (hi - lo > 1) + { + int mid = (lo + hi) >> 1; + double pMid = prefix[mid] / (total - w[mid]); + if (pMid <= k) lo = mid; + else hi = mid; + } + double pLo = prefix[lo] / (total - w[lo]); + double pHi = prefix[hi] / (total - w[hi]); + if (k <= pLo) return x[lo]; + if (k >= pHi) return x[hi]; + double theta = (k - pLo) / (pHi - pLo); + return x[lo] + theta * (x[hi] - x[lo]); + } + + #endregion + } } \ No newline at end of file diff --git a/Numerics/Data/Statistics/WeightType.cs b/Numerics/Data/Statistics/WeightType.cs new file mode 100644 index 00000000..5ac6d998 --- /dev/null +++ b/Numerics/Data/Statistics/WeightType.cs @@ -0,0 +1,32 @@ +using System; + +namespace Numerics.Data.Statistics +{ + /// + /// Enumeration of statistical weight interpretations for the weighted sample statistics. + /// + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + [Serializable] + public enum WeightType + { + /// + /// Frequency (repeat-count) weights: a weight states how many observations the entry + /// represents, and the weight total plays the role of the sample size in the bias + /// corrections. Integer frequency weights reproduce the unweighted statistics of the + /// correspondingly replicated sample exactly. + /// + Frequency, + + /// + /// Reliability (precision or importance) weights: only relative weights carry meaning, and + /// the bias corrections use the effective sample size W^2 / sum(w^2) in place of the count, + /// with the variance denominator W - sum(w^2)/W. + /// + Reliability + } +} diff --git a/Test_Numerics/Data/Statistics/Test_Statistics.cs b/Test_Numerics/Data/Statistics/Test_Statistics.cs index 8f74b874..3a3ded3b 100644 --- a/Test_Numerics/Data/Statistics/Test_Statistics.cs +++ b/Test_Numerics/Data/Statistics/Test_Statistics.cs @@ -739,5 +739,197 @@ public void Test_JackKnife_SingleElementAndCallbackIsolation() Assert.HasCount(original.Length, callbackSamples); Assert.AreEqual(original.Length, callbackSamples.Distinct().Count()); } + + /// + /// Unit weights take arithmetically identical paths to the unweighted statistics, so the + /// weighted mean, skewness, kurtosis, and percentile must match bit-for-bit; the unweighted + /// variance uses an incremental update formula, so unit weights agree to rounding there. + /// + [TestMethod] + public void Test_Weighted_UnitWeights_MatchUnweightedExactly() + { + var data = new double[] { 3d, 1d, 4d, 1.5d, 9d, 2.5d, 6d }; + var weights = new double[] { 1d, 1d, 1d, 1d, 1d, 1d, 1d }; + + Assert.AreEqual(Numerics.Data.Statistics.Statistics.Mean(data), Numerics.Data.Statistics.Statistics.Mean(data, weights), 0d); + Assert.AreEqual(Numerics.Data.Statistics.Statistics.Skewness(data), Numerics.Data.Statistics.Statistics.Skewness(data, weights), 0d); + // The weighted kurtosis routes through normalized central moments for reliability-weight + // scale invariance, so it agrees to rounding rather than bit-for-bit. + Assert.AreEqual(Numerics.Data.Statistics.Statistics.Kurtosis(data), Numerics.Data.Statistics.Statistics.Kurtosis(data, weights), 1E-13 * Math.Abs(Numerics.Data.Statistics.Statistics.Kurtosis(data))); + Assert.AreEqual(Numerics.Data.Statistics.Statistics.Variance(data), Numerics.Data.Statistics.Statistics.Variance(data, weights), 1E-13 * Numerics.Data.Statistics.Statistics.Variance(data)); + Assert.AreEqual(Numerics.Data.Statistics.Statistics.StandardDeviation(data), Numerics.Data.Statistics.Statistics.StandardDeviation(data, weights), 1E-13); + + // Dyadic percentile levels on a 5-point sample interpolate through identical arithmetic. + var five = new double[] { 30d, 10d, 50d, 20d, 40d }; + var unit = new double[] { 1d, 1d, 1d, 1d, 1d }; + foreach (double k in new[] { 0d, 0.25d, 0.375d, 0.5d, 0.75d, 1d }) + { + Assert.AreEqual(Numerics.Data.Statistics.Statistics.Percentile(five, k), Numerics.Data.Statistics.Statistics.Percentile(five, k, unit), 0d); + } + } + + /// + /// Under the frequency convention, integer weights must reproduce the unweighted statistics + /// of the replicated sample: mean, variance, standard deviation, skewness, and kurtosis. + /// + [TestMethod] + public void Test_Weighted_IntegerFrequencyWeights_MatchReplicatedSample() + { + var data = new double[] { 2d, 5d, 7d, 11d }; + var weights = new double[] { 1d, 3d, 2d, 4d }; + var replicated = new double[] { 2d, 5d, 5d, 5d, 7d, 7d, 11d, 11d, 11d, 11d }; + + Assert.AreEqual(Numerics.Data.Statistics.Statistics.Mean(replicated), Numerics.Data.Statistics.Statistics.Mean(data, weights), 1E-13 * Math.Abs(Numerics.Data.Statistics.Statistics.Mean(replicated))); + Assert.AreEqual(Numerics.Data.Statistics.Statistics.Variance(replicated), Numerics.Data.Statistics.Statistics.Variance(data, weights), 1E-12 * Numerics.Data.Statistics.Statistics.Variance(replicated)); + Assert.AreEqual(Numerics.Data.Statistics.Statistics.StandardDeviation(replicated), Numerics.Data.Statistics.Statistics.StandardDeviation(data, weights), 1E-12 * Numerics.Data.Statistics.Statistics.StandardDeviation(replicated)); + Assert.AreEqual(Numerics.Data.Statistics.Statistics.Skewness(replicated), Numerics.Data.Statistics.Statistics.Skewness(data, weights), 1E-12 * Math.Abs(Numerics.Data.Statistics.Statistics.Skewness(replicated))); + Assert.AreEqual(Numerics.Data.Statistics.Statistics.Kurtosis(replicated), Numerics.Data.Statistics.Statistics.Kurtosis(data, weights), 1E-12 * Math.Abs(Numerics.Data.Statistics.Statistics.Kurtosis(replicated))); + } + + /// + /// The reliability convention is scale-invariant: any equal weight vector reproduces the + /// unweighted variance, skewness, and kurtosis (dyadic weights make the reduction exact), + /// while the frequency convention deliberately reads scaled weights as replication. + /// + [TestMethod] + public void Test_Weighted_ReliabilityEqualWeights_MatchUnweighted() + { + var data = new double[] { 3d, 1d, 4d, 1.5d, 9d, 2.5d, 6d, 8d }; + var weights = new double[] { 0.5d, 0.5d, 0.5d, 0.5d, 0.5d, 0.5d, 0.5d, 0.5d }; + + Assert.AreEqual(Numerics.Data.Statistics.Statistics.Variance(data), Numerics.Data.Statistics.Statistics.Variance(data, weights, Numerics.Data.Statistics.WeightType.Reliability), 1E-13 * Numerics.Data.Statistics.Statistics.Variance(data)); + Assert.AreEqual(Numerics.Data.Statistics.Statistics.Skewness(data), Numerics.Data.Statistics.Statistics.Skewness(data, weights, Numerics.Data.Statistics.WeightType.Reliability), 0d); + Assert.AreEqual(Numerics.Data.Statistics.Statistics.Kurtosis(data), Numerics.Data.Statistics.Statistics.Kurtosis(data, weights, Numerics.Data.Statistics.WeightType.Reliability), 1E-13 * Math.Abs(Numerics.Data.Statistics.Statistics.Kurtosis(data))); + + // Frequency semantics with weight 2 everywhere equal the doubled (replicated) sample. + var doubled = new double[] { 2d, 2d, 2d, 2d, 2d, 2d, 2d, 2d }; + var replicated = new double[16]; + for (int i = 0; i < data.Length; i++) { replicated[2 * i] = data[i]; replicated[2 * i + 1] = data[i]; } + Assert.AreEqual(Numerics.Data.Statistics.Statistics.Variance(replicated), Numerics.Data.Statistics.Statistics.Variance(data, doubled, Numerics.Data.Statistics.WeightType.Frequency), 1E-12 * Numerics.Data.Statistics.Statistics.Variance(replicated)); + } + + /// + /// The reliability variance matches the closed form s2 / (W - sum(w^2)/W) on a hand-computed + /// three-point fixture: s2 = 1.56, W = 1, sum(w^2) = 0.38, variance = 1.56 / 0.62; the + /// weighted mean of the fixture is 2.8. + /// + [TestMethod] + public void Test_WeightedVariance_Reliability_ClosedForm() + { + var data = new double[] { 1d, 2d, 4d }; + var weights = new double[] { 0.2d, 0.3d, 0.5d }; + double expected = 1.56d / 0.62d; + Assert.AreEqual(expected, Numerics.Data.Statistics.Statistics.Variance(data, weights, Numerics.Data.Statistics.WeightType.Reliability), 1E-14 * expected); + Assert.AreEqual(2.8d, Numerics.Data.Statistics.Statistics.Mean(data, weights), 1E-15); + } + + /// + /// The weighted percentile pins its plotting-position convention p(i) = A(i)/(A(i)+B(i)): + /// at every position knot the integer-weight percentile equals the replicated-sample + /// percentile exactly, and the interpolated value between knots follows the convention. + /// + [TestMethod] + public void Test_WeightedPercentile_IntegerWeights_KnotExact() + { + var data = new double[] { 2d, 5d, 7d }; + var weights = new double[] { 1d, 3d, 2d }; + var replicated = new double[] { 2d, 5d, 5d, 5d, 7d, 7d }; + + // Positions: p0 = 0, p1 = 1/3, p2 = 1. + double p1 = 1d / 3d; + Assert.AreEqual(2d, Numerics.Data.Statistics.Statistics.Percentile(data, 0d, weights), 0d); + Assert.AreEqual(Numerics.Data.Statistics.Statistics.Percentile(replicated, p1), Numerics.Data.Statistics.Statistics.Percentile(data, p1, weights), 0d); + Assert.AreEqual(5d, Numerics.Data.Statistics.Statistics.Percentile(data, p1, weights), 0d); + Assert.AreEqual(7d, Numerics.Data.Statistics.Statistics.Percentile(data, 1d, weights), 0d); + + // Between knots the convention interpolates between adjacent positions: + // at k = 0.5, theta = (0.5 - 1/3)/(1 - 1/3) = 0.25, giving 5 + 0.25*(7 - 5) = 5.5. + Assert.AreEqual(5.5d, Numerics.Data.Statistics.Statistics.Percentile(data, 0.5d, weights), 1E-14); + } + + /// + /// Zero-weight entries carry no mass: dropping them and weighting them zero produce + /// identical percentiles and moments. + /// + [TestMethod] + public void Test_Weighted_ZeroWeightEntries_Dropped() + { + var data = new double[] { 1d, 99d, 2d, 3d }; + var weights = new double[] { 1d, 0d, 1d, 1d }; + var kept = new double[] { 1d, 2d, 3d }; + var keptWeights = new double[] { 1d, 1d, 1d }; + foreach (double k in new[] { 0d, 0.25d, 0.5d, 0.75d, 1d }) + { + Assert.AreEqual(Numerics.Data.Statistics.Statistics.Percentile(kept, k, keptWeights), Numerics.Data.Statistics.Statistics.Percentile(data, k, weights), 0d); + } + Assert.AreEqual(Numerics.Data.Statistics.Statistics.Mean(kept, keptWeights), Numerics.Data.Statistics.Statistics.Mean(data, weights), 0d); + Assert.AreEqual(Numerics.Data.Statistics.Statistics.Variance(kept, keptWeights), Numerics.Data.Statistics.Statistics.Variance(data, weights), 0d); + } + + /// + /// Degenerate samples: a single point returns itself at every percentile; a single point + /// leaves the variance undefined (NaN), matching the unweighted convention; all mass on one + /// point behaves as a single-point sample; and a frequency-weight total at or below one + /// leaves the Bessel denominator non-positive, so the variance is NaN. + /// + [TestMethod] + public void Test_Weighted_DegenerateSamples() + { + Assert.AreEqual(42d, Numerics.Data.Statistics.Statistics.Percentile(new double[] { 42d }, 0.5d, new double[] { 7d }), 0d); + Assert.IsTrue(double.IsNaN(Numerics.Data.Statistics.Statistics.Variance(new double[] { 42d }, new double[] { 7d }))); + Assert.AreEqual(9d, Numerics.Data.Statistics.Statistics.Percentile(new double[] { 9d, 5d }, 0.5d, new double[] { 3d, 0d }), 0d); + Assert.IsTrue(double.IsNaN(Numerics.Data.Statistics.Statistics.Variance(new double[] { 1d, 2d }, new double[] { 0.3d, 0.3d }, Numerics.Data.Statistics.WeightType.Frequency))); + } + + /// + /// The multi-percentile overload matches the scalar overload, the sorted-data flag matches + /// the unsorted call, and an empty percentile list returns an empty array. + /// + [TestMethod] + public void Test_WeightedPercentile_Overloads_Consistent() + { + var data = new double[] { 9d, 1d, 5d, 3d, 7d }; + var weights = new double[] { 0.5d, 1d, 2d, 1.5d, 1d }; + var ks = new double[] { 0d, 0.2d, 0.4d, 0.6d, 0.8d, 1d }; + var batch = Numerics.Data.Statistics.Statistics.Percentile(data, ks, weights); + for (int i = 0; i < ks.Length; i++) + { + Assert.AreEqual(Numerics.Data.Statistics.Statistics.Percentile(data, ks[i], weights), batch[i], 0d); + } + + var sortedData = new double[] { 1d, 3d, 5d, 7d, 9d }; + var sortedWeights = new double[] { 1d, 1.5d, 2d, 1d, 0.5d }; + foreach (double k in ks) + { + Assert.AreEqual(Numerics.Data.Statistics.Statistics.Percentile(data, k, weights), Numerics.Data.Statistics.Statistics.Percentile(sortedData, k, sortedWeights, dataIsSorted: true), 0d); + } + + Assert.IsEmpty(Numerics.Data.Statistics.Statistics.Percentile(data, new double[0], weights)); + } + + /// + /// Guard matrix for the weighted statistics: nulls, length mismatches, invalid weights, + /// all-zero weights, empty data, and out-of-range percentile levels all throw the + /// documented exceptions, and NaN data propagates NaN through the moments. + /// + [TestMethod] + public void Test_Weighted_GuardMatrix() + { + var data = new double[] { 1d, 2d }; + var weights = new double[] { 1d, 1d }; + Assert.Throws(() => Numerics.Data.Statistics.Statistics.Mean(null!, weights)); + Assert.Throws(() => Numerics.Data.Statistics.Statistics.Mean(data, null!)); + Assert.Throws(() => Numerics.Data.Statistics.Statistics.Mean(data, new double[] { 1d })); + Assert.Throws(() => Numerics.Data.Statistics.Statistics.Mean(data, new double[] { 1d, -0.5d })); + Assert.Throws(() => Numerics.Data.Statistics.Statistics.Mean(data, new double[] { 1d, double.NaN })); + Assert.Throws(() => Numerics.Data.Statistics.Statistics.Mean(data, new double[] { 1d, double.PositiveInfinity })); + Assert.Throws(() => Numerics.Data.Statistics.Statistics.Mean(data, new double[] { 0d, 0d })); + Assert.Throws(() => Numerics.Data.Statistics.Statistics.Percentile(new double[0], 0.5d, new double[0])); + Assert.Throws(() => Numerics.Data.Statistics.Statistics.Percentile(data, double.NaN, weights)); + Assert.Throws(() => Numerics.Data.Statistics.Statistics.Percentile(data, -0.1d, weights)); + Assert.Throws(() => Numerics.Data.Statistics.Statistics.Percentile(data, 1.1d, weights)); + Assert.Throws(() => Numerics.Data.Statistics.Statistics.Percentile(data, new double[] { 0.5d, 2d }, weights)); + Assert.IsTrue(double.IsNaN(Numerics.Data.Statistics.Statistics.Mean(new double[] { 1d, double.NaN }, weights))); + } } } From 78bce263c650a9bc29a2cc9044fe4cfd79354234 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Thu, 27 Aug 2026 12:02:31 -0600 Subject: [PATCH 124/222] Add sided transform-aware extrapolation to the ordered paired data lookups A [Flags] ExtrapolationSides enum (None, Below, Above, Both - the Hydrologics shape) joins GetYFromX and GetXFromY as an optional trailing parameter, on the scalar and list overloads. When the matching side is requested, an out-of-range lookup extends the boundary segment through the existing BaseInterpolate kernel - linearly in the configured transform space, so a logarithmic axis extends log-linearly and a normal-probability axis extends linearly in standard-normal z and stays within [0, 1] by construction. Sides are defined in value space regardless of sort orientation. The default None takes the historical endpoint-hold path unchanged, pinned by a regression test; exact endpoints return the boundary ordinate, single-point tables always hold, and plateau boundary segments extend at slope zero through the kernel's existing zero-run guard. Tests cover the endpoint-hold pin across ascending, descending, and transformed fixtures; closed-form linear, log-linear, and normal-z extensions on both ends with side selectivity; the GetXFromY mirror; list overload parity; and sampled quantile-ladder curves from UncertainOrderedPairedData extrapolating monotonically through CurveSample. --- .../Data/Paired Data/ExtrapolationSides.cs | 47 ++++++ .../Data/Paired Data/OrderedPairedData.cs | 74 +++++++-- .../Test_PairedDataInterpolation.cs | 151 ++++++++++++++++++ .../Paired Data/Test_UncertainPairedData.cs | 40 +++++ 4 files changed, 301 insertions(+), 11 deletions(-) create mode 100644 Numerics/Data/Paired Data/ExtrapolationSides.cs diff --git a/Numerics/Data/Paired Data/ExtrapolationSides.cs b/Numerics/Data/Paired Data/ExtrapolationSides.cs new file mode 100644 index 00000000..a8365468 --- /dev/null +++ b/Numerics/Data/Paired Data/ExtrapolationSides.cs @@ -0,0 +1,47 @@ +using System; + +namespace Numerics.Data +{ + /// + /// Enumeration of the sides of an ordered data range on which out-of-range lookups may + /// extrapolate rather than hold the boundary value. + /// + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + /// Sides are defined in value space and are independent of the sort orientation: + /// refers to lookups beyond the minimum value of the lookup axis and + /// to lookups beyond its maximum. Extrapolation extends the boundary + /// segment linearly in the configured transform space, so a logarithmic axis extends + /// log-linearly and a normal-probability axis extends linearly in standard-normal z - the + /// latter therefore always back-transforms into (0, 1). + /// + /// + [Flags] + [Serializable] + public enum ExtrapolationSides + { + /// + /// No extrapolation: out-of-range lookups hold the boundary value on both sides. + /// + None = 0, + + /// + /// Extrapolate below the minimum value of the lookup axis. + /// + Below = 1, + + /// + /// Extrapolate above the maximum value of the lookup axis. + /// + Above = 2, + + /// + /// Extrapolate on both sides of the range. + /// + Both = Below | Above + } +} diff --git a/Numerics/Data/Paired Data/OrderedPairedData.cs b/Numerics/Data/Paired Data/OrderedPairedData.cs index 0a2002a4..1384e8a8 100644 --- a/Numerics/Data/Paired Data/OrderedPairedData.cs +++ b/Numerics/Data/Paired Data/OrderedPairedData.cs @@ -859,8 +859,21 @@ private double BaseInterpolate(double value, int index, bool givenX = true, Tran /// The x value. /// Optional. Transform for the x values. Default = None. /// Optional. Transform for the y values. Default = None. + /// + /// Optional. The sides of the x-range on which an out-of-range lookup extends the boundary + /// segment linearly in the configured transform space rather than holding the boundary + /// ordinate. Default = None, the historical endpoint hold. + /// /// The interpolated value. - public double GetYFromX(double x, Transform xTransform = Transform.None, Transform yTransform = Transform.None) + /// + /// Sides are defined in value space regardless of the sort orientation: Below is beyond the + /// minimum x and Above beyond the maximum. Exactly at an endpoint the boundary ordinate is + /// returned unchanged, a single-point table always holds, and a plateau (equal boundary + /// ordinates in transform space) extends at slope zero. Extrapolation on an untransformed + /// axis is unbounded, so a caller holding a bounded quantity such as a probability must + /// clamp the result or use the NormalZ transform, which is bounded by construction. + /// + public double GetYFromX(double x, Transform xTransform = Transform.None, Transform yTransform = Transform.None, ExtrapolationSides extrapolation = ExtrapolationSides.None) { if (Count == 0) return double.NaN; if (OrderX == SortOrder.None) @@ -868,9 +881,22 @@ public double GetYFromX(double x, Transform xTransform = Transform.None, Transfo // First see if value is out of range if (Count == 1) return _ordinates[0].Y; - if ((OrderX == SortOrder.Ascending && x <= _ordinates[0].X) || (OrderX == SortOrder.Descending && x >= _ordinates[0].X)) return _ordinates[0].Y; - if ((OrderX == SortOrder.Ascending && x >= _ordinates[Count - 1].X) || (OrderX == SortOrder.Descending && x <= _ordinates[Count - 1].X)) return _ordinates[Count - 1].Y; - + if ((OrderX == SortOrder.Ascending && x <= _ordinates[0].X) || (OrderX == SortOrder.Descending && x >= _ordinates[0].X)) + { + // Index 0 is the minimum x when ascending and the maximum when descending. + var side = OrderX == SortOrder.Ascending ? ExtrapolationSides.Below : ExtrapolationSides.Above; + if ((extrapolation & side) != 0 && x != _ordinates[0].X) + return BaseInterpolate(x, 0, true, xTransform, yTransform); + return _ordinates[0].Y; + } + if ((OrderX == SortOrder.Ascending && x >= _ordinates[Count - 1].X) || (OrderX == SortOrder.Descending && x <= _ordinates[Count - 1].X)) + { + var side = OrderX == SortOrder.Ascending ? ExtrapolationSides.Above : ExtrapolationSides.Below; + if ((extrapolation & side) != 0 && x != _ordinates[Count - 1].X) + return BaseInterpolate(x, Count - 2, true, xTransform, yTransform); + return _ordinates[Count - 1].Y; + } + // Interpolate return BaseInterpolate(x, SearchX(x), true, xTransform, yTransform); } @@ -881,8 +907,19 @@ public double GetYFromX(double x, Transform xTransform = Transform.None, Transfo /// The y value. /// Optional. Transform for the x values. Default = None. /// Optional. Transform for the y values. Default = None. + /// + /// Optional. The sides of the y-range on which an out-of-range lookup extends the boundary + /// segment linearly in the configured transform space rather than holding the boundary + /// ordinate. Default = None, the historical endpoint hold. + /// /// The interpolated value. - public double GetXFromY(double y, Transform xTransform = Transform.None, Transform yTransform = Transform.None) + /// + /// Sides are defined in value space regardless of the sort orientation: Below is beyond the + /// minimum y and Above beyond the maximum. Exactly at an endpoint the boundary ordinate is + /// returned unchanged, a single-point table always holds, and a plateau (equal boundary + /// ordinates in transform space) extends at slope zero. + /// + public double GetXFromY(double y, Transform xTransform = Transform.None, Transform yTransform = Transform.None, ExtrapolationSides extrapolation = ExtrapolationSides.None) { if (Count == 0) return double.NaN; if (OrderY == SortOrder.None) @@ -890,8 +927,21 @@ public double GetXFromY(double y, Transform xTransform = Transform.None, Transfo // First see if value is out of range if (Count == 1) return _ordinates[0].X; - if ((OrderY == SortOrder.Ascending && y <= _ordinates[0].Y) || (OrderY == SortOrder.Descending && y >= _ordinates[0].Y)) return _ordinates[0].X; - if ((OrderY == SortOrder.Ascending && y >= _ordinates[Count - 1].Y) || (OrderY == SortOrder.Descending && y <= _ordinates[Count - 1].Y)) return _ordinates[Count - 1].X; + if ((OrderY == SortOrder.Ascending && y <= _ordinates[0].Y) || (OrderY == SortOrder.Descending && y >= _ordinates[0].Y)) + { + // Index 0 is the minimum y when ascending and the maximum when descending. + var side = OrderY == SortOrder.Ascending ? ExtrapolationSides.Below : ExtrapolationSides.Above; + if ((extrapolation & side) != 0 && y != _ordinates[0].Y) + return BaseInterpolate(y, 0, false, xTransform, yTransform); + return _ordinates[0].X; + } + if ((OrderY == SortOrder.Ascending && y >= _ordinates[Count - 1].Y) || (OrderY == SortOrder.Descending && y <= _ordinates[Count - 1].Y)) + { + var side = OrderY == SortOrder.Ascending ? ExtrapolationSides.Above : ExtrapolationSides.Below; + if ((extrapolation & side) != 0 && y != _ordinates[Count - 1].Y) + return BaseInterpolate(y, Count - 2, false, xTransform, yTransform); + return _ordinates[Count - 1].X; + } // Interpolate return BaseInterpolate(y, SearchY(y), false, xTransform, yTransform); } @@ -902,12 +952,13 @@ public double GetXFromY(double y, Transform xTransform = Transform.None, Transfo /// The list of x-values. /// Optional. Transform for the x values. Default = None. /// Optional. Transform for the y values. Default = None. + /// Optional. The sides of the x-range on which out-of-range lookups extrapolate. Default = None, the historical endpoint hold. /// An array of interpolated values. - public double[] GetYFromX(IList xValues, Transform xTransform = Transform.None, Transform yTransform = Transform.None) + public double[] GetYFromX(IList xValues, Transform xTransform = Transform.None, Transform yTransform = Transform.None, ExtrapolationSides extrapolation = ExtrapolationSides.None) { var result = new double[xValues.Count]; for (int i = 0; i < xValues.Count; i++) - result[i] = GetYFromX(xValues[i], xTransform, yTransform); + result[i] = GetYFromX(xValues[i], xTransform, yTransform, extrapolation); return result; } @@ -917,12 +968,13 @@ public double[] GetYFromX(IList xValues, Transform xTransform = Transfor /// The list of y-values. /// Optional. Transform for the x values. Default = None. /// Optional. Transform for the y values. Default = None. + /// Optional. The sides of the y-range on which out-of-range lookups extrapolate. Default = None, the historical endpoint hold. /// An array of interpolated values. - public double[] GetXFromY(IList yValues, Transform xTransform = Transform.None, Transform yTransform = Transform.None) + public double[] GetXFromY(IList yValues, Transform xTransform = Transform.None, Transform yTransform = Transform.None, ExtrapolationSides extrapolation = ExtrapolationSides.None) { var result = new double[yValues.Count]; for (int i = 0; i < yValues.Count; i++) - result[i] = GetXFromY(yValues[i], xTransform, yTransform); + result[i] = GetXFromY(yValues[i], xTransform, yTransform, extrapolation); return result; } diff --git a/Test_Numerics/Data/Paired Data/Test_PairedDataInterpolation.cs b/Test_Numerics/Data/Paired Data/Test_PairedDataInterpolation.cs index eac0e91f..171bd954 100644 --- a/Test_Numerics/Data/Paired Data/Test_PairedDataInterpolation.cs +++ b/Test_Numerics/Data/Paired Data/Test_PairedDataInterpolation.cs @@ -424,5 +424,156 @@ public void Test_Lin_List() Assert.AreEqual(XArray[i], xFromY[i], 1E-6); } + /// + /// Regression pin for the default out-of-range behavior: with no extrapolation requested, + /// lookups hold the boundary ordinate on both sides, at and beyond the endpoints, for + /// ascending and descending tables and for transformed axes. This is the bit-identity gate + /// for ExtrapolationSides.None. + /// + [TestMethod] + public void Test_Extrapolation_DefaultNone_MatchesEndpointHold() + { + // Ascending, no transforms. + var asc = new OrderedPairedData(new double[] { 1d, 2d, 4d, 8d }, new double[] { 10d, 20d, 40d, 80d }, true, SortOrder.Ascending, true, SortOrder.Ascending); + Assert.AreEqual(30d, asc.GetYFromX(3d), 1E-12); + Assert.AreEqual(10d, asc.GetYFromX(1d), 0d); + Assert.AreEqual(80d, asc.GetYFromX(8d), 0d); + Assert.AreEqual(10d, asc.GetYFromX(0.5d), 0d); + Assert.AreEqual(80d, asc.GetYFromX(16d), 0d); + Assert.AreEqual(1d, asc.GetXFromY(5d), 0d); + Assert.AreEqual(8d, asc.GetXFromY(100d), 0d); + + // Descending X, no transforms. + var desc = new OrderedPairedData(new double[] { 8d, 4d, 2d, 1d }, new double[] { 10d, 20d, 40d, 80d }, true, SortOrder.Descending, true, SortOrder.Ascending); + Assert.AreEqual(10d, desc.GetYFromX(16d), 0d); + Assert.AreEqual(80d, desc.GetYFromX(0.5d), 0d); + + // Logarithmic axes hold the raw boundary ordinates as well. + var logs = new OrderedPairedData(new double[] { 1d, 10d, 100d }, new double[] { 2d, 20d, 200d }, true, SortOrder.Ascending, true, SortOrder.Ascending); + Assert.AreEqual(2d, logs.GetYFromX(0.1d, Transform.Logarithmic, Transform.Logarithmic), 0d); + Assert.AreEqual(200d, logs.GetYFromX(1000d, Transform.Logarithmic, Transform.Logarithmic), 0d); + + // Normal-z probability axis holds too. + var normz = new OrderedPairedData(new double[] { 1d, 2d, 3d }, new double[] { 0.2d, 0.5d, 0.8d }, true, SortOrder.Ascending, true, SortOrder.Ascending); + Assert.AreEqual(0.2d, normz.GetYFromX(0d, Transform.None, Transform.NormalZ), 0d); + Assert.AreEqual(0.8d, normz.GetYFromX(4d, Transform.None, Transform.NormalZ), 0d); + } + + /// + /// Linear extrapolation with no transforms: each end extends the boundary segment's secant, + /// the side flags act independently, and ascending and descending tables agree on the same + /// underlying line. + /// + [TestMethod] + public void Test_Extrapolation_LinearBothEnds_NoTransform() + { + var asc = new OrderedPairedData(new double[] { 1d, 2d, 4d, 8d }, new double[] { 10d, 20d, 40d, 80d }, true, SortOrder.Ascending, true, SortOrder.Ascending); + // Below: through (1,10)-(2,20) at x = 0.5 -> 5. Above: through (4,40)-(8,80) at x = 10 -> 100. + Assert.AreEqual(5d, asc.GetYFromX(0.5d, extrapolation: ExtrapolationSides.Both), 1E-12); + Assert.AreEqual(100d, asc.GetYFromX(10d, extrapolation: ExtrapolationSides.Both), 1E-12); + // Side selectivity: the unrequested side still holds. + Assert.AreEqual(5d, asc.GetYFromX(0.5d, extrapolation: ExtrapolationSides.Below), 1E-12); + Assert.AreEqual(80d, asc.GetYFromX(10d, extrapolation: ExtrapolationSides.Below), 0d); + Assert.AreEqual(10d, asc.GetYFromX(0.5d, extrapolation: ExtrapolationSides.Above), 0d); + Assert.AreEqual(100d, asc.GetYFromX(10d, extrapolation: ExtrapolationSides.Above), 1E-12); + // Exactly at the endpoints the boundary ordinate is returned unchanged. + Assert.AreEqual(10d, asc.GetYFromX(1d, extrapolation: ExtrapolationSides.Both), 0d); + Assert.AreEqual(80d, asc.GetYFromX(8d, extrapolation: ExtrapolationSides.Both), 0d); + + // The same line authored descending extrapolates to the same values: below the minimum + // x of 1 through (2,40)-(1,80) -> 100 at x = 0.5; above the maximum x of 8 through + // (8,10)-(4,20) -> 5 at x = 10. + var desc = new OrderedPairedData(new double[] { 8d, 4d, 2d, 1d }, new double[] { 10d, 20d, 40d, 80d }, true, SortOrder.Descending, true, SortOrder.Ascending); + Assert.AreEqual(100d, desc.GetYFromX(0.5d, extrapolation: ExtrapolationSides.Both), 1E-12); + Assert.AreEqual(5d, desc.GetYFromX(10d, extrapolation: ExtrapolationSides.Both), 1E-12); + Assert.AreEqual(100d, desc.GetYFromX(0.5d, extrapolation: ExtrapolationSides.Below), 1E-12); + Assert.AreEqual(10d, desc.GetYFromX(10d, extrapolation: ExtrapolationSides.Below), 0d); + } + + /// + /// Logarithmic-axis extrapolation extends log-linearly: the table y = 2x on log-log axes + /// continues to y = 2x beyond both ends. + /// + [TestMethod] + public void Test_Extrapolation_LogTransform() + { + var logs = new OrderedPairedData(new double[] { 1d, 10d, 100d }, new double[] { 2d, 20d, 200d }, true, SortOrder.Ascending, true, SortOrder.Ascending); + Assert.AreEqual(2000d, logs.GetYFromX(1000d, Transform.Logarithmic, Transform.Logarithmic, ExtrapolationSides.Both), 1E-9 * 2000d); + Assert.AreEqual(0.2d, logs.GetYFromX(0.1d, Transform.Logarithmic, Transform.Logarithmic, ExtrapolationSides.Both), 1E-9 * 0.2d); + } + + /// + /// Normal-z extrapolation extends linearly in standard-normal z and therefore always + /// back-transforms into (0, 1): the probability ladder 0.2/0.5/0.8 is exactly linear in z, + /// and the far extension stays a valid probability. + /// + [TestMethod] + public void Test_Extrapolation_NormalZTransform() + { + var normz = new OrderedPairedData(new double[] { 1d, 2d, 3d }, new double[] { 0.2d, 0.5d, 0.8d }, true, SortOrder.Ascending, true, SortOrder.Ascending); + double z05 = Numerics.Distributions.Normal.StandardZ(0.5d); + double z08 = Numerics.Distributions.Normal.StandardZ(0.8d); + double expected = Numerics.Distributions.Normal.StandardCDF(z08 + (z08 - z05)); + Assert.AreEqual(expected, normz.GetYFromX(4d, Transform.None, Transform.NormalZ, ExtrapolationSides.Both), 1E-12); + // Extreme extension stays within [0, 1], saturating at the floating-point boundaries. + double far = normz.GetYFromX(100d, Transform.None, Transform.NormalZ, ExtrapolationSides.Both); + Assert.IsGreaterThan(0.999d, far); + Assert.IsLessThanOrEqualTo(1d, far); + double low = normz.GetYFromX(-100d, Transform.None, Transform.NormalZ, ExtrapolationSides.Both); + Assert.IsGreaterThanOrEqualTo(0d, low); + Assert.IsLessThan(0.001d, low); + } + + /// + /// GetXFromY extrapolates on the y-range sides with the same semantics as GetYFromX. + /// + [TestMethod] + public void Test_Extrapolation_GetXFromY() + { + var asc = new OrderedPairedData(new double[] { 1d, 2d, 4d, 8d }, new double[] { 10d, 20d, 40d, 80d }, true, SortOrder.Ascending, true, SortOrder.Ascending); + Assert.AreEqual(0.5d, asc.GetXFromY(5d, extrapolation: ExtrapolationSides.Both), 1E-12); + Assert.AreEqual(10d, asc.GetXFromY(100d, extrapolation: ExtrapolationSides.Both), 1E-12); + Assert.AreEqual(1d, asc.GetXFromY(5d, extrapolation: ExtrapolationSides.Above), 0d); + Assert.AreEqual(8d, asc.GetXFromY(100d, extrapolation: ExtrapolationSides.Below), 0d); + } + + /// + /// Plateau (non-strict) boundary segments extend at slope zero, which coincides with the + /// endpoint hold, and a single-point table always holds regardless of the flags. + /// + [TestMethod] + public void Test_Extrapolation_PlateauAndSinglePoint() + { + var plateau = new OrderedPairedData(new double[] { 1d, 2d, 3d, 4d }, new double[] { 5d, 5d, 10d, 10d }, true, SortOrder.Ascending, false, SortOrder.Ascending); + Assert.AreEqual(5d, plateau.GetYFromX(0d, extrapolation: ExtrapolationSides.Both), 0d); + Assert.AreEqual(10d, plateau.GetYFromX(6d, extrapolation: ExtrapolationSides.Both), 0d); + + var single = new OrderedPairedData(new double[] { 2d }, new double[] { 7d }, true, SortOrder.Ascending, true, SortOrder.Ascending); + Assert.AreEqual(7d, single.GetYFromX(100d, extrapolation: ExtrapolationSides.Both), 0d); + Assert.AreEqual(7d, single.GetYFromX(-100d, extrapolation: ExtrapolationSides.Both), 0d); + } + + /// + /// The list overloads match the scalar overload point-for-point under extrapolation. + /// + [TestMethod] + public void Test_Extrapolation_ListOverloads_MatchScalar() + { + var asc = new OrderedPairedData(new double[] { 1d, 2d, 4d, 8d }, new double[] { 10d, 20d, 40d, 80d }, true, SortOrder.Ascending, true, SortOrder.Ascending); + var xs = new double[] { 0.5d, 1d, 3d, 8d, 10d }; + var ys = asc.GetYFromX(xs, Transform.None, Transform.None, ExtrapolationSides.Both); + for (int i = 0; i < xs.Length; i++) + { + Assert.AreEqual(asc.GetYFromX(xs[i], Transform.None, Transform.None, ExtrapolationSides.Both), ys[i], 0d); + } + + var targets = new double[] { 5d, 10d, 30d, 80d, 100d }; + var backs = asc.GetXFromY(targets, Transform.None, Transform.None, ExtrapolationSides.Both); + for (int i = 0; i < targets.Length; i++) + { + Assert.AreEqual(asc.GetXFromY(targets[i], Transform.None, Transform.None, ExtrapolationSides.Both), backs[i], 0d); + } + } + } } diff --git a/Test_Numerics/Data/Paired Data/Test_UncertainPairedData.cs b/Test_Numerics/Data/Paired Data/Test_UncertainPairedData.cs index ab5b865a..7e9a857b 100644 --- a/Test_Numerics/Data/Paired Data/Test_UncertainPairedData.cs +++ b/Test_Numerics/Data/Paired Data/Test_UncertainPairedData.cs @@ -237,5 +237,45 @@ public void Test_Equality() bool test4 = (_dataset1 != dataset5); Assert.IsFalse(test4); } + + /// + /// Sampled quantile-ladder curves extrapolate through the ordered-paired-data mechanism: + /// with slope-ordered Normal ordinates (shared standard deviation), every sampled curve is a + /// parallel line, so the extrapolated values are exact and remain monotone across the + /// quantile ladder. Slope-disordered ladders can cross beyond the data, which the consumer + /// validates. + /// + [TestMethod] + public void Test_CurveSample_Extrapolation_LadderMonotone() + { + var xs = new List { 1d, 2d, 3d }; + var dists = new List + { + new Normal(10d, 2d), + new Normal(20d, 2d), + new Normal(30d, 2d) + }; + var uopd = new UncertainOrderedPairedData(xs, dists, true, SortOrder.Ascending, true, SortOrder.Ascending, UnivariateDistributionType.Normal); + + double previous = double.NegativeInfinity; + foreach (double p in new[] { 0.1d, 0.5d, 0.9d }) + { + var curve = uopd.CurveSample(p); + // Each sampled curve is x -> 10x + 2z(p): at x = 5 the extension is 50 + 2z(p). + double expected = 50d + 2d * Normal.StandardZ(p); + double value = curve.GetYFromX(5d, Transform.None, Transform.None, ExtrapolationSides.Both); + Assert.AreEqual(expected, value, 1E-10); + Assert.IsGreaterThan(previous, value); + previous = value; + + // The default lookup still holds the endpoint. + Assert.AreEqual(30d + 2d * Normal.StandardZ(p), curve.GetYFromX(5d), 1E-10); + } + + // The mean curve (y = 10x) extrapolates through the same mechanism on both sides. + var mean = uopd.CurveSample(); + Assert.AreEqual(50d, mean.GetYFromX(5d, Transform.None, Transform.None, ExtrapolationSides.Both), 1E-10); + Assert.AreEqual(-40d, mean.GetYFromX(-4d, Transform.None, Transform.None, ExtrapolationSides.Both), 1E-10); + } } } From ffdb44d49a1fe73464b8f5b710fb150423c73785 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Thu, 27 Aug 2026 12:07:41 -0600 Subject: [PATCH 125/222] Add seeded linear-matrix scrambling with a digital shift to the Sobol sequence A new SobolSequence(dimension, seed) constructor applies Matousek's linear matrix scramble - a random unit lower-triangular bit matrix per dimension, pre-applied once to the direction numbers, which the generator's GF(2) linearity makes exactly equivalent to scrambling every point - followed by a per-dimension 52-bit digital shift XORed at emission. This is the practical realization of Owen-style scrambling, giving a reproducible seeded randomization that a content-seed contract can drive; the seeded draw order (per dimension: sub-diagonal matrix bits row by row, then the shift bits) is part of the contract. Both operations preserve dyadic equidistribution, so each scrambled dimension remains a base-2 (0,1)-sequence. The parameterless constructor keeps an all-zero shift and untouched direction numbers, so the unrandomized stream is arithmetically identical; the existing R randtoolbox pins are the regression gate. A nullable Seed property records provenance. Tests cover bit-identical same-seed reproduction, differing seeds, SkipTo parity with sequential emission (pinning the historical next-emission indexing, where SkipTo(0) and SkipTo(1) coincide, for the scrambled and unrandomized sequences alike), exact one-per-bin dyadic-block stratification for every dimension under scrambling, marginal uniformity, and a twenty-seed variance-reduction measurement against plain Monte Carlo. --- Numerics/Sampling/SobolSequence.cs | 124 ++++++++++++- Test_Numerics/Sampling/Test_SobolSequence.cs | 184 ++++++++++++++++++- 2 files changed, 303 insertions(+), 5 deletions(-) diff --git a/Numerics/Sampling/SobolSequence.cs b/Numerics/Sampling/SobolSequence.cs index 4721a186..4d48e138 100644 --- a/Numerics/Sampling/SobolSequence.cs +++ b/Numerics/Sampling/SobolSequence.cs @@ -50,10 +50,46 @@ public SobolSequence(int dimension = 1) Dimension = dimension; _direction = new long[dimension, BITS + 1]; x = new long[dimension]; + _shift = new long[dimension]; initialize(); } + /// + /// Constructs a new randomized (scrambled) Sobol Sequence with a reproducible seed. + /// + /// The spatial dimension. + /// The pseudorandom seed for the scrambling. + /// + /// + /// + /// The randomization applies Matousek's linear matrix scrambling followed by a random + /// digital shift - the practical realization of Owen-style scrambling. For each dimension a + /// random unit lower-triangular bit matrix is applied once to the direction numbers (the + /// generator recursion is linear over GF(2), so pre-scrambling the direction numbers + /// scrambles every generated point exactly), and a random 52-bit digital shift is applied to + /// each emitted coordinate. Both preserve the sequence's dyadic equidistribution, so each + /// scrambled dimension remains a base-2 (0,1)-sequence. + /// + /// + /// The same seed always reproduces the same sequence. The seeded draw order is part of that + /// contract: for each dimension in order, the sub-diagonal matrix bits row by row, then the + /// shift bits. The parameterless-seed constructor generates the original unrandomized + /// sequence and is unaffected. + /// + /// References: + /// + /// Matousek, J. (1998). On the L2-discrepancy for anchored boxes. Journal of Complexity, 14(4), 527-556. + /// Owen, A. B. (2021). On dropping the first Sobol' point. arXiv:2008.08051. + /// + /// + public SobolSequence(int dimension, int seed) : this(dimension) + { + Seed = seed; + var prng = new MersenneTwister(seed); + ScrambleDirections(prng); + } + /// /// The number of bits to use. /// @@ -70,15 +106,20 @@ public SobolSequence(int dimension = 1) private static int MAX_DIMENSION = 21201; /// - /// The current index in the sequence. + /// The current index in the sequence. /// - private int count; + private int count; /// - /// Space dimension. + /// Space dimension. /// public int Dimension { get; private set; } + /// + /// The pseudorandom seed of the scrambling, or null for the original unrandomized sequence. + /// + public int? Seed { get; } + /// /// The direction vector for each component. /// @@ -89,6 +130,11 @@ public SobolSequence(int dimension = 1) /// private long[] x; + /// + /// The per-dimension digital shift applied at emission; all zero for the unrandomized sequence. + /// + private readonly long[] _shift; + /// /// Initialize the Sobol Sequence. /// @@ -180,6 +226,74 @@ private void initDirectionVector(int d, int a, int[] m) } } + /// + /// Applies the seeded linear matrix scramble to the direction numbers and draws the digital + /// shifts. For each dimension in order: the sub-diagonal bits of a random unit + /// lower-triangular bit matrix, row by row, then the 52 shift bits. + /// + /// The seeded pseudorandom number generator. + private void ScrambleDirections(Random prng) + { + var rowMasks = new long[BITS]; + for (int d = 0; d < Dimension; d++) + { + // Row i keeps digit i on the diagonal and mixes a random subset of earlier digits. + for (int i = 1; i <= BITS; i++) + { + long mask = 1L << (BITS - i); + for (int j = 1; j < i; j++) + { + if (prng.Next(2) == 1) mask |= 1L << (BITS - j); + } + rowMasks[i - 1] = mask; + } + // The generator XORs direction numbers, and the scramble is linear over GF(2), so + // scrambling the direction numbers once scrambles every generated point exactly. + for (int c = 1; c <= BITS; c++) + { + _direction[d, c] = ApplyLinearScramble(rowMasks, _direction[d, c]); + } + long shift = 0; + for (int i = 1; i <= BITS; i++) + { + if (prng.Next(2) == 1) shift |= 1L << (BITS - i); + } + _shift[d] = shift; + } + } + + /// + /// Multiplies a direction number's digit vector by the unit lower-triangular bit matrix over GF(2). + /// + /// The matrix rows as digit masks. + /// The direction number. + /// The scrambled direction number. + private static long ApplyLinearScramble(long[] rowMasks, long vector) + { + long result = 0; + for (int i = 1; i <= BITS; i++) + { + if (Parity(rowMasks[i - 1] & vector)) result |= 1L << (BITS - i); + } + return result; + } + + /// + /// Returns the bit parity of a value (true when the number of set bits is odd). + /// + /// The value. + /// True for odd parity. + private static bool Parity(long value) + { + value ^= value >> 32; + value ^= value >> 16; + value ^= value >> 8; + value ^= value >> 4; + value ^= value >> 2; + value ^= value >> 1; + return (value & 1L) != 0; + } + /// /// Returns a double-precision number that is greater than or equal to 0.0, and less than 1.0. /// @@ -205,7 +319,9 @@ public double[] NextDouble() for (int i = 0; i < Dimension; i++) { x[i] ^= _direction[i, c]; - v[i] = (double)x[i] / SCALE; + // The digital shift is all zero for the unrandomized sequence, so the emission is + // arithmetically identical there. + v[i] = (double)(x[i] ^ _shift[i]) / SCALE; } count++; return v; diff --git a/Test_Numerics/Sampling/Test_SobolSequence.cs b/Test_Numerics/Sampling/Test_SobolSequence.cs index c4d335ef..040281c9 100644 --- a/Test_Numerics/Sampling/Test_SobolSequence.cs +++ b/Test_Numerics/Sampling/Test_SobolSequence.cs @@ -1,5 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Sampling; +using System; namespace Sampling { @@ -51,7 +52,188 @@ public void Test_Sobol() Assert.AreEqual(trueResult[i, j], rnd[j]); } } - + + } + + /// + /// The same seed reproduces the scrambled sequence bit-for-bit, and the Seed property + /// reports the scrambling seed (null for the unrandomized sequence). + /// + [TestMethod] + public void Test_ScrambledSobol_SameSeed_BitIdentical() + { + var first = new SobolSequence(3, 12345); + var second = new SobolSequence(3, 12345); + for (int i = 0; i < 100; i++) + { + var a = first.NextDouble(); + var b = second.NextDouble(); + for (int j = 0; j < 3; j++) + { + Assert.AreEqual(BitConverter.DoubleToInt64Bits(a[j]), BitConverter.DoubleToInt64Bits(b[j])); + } + } + Assert.AreEqual(12345, first.Seed); + Assert.IsNull(new SobolSequence(3).Seed); + } + + /// + /// Different seeds produce different scrambled sequences. + /// + [TestMethod] + public void Test_ScrambledSobol_DifferentSeeds_Differ() + { + var first = new SobolSequence(2, 1); + var second = new SobolSequence(2, 2); + bool anyDifferent = false; + for (int i = 0; i < 10 && !anyDifferent; i++) + { + var a = first.NextDouble(); + var b = second.NextDouble(); + for (int j = 0; j < 2; j++) + { + if (a[j] != b[j]) anyDifferent = true; + } + } + Assert.IsTrue(anyDifferent); + } + + /// + /// SkipTo on a scrambled sequence reproduces the sequential emissions under the class's + /// historical indexing, pinned as-is: the index positions the next emission, so SkipTo(0) + /// returns the first emitted point and SkipTo(i) for i >= 1 returns the i-th emitted + /// point (SkipTo(0) and SkipTo(1) coincide). The unrandomized sequence shares the contract. + /// + [TestMethod] + public void Test_ScrambledSobol_SkipTo_MatchesSequential() + { + foreach (int? seed in new int?[] { null, 42 }) + { + var sequential = seed.HasValue ? new SobolSequence(3, seed.Value) : new SobolSequence(3); + var points = new double[8][]; + for (int i = 0; i < 8; i++) + points[i] = sequential.NextDouble(); + + for (int i = 0; i < 8; i++) + { + var skipped = seed.HasValue ? new SobolSequence(3, seed.Value) : new SobolSequence(3); + var point = skipped.SkipTo(i); + var expected = points[i == 0 ? 0 : i - 1]; + for (int j = 0; j < 3; j++) + { + Assert.AreEqual(BitConverter.DoubleToInt64Bits(expected[j]), BitConverter.DoubleToInt64Bits(point[j])); + } + } + } + } + + /// + /// Linear matrix scrambling with a digital shift preserves the base-2 (0,1)-sequence + /// property of every dimension: each full dyadic block of 2^k consecutive points (standard + /// indices 2^k through 2^(k+1)-1; this generator drops the origin, so those are emitted + /// positions 2^k through 2^(k+1)-1) places exactly one point in each of the 2^k equal bins. + /// + [TestMethod] + public void Test_ScrambledSobol_PreservesDyadicStratification() + { + foreach (int? seed in new int?[] { null, 11, 12345 }) + { + for (int k = 1; k <= 7; k++) + { + var sobol = seed.HasValue ? new SobolSequence(3, seed.Value) : new SobolSequence(3); + int block = 1 << k; + // Discard emitted positions 1 .. 2^k - 1. + for (int i = 1; i < block; i++) + sobol.NextDouble(); + var counts = new int[3, block]; + for (int i = 0; i < block; i++) + { + var point = sobol.NextDouble(); + for (int j = 0; j < 3; j++) + { + int bin = (int)(point[j] * block); + counts[j, bin]++; + } + } + for (int j = 0; j < 3; j++) + { + for (int b = 0; b < block; b++) + { + Assert.AreEqual(1, counts[j, b], $"seed {seed}, k {k}, dimension {j}, bin {b}"); + } + } + } + } } + + /// + /// Scrambled marginals are uniform: over 4096 points each dimension's mean is near 1/2 and + /// variance near 1/12, far inside Monte Carlo tolerances at this sample size. + /// + [TestMethod] + public void Test_ScrambledSobol_MarginalUniformity() + { + var sobol = new SobolSequence(2, 777); + int n = 4096; + var sums = new double[2]; + var sumSquares = new double[2]; + for (int i = 0; i < n; i++) + { + var point = sobol.NextDouble(); + for (int j = 0; j < 2; j++) + { + sums[j] += point[j]; + sumSquares[j] += point[j] * point[j]; + } + } + for (int j = 0; j < 2; j++) + { + double mean = sums[j] / n; + double variance = sumSquares[j] / n - mean * mean; + Assert.AreEqual(0.5d, mean, 0.01d); + Assert.AreEqual(1d / 12d, variance, 0.005d); + } + } + + /// + /// Scrambled quasi-Monte Carlo beats plain Monte Carlo on a smooth integrand: across twenty + /// seeds at 1,024 points, the root-mean-square error of the scrambled-Sobol estimate of + /// the integral of x^2*e^y over the unit square must be under half the Monte Carlo RMSE + /// (the measured advantage is roughly an order of magnitude; one half is the guarded bound). + /// + [TestMethod] + public void Test_ScrambledSobol_VarianceReductionVsMonteCarlo() + { + double exact = (Math.E - 1d) / 3d; + int n = 1024; + double sumSqQmc = 0, sumSqMc = 0; + for (int seed = 101; seed <= 120; seed++) + { + var sobol = new SobolSequence(2, seed); + double qmc = 0; + for (int i = 0; i < n; i++) + { + var p = sobol.NextDouble(); + qmc += p[0] * p[0] * Math.Exp(p[1]); + } + qmc /= n; + sumSqQmc += (qmc - exact) * (qmc - exact); + + var prng = new MersenneTwister(seed); + double mc = 0; + for (int i = 0; i < n; i++) + { + double u1 = prng.NextDouble(); + double u2 = prng.NextDouble(); + mc += u1 * u1 * Math.Exp(u2); + } + mc /= n; + sumSqMc += (mc - exact) * (mc - exact); + } + double rmseQmc = Math.Sqrt(sumSqQmc / 20d); + double rmseMc = Math.Sqrt(sumSqMc / 20d); + Assert.IsLessThan(0.5d * rmseMc, rmseQmc); + } + } } From a2b5523ce26d55bf8f9daf00e65a8020dc3359ba Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Thu, 27 Aug 2026 12:17:56 -0600 Subject: [PATCH 126/222] Add the equicorrelated single-factor union and conditional-probability primitive UnionSingleFactor computes the probability of union under an equicorrelated single-factor Gaussian structure: conditional on one shared standard normal factor the events are independent, so the union collapses to a one-dimensional integral of a survival product - O(n) per node against the 2^n inclusion-exclusion of UnionPCM and UnionMVN. The integral is evaluated in probability space u = Phi(z) by adaptive Gauss-Kronrod quadrature over [1e-16, 1 - 1e-16], with the survival product accumulated through Log1p and recovered through a new Tools.Expm1 companion (compensated exp(x) - 1; net481 has no framework equivalent) so rare-event unions keep relative accuracy. The quadrature's absolute tolerance is pinned at the framework floor because acceptance is absolute-or-relative and the union can be arbitrarily small - left at its default, the absolute criterion governed and cost small unions three orders of relative accuracy, caught by the exact bivariate oracle. The correlation domain is [0, 1]: the comonotone limit returns the maximum marginal analytically, and a negative equicorrelation (positive semi-definite down to -1/(n-1)) has no real single-factor loading, so negative dependence is directed to the inclusion-exclusion methods. SingleFactorConditionalProbabilities fills a caller-owned buffer with the per-factor conditional probabilities so independent-event kernels can run conditionally per node under an outer quadrature, allocating nothing. Oracles: independence equals IndependentUnion; two events match p1 + p2 - Phi2(b1, b2; rho) through the closed-form bivariate normal CDF across correlations down to 1e-6 marginals; four events agree with UnionPCM and UnionMVN inside those methods' own accuracy; fifty heterogeneous rare marginals match a fixed-seed brute-force simulation of the factor construction at four standard errors; plus comonotone, monotonicity, Frechet-bound, rare-event self-consistency, single-event identity, buffer, and guard tests, and small-argument accuracy pins for Expm1 and Log1p. --- Numerics/Data/Statistics/Probability.cs | 141 ++++++++++++ Numerics/Utilities/Tools.cs | 24 ++ .../Data/Statistics/Test_Probability.cs | 209 ++++++++++++++++++ Test_Numerics/Utilities/Test_Tools.cs | 48 +++- 4 files changed, 421 insertions(+), 1 deletion(-) diff --git a/Numerics/Data/Statistics/Probability.cs b/Numerics/Data/Statistics/Probability.cs index ba59fdbc..c57d1be2 100644 --- a/Numerics/Data/Statistics/Probability.cs +++ b/Numerics/Data/Statistics/Probability.cs @@ -1,4 +1,5 @@ using Numerics.Distributions; +using Numerics.Mathematics.Integration; using Numerics.Mathematics.SpecialFunctions; using System; using System.Collections.Generic; @@ -686,6 +687,146 @@ public static double NegativelyDependentUnion(IList probabilities) return Tools.Clamp(Tools.Sum(probabilities), 0, 1); } + /// + /// Returns the probability of union under an equicorrelated single-factor Gaussian + /// dependence structure, where the events are conditionally independent given one shared + /// standard normal factor. + /// + /// List of marginal event probabilities. + /// The common correlation of the underlying Gaussian variates, within [0, 1]. + /// Optional. The relative tolerance of the quadrature. Default = 1E-8. + /// + /// + /// With thresholds b(i) = StandardZ(p(i)) and loading sqrt(rho) on the shared factor z, the + /// union is P = Integral of phi(z) * [1 - Product(1 - Phi((b(i) - sqrt(rho)*z)/sqrt(1-rho)))] dz. + /// Conditional independence makes each node O(n), rather than the 2^n inclusion-exclusion of + /// and + /// , so the method scales to very wide event + /// sets. The integral is evaluated in probability space u = Phi(z) with adaptive + /// Gauss-Kronrod quadrature over [1e-16, 1 - 1e-16] (the excluded endpoint slivers bound the + /// truncation error by 2e-16), and the conditional survival product is accumulated in log + /// space through and recovered through , + /// so rare-event unions retain relative accuracy. The result is deterministic. + /// + /// + /// The equicorrelated Gaussian correlation matrix is positive semi-definite down to + /// -1/(n-1), but a negative correlation has no real single-factor loading, so this method + /// requires rho within [0, 1]; use or + /// for negative dependence. At rho = 1 the comonotone limit, the maximum marginal probability, + /// is returned analytically; at rho = 0 the events are independent. Zero-probability events + /// carry no mass and any certain event returns one. + /// + /// + /// Thrown when the probabilities list is null or empty. + /// + /// Thrown when a probability is outside [0, 1], the correlation is outside [0, 1], or the + /// relative tolerance is outside the quadrature's accepted range of [1E-15, 1]. + /// + public static double UnionSingleFactor(IList probabilities, double rho, double relativeTolerance = 1E-8) + { + if (probabilities == null || probabilities.Count == 0) + throw new ArgumentException("The probabilities list must be non-null and contain at least one element."); + for (int i = 0; i < probabilities.Count; i++) + { + if (!Tools.IsFinite(probabilities[i]) || probabilities[i] < 0d || probabilities[i] > 1d) + throw new ArgumentOutOfRangeException(nameof(probabilities), "Probabilities must be finite and within [0, 1]."); + } + if (!Tools.IsFinite(rho) || rho < 0d || rho > 1d) + throw new ArgumentOutOfRangeException(nameof(rho), "The common correlation must be within [0, 1]. A negative equicorrelation (positive semi-definite down to -1/(n-1)) has no real single-factor loading; use UnionPCM or UnionMVN for negative dependence."); + + // Zero-probability events never occur; certainty and the comonotone limit are analytic. + double maxP = 0d; + int m = 0; + for (int i = 0; i < probabilities.Count; i++) + { + if (probabilities[i] >= 1d) return 1d; + if (probabilities[i] > maxP) maxP = probabilities[i]; + if (probabilities[i] > 0d) m++; + } + if (m == 0) return 0d; + if (rho == 1d) return maxP; + + var thresholds = new double[m]; + int j = 0; + for (int i = 0; i < probabilities.Count; i++) + { + if (probabilities[i] > 0d) thresholds[j++] = Normal.StandardZ(probabilities[i]); + } + + double sqrtRho = Math.Sqrt(rho); + double sqrtComplement = Math.Sqrt(1d - rho); + var conditional = new double[m]; + Func integrand = u => + { + double z = Normal.StandardZ(u); + SingleFactorConditionalCore(thresholds, sqrtRho, sqrtComplement, z, conditional); + double logSurvival = 0d; + for (int i = 0; i < m; i++) + logSurvival += Tools.Log1p(-conditional[i]); + return -Tools.Expm1(logSurvival); + }; + var quadrature = new AdaptiveGaussKronrod(integrand, 1E-16, 1d - 1E-16) + { + RelativeTolerance = relativeTolerance, + // The union can be arbitrarily small, and the quadrature accepts an interval on the + // absolute OR the relative criterion - pin the absolute tolerance at the framework + // floor so the relative criterion always governs. + AbsoluteTolerance = 1E-15, + MinDepth = 2, + ReportFailure = true + }; + quadrature.Integrate(); + return Tools.Clamp(quadrature.Result, 0d, 1d); + } + + /// + /// Fills a caller-owned buffer with the conditional event probabilities of the + /// equicorrelated single-factor Gaussian structure at a given factor value: + /// Phi((b(i) - sqrt(rho)*z)/sqrt(1-rho)) for each threshold b(i). + /// + /// The standard normal thresholds b(i) = StandardZ(p(i)), precomputed by the caller. + /// The common correlation of the underlying Gaussian variates, within [0, 1). + /// The shared standard normal factor value. + /// The caller-owned output buffer, at least as long as the thresholds. Entries beyond the threshold count are left untouched. + /// + /// Conditional on the factor the events are independent, so downstream combination kernels + /// built for independent probabilities can run per factor node with an outer quadrature + /// around them. The method allocates nothing. At rho = 1 the conditionals degenerate to + /// indicators of z against each threshold; handle that comonotone limit analytically rather + /// than through this method. Non-finite thresholds propagate through the normal CDF without + /// validation - the buffer fill is a hot path. + /// + /// Thrown when either array is null. + /// Thrown when the buffer is shorter than the thresholds. + /// Thrown when the correlation is outside [0, 1) or the factor value is not finite. + public static void SingleFactorConditionalProbabilities(double[] normalThresholds, double rho, double z, double[] conditional) + { + if (normalThresholds == null) throw new ArgumentNullException(nameof(normalThresholds)); + if (conditional == null) throw new ArgumentNullException(nameof(conditional)); + if (conditional.Length < normalThresholds.Length) + throw new ArgumentException("The conditional buffer must be at least as long as the thresholds.", nameof(conditional)); + if (!Tools.IsFinite(rho) || rho < 0d || rho >= 1d) + throw new ArgumentOutOfRangeException(nameof(rho), "The common correlation must be within [0, 1). At rho = 1 the conditional probabilities degenerate to indicators; handle the comonotone limit analytically."); + if (!Tools.IsFinite(z)) + throw new ArgumentOutOfRangeException(nameof(z), "The factor value must be finite."); + SingleFactorConditionalCore(normalThresholds, Math.Sqrt(rho), Math.Sqrt(1d - rho), z, conditional); + } + + /// + /// The unguarded conditional-probability fill shared by the union quadrature and the public + /// buffer helper. + /// + /// The standard normal thresholds. + /// The square root of the common correlation. + /// The square root of one minus the common correlation. + /// The shared standard normal factor value. + /// The output buffer. + private static void SingleFactorConditionalCore(double[] normalThresholds, double sqrtRho, double sqrtComplement, double z, double[] conditional) + { + for (int i = 0; i < normalThresholds.Length; i++) + conditional[i] = Normal.StandardCDF((normalThresholds[i] - sqrtRho * z) / sqrtComplement); + } + /// /// Returns the probability of union using the inclusion-exclusion method. Dependence between events is captured with the PCM method. /// diff --git a/Numerics/Utilities/Tools.cs b/Numerics/Utilities/Tools.cs index 41187b54..0be6afd9 100644 --- a/Numerics/Utilities/Tools.cs +++ b/Numerics/Utilities/Tools.cs @@ -226,6 +226,30 @@ public static double Log1p(double x) return Math.Log(y) - ((y - 1.0) - x) / y; } + /// + /// Computes exp(x) - 1 with improved numerical accuracy for small . + /// + /// The input value. + /// + /// exp(x) - 1. For values of near zero, this method avoids the + /// catastrophic cancellation that occurs in Math.Exp(x) - 1; deeply negative inputs + /// return exactly -1 and large inputs overflow to positive infinity. + /// + /// + /// Uses the compensated evaluation (u - 1) * x / log(u) with u = exp(x), which + /// corrects the rounding of the exponential; when u rounds to one the input itself is + /// returned. This is the companion of for log-space probability + /// arithmetic such as survival products of many small probabilities. + /// + public static double Expm1(double x) + { + double u = Math.Exp(x); + if (u == 1.0) return x; + if (double.IsPositiveInfinity(u)) return u; + if (u == 0.0) return -1.0; + return (u - 1.0) * x / Math.Log(u); + } + /// /// Returns the Euclidean distance between two points ||x - y||. /// diff --git a/Test_Numerics/Data/Statistics/Test_Probability.cs b/Test_Numerics/Data/Statistics/Test_Probability.cs index 77c4d430..37624a18 100644 --- a/Test_Numerics/Data/Statistics/Test_Probability.cs +++ b/Test_Numerics/Data/Statistics/Test_Probability.cs @@ -799,6 +799,215 @@ public void Test_ExclusiveABCD_NegativelyDependent_PCM() #endregion + #region Single-Factor Union + + /// + /// At zero correlation the single-factor union equals the independent union: the + /// conditional probabilities collapse to the marginals, so the quadrature integrates a + /// constant. + /// + [TestMethod] + public void Test_UnionSingleFactor_IndependenceMatchesIndependentUnion() + { + var ps = new double[] { 0.01d, 0.05d, 0.2d, 0.001d }; + double expected = Probability.IndependentUnion(ps); + Assert.AreEqual(expected, Probability.UnionSingleFactor(ps, 0d), 1E-12 * expected); + } + + /// + /// Exact bivariate oracle: for two events, P(union) = p1 + p2 - Phi2(b1, b2; rho) through + /// the closed-form bivariate normal CDF, across correlations and down to rare-event + /// marginals. The 1E-6 relative tolerance is dominated by the quadrature's 1E-8 relative + /// target with two orders of margin. + /// + [TestMethod] + public void Test_UnionSingleFactor_BivariateExactOracle() + { + var pairs = new (double p1, double p2)[] { (0.1d, 0.05d), (1E-4, 5E-4), (1E-6, 1E-5) }; + foreach (double rho in new[] { 0.1d, 0.5d, 0.9d }) + { + foreach (var (p1, p2) in pairs) + { + var mvn = new MultivariateNormal(new double[] { 0d, 0d }, new double[,] { { 1d, rho }, { rho, 1d } }); + double joint = mvn.CDF(new double[] { Normal.StandardZ(p1), Normal.StandardZ(p2) }); + double exact = p1 + p2 - joint; + double union = Probability.UnionSingleFactor(new[] { p1, p2 }, rho); + Assert.AreEqual(exact, union, 1E-6 * exact, $"rho {rho}, p1 {p1}, p2 {p2}"); + } + } + } + + /// + /// Small-n agreement with the inclusion-exclusion methods on the equicorrelated matrix. + /// The 2% relative tolerance reflects the comparison methods' own accuracy - UnionPCM is an + /// approximation and UnionMVN carries the randomized-lattice error of the MVN CDF above two + /// dimensions - not the precision of the single-factor quadrature. + /// + [TestMethod] + public void Test_UnionSingleFactor_SmallN_MatchesPCMAndMVN() + { + var ps = new double[] { 0.02d, 0.05d, 0.1d, 0.15d }; + double rho = 0.3d; + int n = ps.Length; + var correlation = new double[n, n]; + for (int i = 0; i < n; i++) + for (int j = 0; j < n; j++) + correlation[i, j] = i == j ? 1d : rho; + + double union = Probability.UnionSingleFactor(ps, rho); + double pcm = Probability.UnionPCM(ps, correlation); + var mvn = new MultivariateNormal(new double[n], correlation); + double viaMvn = Probability.UnionMVN(ps, mvn); + Assert.AreEqual(pcm, union, 0.02d * pcm); + Assert.AreEqual(viaMvn, union, 0.02d * viaMvn); + } + + /// + /// Wide-set Monte Carlo oracle: fifty heterogeneous rare marginals under the explicit + /// factor construction z(i) = sqrt(rho)*z0 + sqrt(1-rho)*e(i), simulated brute-force with a + /// fixed seed at 200,000 realizations and compared at four standard errors. + /// + [TestMethod] + public void Test_UnionSingleFactor_LargeN_MonteCarloOracle() + { + int n = 50; + var ps = new double[n]; + var betas = new double[n]; + for (int i = 0; i < n; i++) + { + ps[i] = 1E-4 * (i + 1); + betas[i] = Normal.StandardZ(ps[i]); + } + double rho = 0.3d; + double sqrtRho = Math.Sqrt(rho); + double sqrtComplement = Math.Sqrt(1d - rho); + + int realizations = 200000; + var prng = new MersenneTwister(12345); + int failures = 0; + for (int r = 0; r < realizations; r++) + { + double z0 = Normal.StandardZ(prng.NextDouble()); + bool any = false; + for (int i = 0; i < n; i++) + { + double e = Normal.StandardZ(prng.NextDouble()); + if (sqrtRho * z0 + sqrtComplement * e < betas[i]) any = true; + } + if (any) failures++; + } + double mc = (double)failures / realizations; + double se = Math.Sqrt(mc * (1d - mc) / realizations); + + double union = Probability.UnionSingleFactor(ps, rho); + Assert.AreEqual(mc, union, 4d * se); + } + + /// + /// Structural behavior across the correlation domain: the comonotone limit returns the + /// maximum marginal exactly, near-comonotone approaches it, the union decreases + /// monotonically in the correlation for identical marginals, the Frechet bounds hold at + /// every level, and a single event returns its own probability. + /// + [TestMethod] + public void Test_UnionSingleFactor_ComonotoneAndMonotone() + { + var ps = new double[] { 0.05d, 0.05d, 0.05d, 0.05d, 0.05d, 0.05d }; + Assert.AreEqual(0.05d, Probability.UnionSingleFactor(ps, 1d), 0d); + Assert.AreEqual(0.05d, Probability.UnionSingleFactor(ps, 0.999999d), 5E-3 * 0.05d); + + double sum = 0.3d; + double previous = double.PositiveInfinity; + foreach (double rho in new[] { 0d, 0.25d, 0.5d, 0.75d, 0.99d }) + { + double union = Probability.UnionSingleFactor(ps, rho); + Assert.IsLessThan(previous, union); + Assert.IsGreaterThanOrEqualTo(0.05d - 1E-12, union); + Assert.IsLessThanOrEqualTo(sum + 1E-12, union); + previous = union; + } + + // A single event integrates the Gaussian identity E[Phi((b - sqrt(rho) z)/sqrt(1-rho))] = p, + // recovered to the 1E-8 relative quadrature target with an order of headroom. + Assert.AreEqual(0.037d, Probability.UnionSingleFactor(new[] { 0.037d }, 0.5d), 1E-7 * 0.037d); + } + + /// + /// Rare-event depth: ten events at 1e-8 with moderate correlation stay inside the Frechet + /// bracket and are self-consistent across quadrature tolerances - the log-space survival + /// arithmetic keeps relative accuracy where naive products would lose it. + /// + [TestMethod] + public void Test_UnionSingleFactor_RareEvents() + { + var ps = new double[10]; + for (int i = 0; i < 10; i++) ps[i] = 1E-8; + double union = Probability.UnionSingleFactor(ps, 0.25d); + Assert.IsGreaterThan(1E-8, union); + Assert.IsLessThan(1E-7, union); + double refined = Probability.UnionSingleFactor(ps, 0.25d, 1E-10); + Assert.AreEqual(refined, union, 1E-6 * refined); + } + + /// + /// The conditional-probability buffer helper: zero correlation returns the marginals + /// through the z round trip, the fill matches the defining formula, entries beyond the + /// thresholds are untouched, and the guards throw on the comonotone and invalid inputs. + /// + [TestMethod] + public void Test_SingleFactorConditionalProbabilities() + { + var ps = new double[] { 0.01d, 0.2d, 0.7d }; + var betas = new double[3]; + for (int i = 0; i < 3; i++) betas[i] = Normal.StandardZ(ps[i]); + + var buffer = new double[5]; + buffer[3] = -99d; + buffer[4] = -99d; + Probability.SingleFactorConditionalProbabilities(betas, 0d, 1.7d, buffer); + for (int i = 0; i < 3; i++) + Assert.AreEqual(ps[i], buffer[i], 1E-14); + Assert.AreEqual(-99d, buffer[3], 0d); + Assert.AreEqual(-99d, buffer[4], 0d); + + double rho = 0.25d, z = -2d; + Probability.SingleFactorConditionalProbabilities(betas, rho, z, buffer); + for (int i = 0; i < 3; i++) + { + double expected = Normal.StandardCDF((betas[i] - Math.Sqrt(rho) * z) / Math.Sqrt(1d - rho)); + Assert.AreEqual(expected, buffer[i], 0d); + } + + Assert.Throws(() => Probability.SingleFactorConditionalProbabilities(null!, 0.5d, 0d, buffer)); + Assert.Throws(() => Probability.SingleFactorConditionalProbabilities(betas, 0.5d, 0d, null!)); + Assert.Throws(() => Probability.SingleFactorConditionalProbabilities(betas, 0.5d, 0d, new double[2])); + Assert.Throws(() => Probability.SingleFactorConditionalProbabilities(betas, 1d, 0d, buffer)); + Assert.Throws(() => Probability.SingleFactorConditionalProbabilities(betas, -0.1d, 0d, buffer)); + Assert.Throws(() => Probability.SingleFactorConditionalProbabilities(betas, 0.5d, double.NaN, buffer)); + } + + /// + /// Guard matrix for the single-factor union: invalid lists, probabilities, correlations, + /// and tolerances throw; certain events return one and all-zero lists return zero exactly. + /// + [TestMethod] + public void Test_UnionSingleFactor_GuardMatrix() + { + var ps = new double[] { 0.1d, 0.2d }; + Assert.Throws(() => Probability.UnionSingleFactor(null!, 0.5d)); + Assert.Throws(() => Probability.UnionSingleFactor(new double[0], 0.5d)); + Assert.Throws(() => Probability.UnionSingleFactor(new double[] { 0.1d, double.NaN }, 0.5d)); + Assert.Throws(() => Probability.UnionSingleFactor(new double[] { 0.1d, -0.1d }, 0.5d)); + Assert.Throws(() => Probability.UnionSingleFactor(new double[] { 0.1d, 1.1d }, 0.5d)); + Assert.Throws(() => Probability.UnionSingleFactor(ps, -0.1d)); + Assert.Throws(() => Probability.UnionSingleFactor(ps, 1.0000001d)); + Assert.Throws(() => Probability.UnionSingleFactor(ps, double.NaN)); + Assert.Throws(() => Probability.UnionSingleFactor(ps, 0.5d, 0d)); + Assert.AreEqual(1d, Probability.UnionSingleFactor(new double[] { 0.1d, 1d }, 0.5d), 0d); + Assert.AreEqual(0d, Probability.UnionSingleFactor(new double[] { 0d, 0d }, 0.5d), 0d); + } + + #endregion } } diff --git a/Test_Numerics/Utilities/Test_Tools.cs b/Test_Numerics/Utilities/Test_Tools.cs index 5dfbcb8c..68a428d8 100644 --- a/Test_Numerics/Utilities/Test_Tools.cs +++ b/Test_Numerics/Utilities/Test_Tools.cs @@ -601,6 +601,52 @@ public void Test_Decompress() var result = Tools.Decompress(data); Assert.IsGreaterThanOrEqualTo(result.Length, data.Length); } + + /// + /// Expm1 computes exp(x) - 1 without cancellation: tiny arguments return themselves + /// exactly, small arguments match the series exp(x) - 1 = x + x^2/2 + x^3/6 to full + /// precision, deep negatives saturate at exactly -1, large arguments overflow to positive + /// infinity, and the round trip with Log1p closes. + /// + [TestMethod] + public void Test_Expm1() + { + Assert.AreEqual(0d, Tools.Expm1(0d), 0d); + Assert.AreEqual(1E-18, Tools.Expm1(1E-18), 0d); + Assert.AreEqual(Math.E - 1d, Tools.Expm1(1d), 3E-16); + // Series references: 1e-8 + 0.5e-16 + 1.667e-25 and its negative-argument mirror. + Assert.AreEqual(1.0000000050000000167E-8, Tools.Expm1(1E-8), 1E-24); + Assert.AreEqual(-9.9999999500000002E-9, Tools.Expm1(-1E-8), 1E-24); + // Deep negatives: a subnormal exponential still resolves, and full underflow is exact. + Assert.AreEqual(-1d, Tools.Expm1(-746d), 1E-15); + Assert.AreEqual(-1d, Tools.Expm1(-800d), 0d); + Assert.AreEqual(-1d, Tools.Expm1(double.NegativeInfinity), 0d); + Assert.AreEqual(double.PositiveInfinity, Tools.Expm1(800d)); + Assert.AreEqual(double.PositiveInfinity, Tools.Expm1(double.PositiveInfinity)); + Assert.IsTrue(double.IsNaN(Tools.Expm1(double.NaN))); + + foreach (double x in new[] { 1E-12, 0.5d, 3d }) + { + Assert.AreEqual(x, Tools.Expm1(Tools.Log1p(x)), 1E-14 * x); + } + } + + /// + /// Log1p pins for the companion helper: tiny arguments return themselves exactly, small + /// arguments match the series log(1 + x) = x - x^2/2 + x^3/3 to full precision, and the + /// domain edges produce negative infinity at -1 and NaN below it. + /// + [TestMethod] + public void Test_Log1p() + { + Assert.AreEqual(0d, Tools.Log1p(0d), 0d); + Assert.AreEqual(1E-18, Tools.Log1p(1E-18), 0d); + Assert.AreEqual(Math.Log(2d), Tools.Log1p(1d), 3E-16); + Assert.AreEqual(9.9999999500000003E-9, Tools.Log1p(1E-8), 1E-24); + Assert.AreEqual(-1.00000000500000003E-8, Tools.Log1p(-1E-8), 1E-24); + Assert.AreEqual(double.NegativeInfinity, Tools.Log1p(-1d)); + Assert.IsTrue(double.IsNaN(Tools.Log1p(-1.5d))); + } } - + } From 7f03540aee58288ba342d863001ac77611570401 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Thu, 27 Aug 2026 12:22:50 -0600 Subject: [PATCH 127/222] Add given-data global sensitivity estimators GlobalSensitivity provides three deterministic estimators over stored input-output sample pairs, conditioning through equal-frequency rank bins with no special sampling design and no randomness: the Plischke-style given-data first-order Sobol index (variance of conditional bin means over output variance, clamped to [0, 1]), the PAWN indices (per-bin exact two-sample Kolmogorov-Smirnov statistics between the conditional and unconditional output distributions, with the customary median convenience), and the Borgonovo delta by the double-histogram total-variation estimator (half the bin-weighted total variation between conditional and unconditional equal-frequency class histograms). The rank-based class partition makes the delta exactly invariant to strictly increasing output transforms - its defining property - and each estimator's finite-sample bias scale is documented so near-zero readings are interpreted honestly. Oracles: the analytic Ishigami variance decomposition (a = 7, b = 0.1) and the analytic Sobol-g indices (a = 0, 1, 4.5, 9) on fixed-seed samples; independent inputs near zero at each estimator's bias scale; a functional dependence saturating the Sobol index, the PAWN median, and the delta at its exact 1 - 1/yBins value on a divisible grid; bit-identical monotone-transform invariance and repeat-call determinism; and a guard matrix. --- Numerics/Data/Statistics/GlobalSensitivity.cs | 271 ++++++++++++++++++ .../Data/Statistics/Test_GlobalSensitivity.cs | 206 +++++++++++++ 2 files changed, 477 insertions(+) create mode 100644 Numerics/Data/Statistics/GlobalSensitivity.cs create mode 100644 Test_Numerics/Data/Statistics/Test_GlobalSensitivity.cs diff --git a/Numerics/Data/Statistics/GlobalSensitivity.cs b/Numerics/Data/Statistics/GlobalSensitivity.cs new file mode 100644 index 00000000..3e0e1b49 --- /dev/null +++ b/Numerics/Data/Statistics/GlobalSensitivity.cs @@ -0,0 +1,271 @@ +using System; +using System.Collections.Generic; + +namespace Numerics.Data.Statistics +{ + /// + /// Given-data global sensitivity estimators over paired input-output samples. + /// + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + /// Description: + /// These estimators require no special sampling design: they operate on any stored sample of + /// one input against the output, conditioning through equal-frequency bins of the input. The + /// first-order Sobol index measures the share of output variance explained by the input alone, + /// the PAWN indices measure conditional-versus-unconditional distribution shifts through + /// Kolmogorov-Smirnov statistics (sensitive to changes a variance ratio misses), and the + /// Borgonovo delta is a moment-independent total-variation measure suited to tail-driven + /// outputs. All three are deterministic: binning is by rank with ties resolved by the sort's + /// deterministic order, and no randomness is used anywhere. + /// + /// References: + /// + /// Plischke, E., Borgonovo, E., and Smith, C. L. (2013). Global sensitivity measures from given data. European Journal of Operational Research, 226(3), 536-550. + /// Pianosi, F., and Wagener, T. (2015). A simple and efficient method for global sensitivity analysis based on cumulative distribution functions. Environmental Modelling and Software, 67, 1-11. + /// Borgonovo, E. (2007). A new uncertainty importance measure. Reliability Engineering and System Safety, 92(6), 771-784. + /// + /// + public static class GlobalSensitivity + { + + /// + /// Estimates the first-order Sobol index of an input from given data: the variance of the + /// conditional output mean across equal-frequency input bins, over the output variance. + /// + /// The input sample. + /// The output sample, aligned with the input. + /// Optional. The number of equal-frequency input bins. Default = 20. + /// The first-order index, clamped to [0, 1]. + /// + /// The estimator carries a positive finite-sample bias on the order of the bin count over + /// the sample size, so an independent input reads near zero only when the sample is much + /// larger than the bin count. A degenerate output with zero variance returns zero. + /// + /// Thrown when either sample is null. + /// Thrown when the samples differ in length or are shorter than the bin count. + /// Thrown when the bin count is less than two, or a sample value is not finite. + public static double FirstOrderSobol(IList x, IList y, int bins = 20) + { + ValidateSamples(x, y, bins); + int n = x.Count; + var order = SortIndicesBy(x); + + double mean = 0; + for (int i = 0; i < n; i++) + mean += y[i]; + mean /= n; + double totalVariance = 0; + for (int i = 0; i < n; i++) + { + double d = y[i] - mean; + totalVariance += d * d; + } + totalVariance /= n; + if (totalVariance <= 0d) return 0d; + + double betweenVariance = 0; + for (int b = 0; b < bins; b++) + { + int start = (int)((long)b * n / bins); + int end = (int)((long)(b + 1) * n / bins); + double binMean = 0; + for (int i = start; i < end; i++) + binMean += y[order[i]]; + binMean /= end - start; + double d = binMean - mean; + betweenVariance += (end - start) * d * d; + } + betweenVariance /= n; + return Tools.Clamp(betweenVariance / totalVariance, 0d, 1d); + } + + /// + /// Estimates the PAWN sensitivity statistics of an input from given data: for each + /// equal-frequency input bin, the two-sample Kolmogorov-Smirnov statistic between the + /// conditional output distribution in the bin and the unconditional output distribution. + /// + /// The input sample. + /// The output sample, aligned with the input. + /// Optional. The number of equal-frequency input bins. Default = 20. + /// The per-bin Kolmogorov-Smirnov statistics, in bin order. + /// + /// The caller summarizes the per-bin statistics as the application demands - the median is + /// the customary index and provides it directly. An independent + /// input yields statistics at the two-sample sampling-noise scale rather than exactly zero. + /// + /// Thrown when either sample is null. + /// Thrown when the samples differ in length or are shorter than the bin count. + /// Thrown when the bin count is less than two, or a sample value is not finite. + public static double[] Pawn(IList x, IList y, int bins = 20) + { + ValidateSamples(x, y, bins); + int n = x.Count; + var order = SortIndicesBy(x); + + var sortedY = new double[n]; + for (int i = 0; i < n; i++) + sortedY[i] = y[i]; + Array.Sort(sortedY); + + var result = new double[bins]; + for (int b = 0; b < bins; b++) + { + int start = (int)((long)b * n / bins); + int end = (int)((long)(b + 1) * n / bins); + int size = end - start; + var binY = new double[size]; + for (int i = start; i < end; i++) + binY[i - start] = y[order[i]]; + Array.Sort(binY); + result[b] = TwoSampleKolmogorovSmirnov(binY, sortedY); + } + return result; + } + + /// + /// Estimates the customary PAWN index of an input from given data: the median of the + /// per-bin Kolmogorov-Smirnov statistics of . + /// + /// The input sample. + /// The output sample, aligned with the input. + /// Optional. The number of equal-frequency input bins. Default = 20. + /// The median Kolmogorov-Smirnov statistic. + /// Thrown when either sample is null. + /// Thrown when the samples differ in length or are shorter than the bin count. + /// Thrown when the bin count is less than two, or a sample value is not finite. + public static double PawnMedian(IList x, IList y, int bins = 20) + { + return Statistics.Percentile(Pawn(x, y, bins), 0.5d); + } + + /// + /// Estimates the Borgonovo delta of an input from given data with the double-histogram + /// total-variation estimator: the output is partitioned into equal-frequency rank classes, + /// and delta is half the bin-weighted total variation between the conditional and + /// unconditional class frequencies. + /// + /// The input sample. + /// The output sample, aligned with the input. + /// Optional. The number of equal-frequency input bins. Default = 20. + /// Optional. The number of equal-frequency output classes. Default = 20. + /// The delta estimate, within [0, 1 - 1/yBins]. + /// + /// The class partition is rank-based, so the estimate is exactly invariant to strictly + /// increasing transforms of the output - the defining property of the delta measure. A + /// functionally dependent output saturates at 1 - 1/yBins, and an independent input carries + /// a positive finite-sample bias on the order of the square root of the class count over the + /// bin size, so small deltas require samples much larger than the histogram resolution. + /// + /// Thrown when either sample is null. + /// Thrown when the samples differ in length or are shorter than either bin count. + /// Thrown when either bin count is less than two, or a sample value is not finite. + public static double BorgonovoDelta(IList x, IList y, int xBins = 20, int yBins = 20) + { + ValidateSamples(x, y, xBins); + if (yBins < 2) throw new ArgumentOutOfRangeException(nameof(yBins), "The class count must be at least two."); + int n = x.Count; + if (n < yBins) throw new ArgumentException("The sample must be at least as long as the class count.", nameof(yBins)); + var xOrder = SortIndicesBy(x); + var yOrder = SortIndicesBy(y); + + // Assign each observation its equal-frequency output class by rank. + var yClass = new int[n]; + var classCounts = new int[yBins]; + for (int c = 0; c < yBins; c++) + { + int start = (int)((long)c * n / yBins); + int end = (int)((long)(c + 1) * n / yBins); + classCounts[c] = end - start; + for (int i = start; i < end; i++) + yClass[yOrder[i]] = c; + } + + double delta = 0; + var conditionalCounts = new int[yBins]; + for (int b = 0; b < xBins; b++) + { + int start = (int)((long)b * n / xBins); + int end = (int)((long)(b + 1) * n / xBins); + int size = end - start; + Array.Clear(conditionalCounts, 0, yBins); + for (int i = start; i < end; i++) + conditionalCounts[yClass[xOrder[i]]]++; + double totalVariation = 0; + for (int c = 0; c < yBins; c++) + totalVariation += Math.Abs((double)conditionalCounts[c] / size - (double)classCounts[c] / n); + delta += (double)size / n * totalVariation; + } + return 0.5d * delta; + } + + /// + /// Validates a paired sample and bin count. + /// + /// The input sample. + /// The output sample. + /// The bin count. + private static void ValidateSamples(IList x, IList y, int bins) + { + if (x == null) throw new ArgumentNullException(nameof(x)); + if (y == null) throw new ArgumentNullException(nameof(y)); + if (x.Count != y.Count) throw new ArgumentException("The input and output samples must have the same length.", nameof(y)); + if (bins < 2) throw new ArgumentOutOfRangeException(nameof(bins), "The bin count must be at least two."); + if (x.Count < bins) throw new ArgumentException("The sample must be at least as long as the bin count.", nameof(x)); + for (int i = 0; i < x.Count; i++) + { + if (!Tools.IsFinite(x[i]) || !Tools.IsFinite(y[i])) + throw new ArgumentOutOfRangeException(nameof(x), "Sample values must be finite."); + } + } + + /// + /// Returns the sample indices sorted ascending by value. Ties keep the sort's deterministic + /// order, so identical inputs always produce identical bin assignments. + /// + /// The values to rank. + /// The sorted index array. + private static int[] SortIndicesBy(IList values) + { + int n = values.Count; + var keys = new double[n]; + var order = new int[n]; + for (int i = 0; i < n; i++) + { + keys[i] = values[i]; + order[i] = i; + } + Array.Sort(keys, order); + return order; + } + + /// + /// Computes the exact two-sample Kolmogorov-Smirnov statistic between two sorted samples, + /// evaluating the gap only after advancing both empirical distributions through tied values. + /// + /// The first sorted sample. + /// The second sorted sample. + /// The supremum absolute difference of the empirical distributions. + private static double TwoSampleKolmogorovSmirnov(double[] first, double[] second) + { + int n1 = first.Length, n2 = second.Length; + int i = 0, j = 0; + double supremum = 0; + while (i < n1 && j < n2) + { + double value = Math.Min(first[i], second[j]); + while (i < n1 && first[i] == value) i++; + while (j < n2 && second[j] == value) j++; + double gap = Math.Abs((double)i / n1 - (double)j / n2); + if (gap > supremum) supremum = gap; + } + // The remaining tail of either sample only shrinks toward equality at one, and the gap + // at the crossover was already recorded, so the sweep is complete. + return supremum; + } + + } +} diff --git a/Test_Numerics/Data/Statistics/Test_GlobalSensitivity.cs b/Test_Numerics/Data/Statistics/Test_GlobalSensitivity.cs new file mode 100644 index 00000000..94504d82 --- /dev/null +++ b/Test_Numerics/Data/Statistics/Test_GlobalSensitivity.cs @@ -0,0 +1,206 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Data.Statistics; +using Numerics.Distributions; +using Numerics.Sampling; +using System; + +namespace Data.Statistics +{ + /// + /// Unit tests for the given-data global sensitivity estimators. + /// + /// + /// Authors: + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + [TestClass] + public class Test_GlobalSensitivity + { + + /// + /// Generates the fixed-seed Ishigami sample: y = sin(x1) + a*sin(x2)^2 + b*x3^4*sin(x1) + /// with the inputs uniform on (-pi, pi). + /// + /// The sample size. + /// Output. The first input sample. + /// Output. The second input sample. + /// Output. The third input sample. + /// Output. The output sample. + private static void IshigamiSample(int n, out double[] x1, out double[] x2, out double[] x3, out double[] y) + { + const double a = 7d, b = 0.1d; + var prng = new MersenneTwister(12345); + x1 = new double[n]; + x2 = new double[n]; + x3 = new double[n]; + y = new double[n]; + for (int i = 0; i < n; i++) + { + x1[i] = -Math.PI + 2d * Math.PI * prng.NextDouble(); + x2[i] = -Math.PI + 2d * Math.PI * prng.NextDouble(); + x3[i] = -Math.PI + 2d * Math.PI * prng.NextDouble(); + y[i] = Math.Sin(x1[i]) + a * Math.Sin(x2[i]) * Math.Sin(x2[i]) + b * Math.Pow(x3[i], 4) * Math.Sin(x1[i]); + } + } + + /// + /// The given-data first-order Sobol estimator recovers the analytic Ishigami indices + /// (a = 7, b = 0.1): S1 = V1/V and S2 = V2/V from the closed-form variance decomposition, + /// and S3 = 0 (the third input acts only through interaction). The 0.03 tolerance covers + /// the estimator's binning bias and the fixed-seed Monte Carlo noise at 2^14 samples. + /// + [TestMethod] + public void Test_FirstOrderSobol_IshigamiOracle() + { + const double a = 7d, b = 0.1d; + double pi4 = Math.Pow(Math.PI, 4); + double v1 = 0.5d * Math.Pow(1d + b * pi4 / 5d, 2); + double v2 = a * a / 8d; + double v = 0.5d + a * a / 8d + b * pi4 / 5d + b * b * Math.Pow(Math.PI, 8) / 18d; + + IshigamiSample(16384, out var x1, out var x2, out var x3, out var y); + Assert.AreEqual(v1 / v, GlobalSensitivity.FirstOrderSobol(x1, y), 0.03d); + Assert.AreEqual(v2 / v, GlobalSensitivity.FirstOrderSobol(x2, y), 0.03d); + Assert.IsLessThan(0.02d, GlobalSensitivity.FirstOrderSobol(x3, y)); + } + + /// + /// The given-data first-order Sobol estimator recovers the analytic Sobol-g indices for + /// a = (0, 1, 4.5, 9): V(i) = 1/(3*(1+a(i))^2) and V = prod(1+V(i)) - 1. + /// + [TestMethod] + public void Test_FirstOrderSobol_SobolGOracle() + { + var a = new double[] { 0d, 1d, 4.5d, 9d }; + int n = 16384; + var prng = new MersenneTwister(45678); + var xs = new double[4][]; + for (int j = 0; j < 4; j++) xs[j] = new double[n]; + var y = new double[n]; + for (int i = 0; i < n; i++) + { + double product = 1d; + for (int j = 0; j < 4; j++) + { + double u = prng.NextDouble(); + xs[j][i] = u; + product *= (Math.Abs(4d * u - 2d) + a[j]) / (1d + a[j]); + } + y[i] = product; + } + + double v = 1d; + var vi = new double[4]; + for (int j = 0; j < 4; j++) + { + vi[j] = 1d / (3d * (1d + a[j]) * (1d + a[j])); + v *= 1d + vi[j]; + } + v -= 1d; + for (int j = 0; j < 4; j++) + { + Assert.AreEqual(vi[j] / v, GlobalSensitivity.FirstOrderSobol(xs[j], y), 0.03d, $"input {j}"); + } + } + + /// + /// An independent input reads near zero on all three estimators, at each estimator's own + /// finite-sample bias scale: the Sobol index at the bin-count-over-sample-size scale, the + /// PAWN median at the two-sample Kolmogorov-Smirnov noise scale, and the double-histogram + /// delta at the class-fluctuation scale (the largest of the three by construction). + /// + [TestMethod] + public void Test_IndependentInput_NearZero() + { + int n = 16384; + var prng = new MersenneTwister(999); + var x = new double[n]; + var y = new double[n]; + for (int i = 0; i < n; i++) + { + x[i] = prng.NextDouble(); + y[i] = Normal.StandardZ(prng.NextDouble()); + } + Assert.IsLessThan(0.02d, GlobalSensitivity.FirstOrderSobol(x, y)); + Assert.IsLessThan(0.06d, GlobalSensitivity.PawnMedian(x, y)); + Assert.IsLessThan(0.1d, GlobalSensitivity.BorgonovoDelta(x, y)); + } + + /// + /// A functionally dependent output saturates the estimators: on the exact grid y = x with + /// the sample size divisible by the bin counts, the Sobol index is essentially one, every + /// conditional distribution is far from the unconditional one, and the double-histogram + /// delta hits its exact saturation value 1 - 1/yBins because each input bin maps onto + /// exactly one output class. + /// + [TestMethod] + public void Test_FunctionalDependence_Saturates() + { + int n = 1000; + var x = new double[n]; + var y = new double[n]; + for (int i = 0; i < n; i++) + { + x[i] = i + 0.5d; + y[i] = x[i]; + } + Assert.IsGreaterThan(0.99d, GlobalSensitivity.FirstOrderSobol(x, y)); + Assert.IsGreaterThanOrEqualTo(0.45d, GlobalSensitivity.PawnMedian(x, y)); + Assert.AreEqual(1d - 1d / 20d, GlobalSensitivity.BorgonovoDelta(x, y), 1E-12); + } + + /// + /// The Borgonovo delta is exactly invariant to strictly increasing transforms of the + /// output - the class partition is rank-based, so cubing the Ishigami output changes no + /// class assignment and the estimate is bit-identical. + /// + [TestMethod] + public void Test_BorgonovoDelta_MonotoneTransformInvariant() + { + IshigamiSample(4096, out var x1, out _, out _, out var y); + var cubed = new double[y.Length]; + for (int i = 0; i < y.Length; i++) + cubed[i] = y[i] * y[i] * y[i]; + Assert.AreEqual(GlobalSensitivity.BorgonovoDelta(x1, y), GlobalSensitivity.BorgonovoDelta(x1, cubed), 0d); + } + + /// + /// The estimators are deterministic: repeated calls on the same sample are bit-identical. + /// + [TestMethod] + public void Test_Determinism_RepeatedCallsBitEqual() + { + IshigamiSample(4096, out var x1, out _, out _, out var y); + Assert.AreEqual(GlobalSensitivity.FirstOrderSobol(x1, y), GlobalSensitivity.FirstOrderSobol(x1, y), 0d); + Assert.AreEqual(GlobalSensitivity.PawnMedian(x1, y), GlobalSensitivity.PawnMedian(x1, y), 0d); + Assert.AreEqual(GlobalSensitivity.BorgonovoDelta(x1, y), GlobalSensitivity.BorgonovoDelta(x1, y), 0d); + var first = GlobalSensitivity.Pawn(x1, y); + var second = GlobalSensitivity.Pawn(x1, y); + for (int b = 0; b < first.Length; b++) + Assert.AreEqual(first[b], second[b], 0d); + } + + /// + /// Guard matrix: nulls, length mismatches, degenerate bin counts, samples shorter than the + /// bin counts, and non-finite values all throw the documented exceptions, and a + /// zero-variance output returns a zero Sobol index. + /// + [TestMethod] + public void Test_GuardMatrix() + { + var x = new double[] { 1d, 2d, 3d, 4d }; + var y = new double[] { 5d, 6d, 7d, 8d }; + Assert.Throws(() => GlobalSensitivity.FirstOrderSobol(null!, y, 2)); + Assert.Throws(() => GlobalSensitivity.FirstOrderSobol(x, null!, 2)); + Assert.Throws(() => GlobalSensitivity.FirstOrderSobol(x, new double[] { 1d }, 2)); + Assert.Throws(() => GlobalSensitivity.FirstOrderSobol(x, y, 1)); + Assert.Throws(() => GlobalSensitivity.FirstOrderSobol(x, y, 5)); + Assert.Throws(() => GlobalSensitivity.FirstOrderSobol(new double[] { 1d, double.NaN, 3d, 4d }, y, 2)); + Assert.Throws(() => GlobalSensitivity.Pawn(x, new double[] { 1d, 2d, double.PositiveInfinity, 4d }, 2)); + Assert.Throws(() => GlobalSensitivity.BorgonovoDelta(x, y, 2, 1)); + Assert.Throws(() => GlobalSensitivity.BorgonovoDelta(x, y, 2, 5)); + Assert.AreEqual(0d, GlobalSensitivity.FirstOrderSobol(x, new double[] { 3d, 3d, 3d, 3d }, 2), 0d); + } + + } +} From dc5b17c01d2151cbf3468df8adbec7bb3c13f528 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Thu, 27 Aug 2026 12:52:02 -0600 Subject: [PATCH 128/222] Preserve the ordered-paired-data lookup binary signatures The extrapolation parameter moved from an appended optional parameter to a distinct four-argument overload on GetYFromX and GetXFromY (scalar and list), with the historical three-argument signatures restored verbatim and forwarding to the new overloads with ExtrapolationSides.None. An optional parameter changes the compiled method signature, so assemblies built against earlier releases would fail with MissingMethodException on these four methods until recompiled - surfaced by running a consumer binary compiled against the previous signature. Behavior is unchanged: the endpoint-hold regression pins and the extrapolation battery cover both overload forms. --- .../Data/Paired Data/OrderedPairedData.cs | 114 ++++++++++++++---- .../Test_PairedDataInterpolation.cs | 40 +++--- 2 files changed, 112 insertions(+), 42 deletions(-) diff --git a/Numerics/Data/Paired Data/OrderedPairedData.cs b/Numerics/Data/Paired Data/OrderedPairedData.cs index 1384e8a8..aa1d67e0 100644 --- a/Numerics/Data/Paired Data/OrderedPairedData.cs +++ b/Numerics/Data/Paired Data/OrderedPairedData.cs @@ -859,21 +859,40 @@ private double BaseInterpolate(double value, int index, bool givenX = true, Tran /// The x value. /// Optional. Transform for the x values. Default = None. /// Optional. Transform for the y values. Default = None. + /// The interpolated value. + /// + /// Out-of-range lookups hold the boundary ordinate; the four-argument overload can + /// extrapolate instead. Sides there are defined in value space regardless of the sort + /// orientation: Below is beyond the minimum x and Above beyond the maximum. Exactly at an + /// endpoint the boundary ordinate is returned unchanged, a single-point table always + /// holds, and a plateau (equal boundary ordinates in transform space) extends at slope + /// zero. Extrapolation on an untransformed axis is unbounded, so a caller holding a + /// bounded quantity such as a probability must clamp the result or use the NormalZ + /// transform, which is bounded by construction. + /// + public double GetYFromX(double x, Transform xTransform = Transform.None, Transform yTransform = Transform.None) + { + return GetYFromX(x, xTransform, yTransform, ExtrapolationSides.None); + } + + /// + /// Interpolate y from x, extrapolating on the requested sides of the x-range. + /// + /// The x value. + /// Transform for the x values. + /// Transform for the y values. /// - /// Optional. The sides of the x-range on which an out-of-range lookup extends the boundary - /// segment linearly in the configured transform space rather than holding the boundary - /// ordinate. Default = None, the historical endpoint hold. + /// The sides of the x-range on which an out-of-range lookup extends the boundary segment + /// linearly in the configured transform space rather than holding the boundary ordinate. + /// None reproduces the historical endpoint hold. /// /// The interpolated value. /// - /// Sides are defined in value space regardless of the sort orientation: Below is beyond the - /// minimum x and Above beyond the maximum. Exactly at an endpoint the boundary ordinate is - /// returned unchanged, a single-point table always holds, and a plateau (equal boundary - /// ordinates in transform space) extends at slope zero. Extrapolation on an untransformed - /// axis is unbounded, so a caller holding a bounded quantity such as a probability must - /// clamp the result or use the NormalZ transform, which is bounded by construction. + /// This is a distinct overload rather than an optional parameter so the historical + /// three-argument signature keeps binary compatibility with assemblies compiled against + /// earlier releases. /// - public double GetYFromX(double x, Transform xTransform = Transform.None, Transform yTransform = Transform.None, ExtrapolationSides extrapolation = ExtrapolationSides.None) + public double GetYFromX(double x, Transform xTransform, Transform yTransform, ExtrapolationSides extrapolation) { if (Count == 0) return double.NaN; if (OrderX == SortOrder.None) @@ -907,19 +926,38 @@ public double GetYFromX(double x, Transform xTransform = Transform.None, Transfo /// The y value. /// Optional. Transform for the x values. Default = None. /// Optional. Transform for the y values. Default = None. + /// The interpolated value. + /// + /// Out-of-range lookups hold the boundary ordinate; the four-argument overload can + /// extrapolate instead. Sides there are defined in value space regardless of the sort + /// orientation: Below is beyond the minimum y and Above beyond the maximum. Exactly at an + /// endpoint the boundary ordinate is returned unchanged, a single-point table always + /// holds, and a plateau (equal boundary ordinates in transform space) extends at slope + /// zero. + /// + public double GetXFromY(double y, Transform xTransform = Transform.None, Transform yTransform = Transform.None) + { + return GetXFromY(y, xTransform, yTransform, ExtrapolationSides.None); + } + + /// + /// Interpolate x from y, extrapolating on the requested sides of the y-range. + /// + /// The y value. + /// Transform for the x values. + /// Transform for the y values. /// - /// Optional. The sides of the y-range on which an out-of-range lookup extends the boundary - /// segment linearly in the configured transform space rather than holding the boundary - /// ordinate. Default = None, the historical endpoint hold. + /// The sides of the y-range on which an out-of-range lookup extends the boundary segment + /// linearly in the configured transform space rather than holding the boundary ordinate. + /// None reproduces the historical endpoint hold. /// /// The interpolated value. /// - /// Sides are defined in value space regardless of the sort orientation: Below is beyond the - /// minimum y and Above beyond the maximum. Exactly at an endpoint the boundary ordinate is - /// returned unchanged, a single-point table always holds, and a plateau (equal boundary - /// ordinates in transform space) extends at slope zero. + /// This is a distinct overload rather than an optional parameter so the historical + /// three-argument signature keeps binary compatibility with assemblies compiled against + /// earlier releases. /// - public double GetXFromY(double y, Transform xTransform = Transform.None, Transform yTransform = Transform.None, ExtrapolationSides extrapolation = ExtrapolationSides.None) + public double GetXFromY(double y, Transform xTransform, Transform yTransform, ExtrapolationSides extrapolation) { if (Count == 0) return double.NaN; if (OrderY == SortOrder.None) @@ -952,9 +990,25 @@ public double GetXFromY(double y, Transform xTransform = Transform.None, Transfo /// The list of x-values. /// Optional. Transform for the x values. Default = None. /// Optional. Transform for the y values. Default = None. - /// Optional. The sides of the x-range on which out-of-range lookups extrapolate. Default = None, the historical endpoint hold. /// An array of interpolated values. - public double[] GetYFromX(IList xValues, Transform xTransform = Transform.None, Transform yTransform = Transform.None, ExtrapolationSides extrapolation = ExtrapolationSides.None) + public double[] GetYFromX(IList xValues, Transform xTransform = Transform.None, Transform yTransform = Transform.None) + { + return GetYFromX(xValues, xTransform, yTransform, ExtrapolationSides.None); + } + + /// + /// Interpolate y-values from a list of x-values, extrapolating on the requested sides of the x-range. + /// + /// The list of x-values. + /// Transform for the x values. + /// Transform for the y values. + /// The sides of the x-range on which out-of-range lookups extrapolate. None reproduces the historical endpoint hold. + /// An array of interpolated values. + /// + /// A distinct overload rather than an optional parameter, preserving the historical + /// three-argument signature's binary compatibility. + /// + public double[] GetYFromX(IList xValues, Transform xTransform, Transform yTransform, ExtrapolationSides extrapolation) { var result = new double[xValues.Count]; for (int i = 0; i < xValues.Count; i++) @@ -968,9 +1022,25 @@ public double[] GetYFromX(IList xValues, Transform xTransform = Transfor /// The list of y-values. /// Optional. Transform for the x values. Default = None. /// Optional. Transform for the y values. Default = None. - /// Optional. The sides of the y-range on which out-of-range lookups extrapolate. Default = None, the historical endpoint hold. /// An array of interpolated values. - public double[] GetXFromY(IList yValues, Transform xTransform = Transform.None, Transform yTransform = Transform.None, ExtrapolationSides extrapolation = ExtrapolationSides.None) + public double[] GetXFromY(IList yValues, Transform xTransform = Transform.None, Transform yTransform = Transform.None) + { + return GetXFromY(yValues, xTransform, yTransform, ExtrapolationSides.None); + } + + /// + /// Interpolate x-values from a list of y-values, extrapolating on the requested sides of the y-range. + /// + /// The list of y-values. + /// Transform for the x values. + /// Transform for the y values. + /// The sides of the y-range on which out-of-range lookups extrapolate. None reproduces the historical endpoint hold. + /// An array of interpolated values. + /// + /// A distinct overload rather than an optional parameter, preserving the historical + /// three-argument signature's binary compatibility. + /// + public double[] GetXFromY(IList yValues, Transform xTransform, Transform yTransform, ExtrapolationSides extrapolation) { var result = new double[yValues.Count]; for (int i = 0; i < yValues.Count; i++) diff --git a/Test_Numerics/Data/Paired Data/Test_PairedDataInterpolation.cs b/Test_Numerics/Data/Paired Data/Test_PairedDataInterpolation.cs index 171bd954..c5c3906f 100644 --- a/Test_Numerics/Data/Paired Data/Test_PairedDataInterpolation.cs +++ b/Test_Numerics/Data/Paired Data/Test_PairedDataInterpolation.cs @@ -469,25 +469,25 @@ public void Test_Extrapolation_LinearBothEnds_NoTransform() { var asc = new OrderedPairedData(new double[] { 1d, 2d, 4d, 8d }, new double[] { 10d, 20d, 40d, 80d }, true, SortOrder.Ascending, true, SortOrder.Ascending); // Below: through (1,10)-(2,20) at x = 0.5 -> 5. Above: through (4,40)-(8,80) at x = 10 -> 100. - Assert.AreEqual(5d, asc.GetYFromX(0.5d, extrapolation: ExtrapolationSides.Both), 1E-12); - Assert.AreEqual(100d, asc.GetYFromX(10d, extrapolation: ExtrapolationSides.Both), 1E-12); + Assert.AreEqual(5d, asc.GetYFromX(0.5d, Transform.None, Transform.None, ExtrapolationSides.Both), 1E-12); + Assert.AreEqual(100d, asc.GetYFromX(10d, Transform.None, Transform.None, ExtrapolationSides.Both), 1E-12); // Side selectivity: the unrequested side still holds. - Assert.AreEqual(5d, asc.GetYFromX(0.5d, extrapolation: ExtrapolationSides.Below), 1E-12); - Assert.AreEqual(80d, asc.GetYFromX(10d, extrapolation: ExtrapolationSides.Below), 0d); - Assert.AreEqual(10d, asc.GetYFromX(0.5d, extrapolation: ExtrapolationSides.Above), 0d); - Assert.AreEqual(100d, asc.GetYFromX(10d, extrapolation: ExtrapolationSides.Above), 1E-12); + Assert.AreEqual(5d, asc.GetYFromX(0.5d, Transform.None, Transform.None, ExtrapolationSides.Below), 1E-12); + Assert.AreEqual(80d, asc.GetYFromX(10d, Transform.None, Transform.None, ExtrapolationSides.Below), 0d); + Assert.AreEqual(10d, asc.GetYFromX(0.5d, Transform.None, Transform.None, ExtrapolationSides.Above), 0d); + Assert.AreEqual(100d, asc.GetYFromX(10d, Transform.None, Transform.None, ExtrapolationSides.Above), 1E-12); // Exactly at the endpoints the boundary ordinate is returned unchanged. - Assert.AreEqual(10d, asc.GetYFromX(1d, extrapolation: ExtrapolationSides.Both), 0d); - Assert.AreEqual(80d, asc.GetYFromX(8d, extrapolation: ExtrapolationSides.Both), 0d); + Assert.AreEqual(10d, asc.GetYFromX(1d, Transform.None, Transform.None, ExtrapolationSides.Both), 0d); + Assert.AreEqual(80d, asc.GetYFromX(8d, Transform.None, Transform.None, ExtrapolationSides.Both), 0d); // The same line authored descending extrapolates to the same values: below the minimum // x of 1 through (2,40)-(1,80) -> 100 at x = 0.5; above the maximum x of 8 through // (8,10)-(4,20) -> 5 at x = 10. var desc = new OrderedPairedData(new double[] { 8d, 4d, 2d, 1d }, new double[] { 10d, 20d, 40d, 80d }, true, SortOrder.Descending, true, SortOrder.Ascending); - Assert.AreEqual(100d, desc.GetYFromX(0.5d, extrapolation: ExtrapolationSides.Both), 1E-12); - Assert.AreEqual(5d, desc.GetYFromX(10d, extrapolation: ExtrapolationSides.Both), 1E-12); - Assert.AreEqual(100d, desc.GetYFromX(0.5d, extrapolation: ExtrapolationSides.Below), 1E-12); - Assert.AreEqual(10d, desc.GetYFromX(10d, extrapolation: ExtrapolationSides.Below), 0d); + Assert.AreEqual(100d, desc.GetYFromX(0.5d, Transform.None, Transform.None, ExtrapolationSides.Both), 1E-12); + Assert.AreEqual(5d, desc.GetYFromX(10d, Transform.None, Transform.None, ExtrapolationSides.Both), 1E-12); + Assert.AreEqual(100d, desc.GetYFromX(0.5d, Transform.None, Transform.None, ExtrapolationSides.Below), 1E-12); + Assert.AreEqual(10d, desc.GetYFromX(10d, Transform.None, Transform.None, ExtrapolationSides.Below), 0d); } /// @@ -531,10 +531,10 @@ public void Test_Extrapolation_NormalZTransform() public void Test_Extrapolation_GetXFromY() { var asc = new OrderedPairedData(new double[] { 1d, 2d, 4d, 8d }, new double[] { 10d, 20d, 40d, 80d }, true, SortOrder.Ascending, true, SortOrder.Ascending); - Assert.AreEqual(0.5d, asc.GetXFromY(5d, extrapolation: ExtrapolationSides.Both), 1E-12); - Assert.AreEqual(10d, asc.GetXFromY(100d, extrapolation: ExtrapolationSides.Both), 1E-12); - Assert.AreEqual(1d, asc.GetXFromY(5d, extrapolation: ExtrapolationSides.Above), 0d); - Assert.AreEqual(8d, asc.GetXFromY(100d, extrapolation: ExtrapolationSides.Below), 0d); + Assert.AreEqual(0.5d, asc.GetXFromY(5d, Transform.None, Transform.None, ExtrapolationSides.Both), 1E-12); + Assert.AreEqual(10d, asc.GetXFromY(100d, Transform.None, Transform.None, ExtrapolationSides.Both), 1E-12); + Assert.AreEqual(1d, asc.GetXFromY(5d, Transform.None, Transform.None, ExtrapolationSides.Above), 0d); + Assert.AreEqual(8d, asc.GetXFromY(100d, Transform.None, Transform.None, ExtrapolationSides.Below), 0d); } /// @@ -545,12 +545,12 @@ public void Test_Extrapolation_GetXFromY() public void Test_Extrapolation_PlateauAndSinglePoint() { var plateau = new OrderedPairedData(new double[] { 1d, 2d, 3d, 4d }, new double[] { 5d, 5d, 10d, 10d }, true, SortOrder.Ascending, false, SortOrder.Ascending); - Assert.AreEqual(5d, plateau.GetYFromX(0d, extrapolation: ExtrapolationSides.Both), 0d); - Assert.AreEqual(10d, plateau.GetYFromX(6d, extrapolation: ExtrapolationSides.Both), 0d); + Assert.AreEqual(5d, plateau.GetYFromX(0d, Transform.None, Transform.None, ExtrapolationSides.Both), 0d); + Assert.AreEqual(10d, plateau.GetYFromX(6d, Transform.None, Transform.None, ExtrapolationSides.Both), 0d); var single = new OrderedPairedData(new double[] { 2d }, new double[] { 7d }, true, SortOrder.Ascending, true, SortOrder.Ascending); - Assert.AreEqual(7d, single.GetYFromX(100d, extrapolation: ExtrapolationSides.Both), 0d); - Assert.AreEqual(7d, single.GetYFromX(-100d, extrapolation: ExtrapolationSides.Both), 0d); + Assert.AreEqual(7d, single.GetYFromX(100d, Transform.None, Transform.None, ExtrapolationSides.Both), 0d); + Assert.AreEqual(7d, single.GetYFromX(-100d, Transform.None, Transform.None, ExtrapolationSides.Both), 0d); } /// From 866b3fc7db46114c0553e1d40bfc801176a5f5f5 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Thu, 27 Aug 2026 15:25:14 -0600 Subject: [PATCH 129/222] Reuse the HMC trajectory-start gradient and copy the chain state for the delegate --- Numerics/Sampling/MCMC/HMC.cs | 136 ++++- .../Sampling/MCMC/Test_HMC_GradientReuse.cs | 497 ++++++++++++++++++ docs/sampling/mcmc.md | 3 +- 3 files changed, 629 insertions(+), 7 deletions(-) create mode 100644 Test_Numerics/Sampling/MCMC/Test_HMC_GradientReuse.cs diff --git a/Numerics/Sampling/MCMC/HMC.cs b/Numerics/Sampling/MCMC/HMC.cs index 4c16e314..f19b5528 100644 --- a/Numerics/Sampling/MCMC/HMC.cs +++ b/Numerics/Sampling/MCMC/HMC.cs @@ -21,7 +21,13 @@ namespace Numerics.Sampling.MCMC /// Description: /// /// - /// The optimal acceptance rate for this sampler is 65%, whereas Metropolis-Hastings samplers have an optimal rate of 23.4%. + /// The optimal acceptance rate for this sampler is 65%, whereas Metropolis-Hastings samplers have an optimal rate of 23.4%. + /// + /// + /// A trajectory of L leapfrog steps costs at most L + 1 gradient evaluations: the closing + /// half-step of each step is fused with the opening half-step of the next. After a chain's first + /// transition the opening evaluation is served from a per-chain memo of the previous transition's + /// closing evaluation, reducing the cost to L. /// /// /// References: @@ -39,6 +45,12 @@ public class HMC : MCMCSampler /// /// The list of parameters to evaluate. /// Returns the gradient of the log-likelihood function. + /// + /// The sampler hands the delegate a private working array, never the chain state itself, so an + /// implementation that writes through its argument cannot corrupt the chain. A gradient the + /// sampler keeps for reuse is copied out of the returned vector, so an implementation may return + /// one reused buffer. + /// public delegate Vector Gradient(IList parameters); /// @@ -103,6 +115,18 @@ public HMC(List priorDistributions, LogLikelihood logLi private double[] _lowerBounds; private double[] _upperBounds; + // Per-chain memo of the gradient at the current chain state. A leapfrog trajectory closes with a + // gradient evaluation at its final position, and the next transition opens by evaluating the + // gradient at that same position when the proposal was accepted, or at the unchanged state when it + // was rejected, so the opening evaluation is redundant after the chain's first transition. The + // stored arrays are owned by the memo and are copies in both directions: positions are recorded + // before the delegate runs and gradients are copied out of the returned vector, so neither a + // delegate that writes through its argument nor one that returns a reused buffer can corrupt an + // entry. Entries are matched on bit patterns, never with ==, so +0 and -0 are distinct positions. + private double[][] _startGradientPositions = null!; + private double[][] _startGradientValues = null!; + private bool[] _startGradientOccupied = null!; + /// /// The mass vector for the momentum distribution. /// @@ -163,6 +187,19 @@ protected override void ValidateCustomSettings() if (Steps < 1) throw new ArgumentException("The number of leapfrog steps must be at least one.", nameof(Steps)); } + /// + /// + /// Allocates the per-chain start-gradient memo. A resumed simulation skips this, keeping the + /// entries from the run it continues; those entries still describe the chain states the resume + /// starts from, so they remain valid. + /// + protected override void InitializeCustomSettings() + { + _startGradientPositions = new double[NumberOfChains][]; + _startGradientValues = new double[NumberOfChains][]; + _startGradientOccupied = new bool[NumberOfChains]; + } + /// protected override ParameterSet ChainIteration(int index, ParameterSet state) { @@ -184,9 +221,13 @@ protected override ParameterSet ChainIteration(int index, ParameterSet state) // Get kinetic energy of the current state var logKi = -0.5 * QuadraticForm(phi, _inverseMass); - // Step 2. Perform leapfrog steps to get proposal vector - var xp = new Vector(state.Values); - phi += GradientFunction(xp.Array) * _stepSize * 0.5; + // Step 2. Perform leapfrog steps to get proposal vector. The trajectory works on a + // private copy of the chain state, so the gradient delegate never sees the chain's own + // array. + var xp = new Vector((double[])state.Values.Clone()); + phi += StartGradient(index, xp.Array) * _stepSize * 0.5; + double[] proposalPosition = null!; + Vector proposalGradient = null!; for (int i = 0; i < _steps; i++) { xp += _inverseMass * phi * _stepSize; @@ -206,7 +247,19 @@ protected override ParameterSet ChainIteration(int index, ParameterSet state) } } - phi += GradientFunction(xp.Array) * _stepSize * (i == _steps - 1 ? 0.5 : 1.0); + if (i == _steps - 1) + { + // The closing half-step evaluates the gradient at the proposal position. Keep the + // position, recorded before the delegate runs, and the gradient, so an accepted + // proposal seeds the memo for the next transition's opening half-step. + proposalPosition = (double[])xp.Array.Clone(); + proposalGradient = GradientFunction(xp.Array); + phi += proposalGradient * _stepSize * 0.5; + } + else + { + phi += GradientFunction(xp.Array) * _stepSize; + } } phi *= -1d; @@ -225,8 +278,11 @@ protected override ParameterSet ChainIteration(int index, ParameterSet state) var logU = Math.Log(_chainPRNGs[index].NextDouble()); if (logU <= logRatio) { - // The proposal is accepted + // The proposal is accepted. The closing half-step's gradient becomes the opening + // gradient of the next transition, so store it against the accepted position. On the + // reject path the memo already holds the entry for the unchanged state. AcceptCount[index] += 1; + StoreStartGradient(index, proposalPosition, proposalGradient); return new ParameterSet(xp.Array, logLHp); } else @@ -245,6 +301,74 @@ protected override ParameterSet ChainIteration(int index, ParameterSet state) } + /// + /// Returns the gradient at the trajectory start, served from the per-chain memo when the position + /// matches the stored entry bit for bit, and evaluated through + /// otherwise. + /// + /// The Markov Chain zero-based index. + /// The trajectory start position. The array is the trajectory's private + /// working copy, so the delegate may be handed it directly. + /// The gradient at . On a memo hit the returned vector wraps + /// the memo's stored array and must be treated as read-only; every use here only feeds allocating + /// vector arithmetic. + /// + /// A miss records the position before the delegate runs, so a delegate that writes through its + /// argument cannot pair a mutated position with another point's gradient; such a mutation only + /// produces a harmless later miss. Matching is on + /// rather than ==, so a position differing only in the sign of a zero is a distinct + /// position, and NaN never matches so a non-finite state can never be served a stale gradient. + /// + private Vector StartGradient(int index, double[] position) + { + if (_startGradientOccupied[index] && MatchesBitwise(_startGradientPositions[index], position)) + return new Vector(_startGradientValues[index]); + + var recorded = (double[])position.Clone(); + var gradient = GradientFunction(position); + StoreStartGradient(index, recorded, gradient); + return gradient; + } + + /// + /// Stores a position and its gradient as the chain's start-gradient memo entry, copying the + /// gradient out of the delegate's vector so a reused buffer cannot rewrite the entry later. + /// + /// The Markov Chain zero-based index. + /// The position the gradient was evaluated at, recorded before the + /// delegate ran. The memo takes ownership of this array. + /// The gradient the delegate returned. A gradient of the wrong length + /// clears the entry instead of storing it. + private void StoreStartGradient(int index, double[] position, Vector gradient) + { + if (gradient.Length != NumberOfParameters) + { + _startGradientOccupied[index] = false; + return; + } + _startGradientPositions[index] = position; + _startGradientValues[index] = (double[])gradient.Array.Clone(); + _startGradientOccupied[index] = true; + } + + /// + /// Compares a stored memo position against a query position on the bit patterns of every + /// coordinate. + /// + /// The memo's stored position. + /// The query position. + /// True when every coordinate matches bit for bit. + private static bool MatchesBitwise(double[] stored, double[] position) + { + if (stored.Length != position.Length) return false; + for (int i = 0; i < stored.Length; i++) + { + if (BitConverter.DoubleToInt64Bits(stored[i]) != BitConverter.DoubleToInt64Bits(position[i])) + return false; + } + return true; + } + /// /// Evaluates the log-likelihood, returning negative infinity if the parameters are out of range. /// This prevents ArgumentOutOfRangeException from propagating during leapfrog integration diff --git a/Test_Numerics/Sampling/MCMC/Test_HMC_GradientReuse.cs b/Test_Numerics/Sampling/MCMC/Test_HMC_GradientReuse.cs new file mode 100644 index 00000000..0f300078 --- /dev/null +++ b/Test_Numerics/Sampling/MCMC/Test_HMC_GradientReuse.cs @@ -0,0 +1,497 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Distributions; +using Numerics.Mathematics.LinearAlgebra; +using Numerics.Sampling.MCMC; + +namespace Sampling.MCMC +{ + /// + /// Characterization tests for the HMC start-gradient memo. + /// + /// + /// + /// Authors: + /// + /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil + /// + /// + /// + /// Description: + /// + /// + /// An HMC trajectory closes with a gradient evaluation at its final position, and the next + /// transition opens by evaluating the gradient at that same position when the proposal was accepted, + /// or at the unchanged chain state when it was rejected. The sampler memoizes the closing evaluation + /// per chain, which is only defensible if it changes nothing. These tests are the evidence for that + /// claim, so their assertions are bitwise on + /// rather than on a tolerance: an epsilon-based assertion would not separate "identical" from "very + /// close", which is the only distinction being made here. + /// + /// + /// The reference bit patterns were captured from the sampler before the memo was added. They are the + /// same on .NET Framework 4.8.1 and on .NET 8, 9, and 10: this target's gradient is exact, the + /// standard deviations are powers of two, and the trajectory arithmetic is IEEE-754 addition, + /// multiplication, and division, so no transcendental last-place difference reaches the recorded + /// draws on this configuration. + /// + /// + [TestClass] + public class Test_HMC_GradientReuse + { + /// The number of leading recorded draws compared against the reference, per chain. + private const int GoldenDraws = 20; + + /// The gradient evaluation count of the fixed configuration. + private const int GradientCalls = 5143; + + /// The per-coordinate standard deviations of the target. + /// + /// Powers of two, so that the gradient -x / s^2 is exact and the reference values cannot + /// drift on a division. + /// + private static readonly double[] Sd = { 0.25d, 1d, 2d, 8d }; + + /// + /// The log-density of the zero-mean diagonal Gaussian target, up to an additive constant. + /// + /// The parameter vector. + /// The log-density. + private static double LogLikelihood(double[] x) + { + double sum = 0d; + for (int j = 0; j < Sd.Length; j++) + { + double z = x[j] / Sd[j]; + sum += -0.5d * z * z; + } + return sum; + } + + /// + /// The exact gradient of . + /// + /// The parameter vector. + /// The gradient. + private static Vector Gradient(IList x) + { + var g = new Vector(Sd.Length); + for (int j = 0; j < Sd.Length; j++) + g[j] = -x[j] / (Sd[j] * Sd[j]); + return g; + } + + /// + /// Builds the fixed configuration the reference values were captured from. + /// + /// The gradient delegate. + /// An unsampled sampler. + /// + /// Two chains, run serially so that ordering cannot vary, and a seeded PRNG. The analytic + /// gradient is supplied so that the delegate invocation count is exactly the number of gradient + /// evaluations the sampler asked for. The acceptance rates of this configuration are 89.5% and + /// 87.5%, so both the accept path, which stores the closing evaluation, and the reject path, + /// which relies on the entry surviving, are exercised. + /// + private static HMC BuildSampler(HMC.Gradient gradient) + { + var priors = new List(); + for (int j = 0; j < Sd.Length; j++) priors.Add(new Uniform(-50d, 50d)); + + return new HMC(priors, LogLikelihood, stepSize: 0.25, steps: 12, gradientFunction: gradient) + { + NumberOfChains = 2, + ParallelizeChains = false, + InitialIterations = 8, + ThinningInterval = 1, + WarmupIterations = 60, + Iterations = 150, + OutputLength = 100, + PRNGSeed = 12345 + }; + } + + /// + /// The recorded draws must match the reference bit for bit. + /// + [TestMethod] + public void Test_HMC_GradientReuse_ReproducesReferenceDrawsBitwise() + { + var sampler = BuildSampler(Gradient); + sampler.Sample(); + + int k = 0; + for (int c = 0; c < sampler.Output.Length; c++) + { + Assert.IsGreaterThanOrEqualTo(GoldenDraws, sampler.Output[c].Count, + $"Chain {c} recorded only {sampler.Output[c].Count} draws; the reference covers {GoldenDraws}."); + + for (int i = 0; i < GoldenDraws; i++) + { + var values = sampler.Output[c][i].Values; + for (int j = 0; j < values.Length; j++, k++) + { + long actual = BitConverter.DoubleToInt64Bits(values[j]); + Assert.AreEqual(ReferenceDraws[k], actual, + $"Chain {c} draw {i} coordinate {j} moved: expected {BitConverter.Int64BitsToDouble(ReferenceDraws[k]):R}, got {values[j]:R}. " + + "The start-gradient memo must not move a single draw."); + } + } + } + + Assert.AreEqual(ReferenceDraws.Length, k, "The reference length and the compared draw count disagree."); + } + + /// + /// The memo must remove the opening gradient evaluation of every transition after a chain's + /// first, and must remove nothing else. + /// + /// + /// + /// Before the memo this configuration evaluated the gradient 5,541 times; each of the two chains + /// runs 200 transitions, and the memo removes one evaluation from all but the first of them, + /// which is 5,541 - 2 x 199 = 5,143. The count is asserted exactly rather than as an inequality: + /// the companion bitwise test already pins the trajectory, so with the trajectory fixed the count + /// is decided by the memo policy alone, and an exact assertion catches a silent change in hit + /// rate that a threshold would not. + /// + /// + /// The count is the same on all four target frameworks, as are the draws. + /// + /// + [TestMethod] + public void Test_HMC_GradientReuse_ReducesGradientEvaluations() + { + int count = 0; + var sampler = BuildSampler((x) => { count++; return Gradient(x); }); + sampler.Sample(); + Assert.AreEqual(GradientCalls, count, + "The gradient evaluation count changed; before the memo it was 5,541. Check the companion " + + "bitwise test first: if it also moved the trajectory changed and this count followed it, " + + "which is a different finding from a change in memo hit rate."); + } + + /// + /// A gradient delegate that writes through its argument must not be able to corrupt the chain + /// state or the recorded draws. + /// + /// + /// The trajectory works on a private copy of the chain state, so the delegate never receives the + /// chain's own array. Without that copy, the delegate's write would land in the current state's + /// array: a rejected transition would then return, and record, the sentinel the delegate wrote + /// rather than the state the chain was actually at. The sentinel sits inside the prior support on + /// purpose, so nothing downstream can clamp or reject it away; only the private copy keeps it out + /// of the record. + /// + [TestMethod] + public void Test_HMC_GradientReuse_DelegateWritingThroughItsArgumentCannotCorruptTheChain() + { + const double sentinel = 43d; + var sampler = BuildSampler((x) => + { + var g = Gradient(x); + // Overwrite the argument after computing its gradient, as a delegate that uses its + // argument as scratch space would. + for (int j = 0; j < x.Count; j++) x[j] = sentinel; + return g; + }); + sampler.Sample(); + + for (int c = 0; c < sampler.MarkovChains.Length; c++) + { + for (int i = 0; i < sampler.MarkovChains[c].Count; i++) + { + var values = sampler.MarkovChains[c][i].Values; + for (int j = 0; j < values.Length; j++) + { + Assert.AreNotEqual(sentinel, values[j], + $"Chain {c} draw {i} coordinate {j} recorded the delegate's sentinel; the " + + "delegate was handed the chain state's own array rather than a private copy."); + } + } + } + } + + /// + /// The memo must serve a repeat of the identical position and must not serve a position that + /// differs only in the sign of a zero. + /// + /// + /// + /// This is a white-box probe of the memo's lookup, driven through reflection because the memo is + /// private and the situation it guards against cannot be provoked reliably through a real chain. + /// It exists because the obvious simplification of the lookup, comparing with == instead + /// of on the bit pattern, would pass every other test in this class: +0 == -0 is true, so + /// an == lookup would answer a query at negative zero with the gradient at positive zero. + /// For this target those two gradients differ, in the sign of their own zero, and the difference + /// would then propagate into the momentum. + /// + /// + /// Negative zero is constructed from its bit pattern rather than written as a literal so that + /// nothing about the constant depends on how the compiler folds a unary minus. + /// + /// + [TestMethod] + public void Test_HMC_GradientReuse_TreatsNegativeZeroAsADistinctPosition() + { + int count = 0; + var sampler = BuildSampler((x) => { count++; return Gradient(x); }); + sampler.Sample(); + ClearMemo(sampler); + + double negativeZero = BitConverter.Int64BitsToDouble(long.MinValue); + var atPositiveZero = new double[Sd.Length]; + var atNegativeZero = new double[Sd.Length]; + for (int j = 0; j < Sd.Length; j++) + { + atPositiveZero[j] = 0d; + atNegativeZero[j] = negativeZero; + } + + count = 0; + var reference = (double[])InvokeStartGradient(sampler, atPositiveZero).Clone(); + Assert.AreEqual(1, count, "The first evaluation at a fresh position must reach the gradient delegate."); + + var repeated = InvokeStartGradient(sampler, (double[])atPositiveZero.Clone()); + Assert.AreEqual(1, count, "Repeating the identical position must be served from the memo."); + AssertSameBits(reference, repeated, + "came back from the memo with different bits than the evaluation that filled it"); + + var flipped = InvokeStartGradient(sampler, atNegativeZero); + Assert.AreEqual(2, count, + "Negative zero is a different position from positive zero and must not be answered from the memo."); + for (int j = 0; j < Sd.Length; j++) + { + Assert.AreNotEqual(BitConverter.DoubleToInt64Bits(reference[j]), BitConverter.DoubleToInt64Bits(flipped[j]), + $"Coordinate {j} returned the gradient at positive zero for a query at negative zero."); + } + } + + /// + /// The memo must store a copy of the queried position, not a reference to the caller's array. + /// + /// + /// The requirement is real and not hypothetical: the trajectory's own working array is what the + /// sampler queries with, and the leapfrog loop rewrites that array's successor one statement + /// later. A memo holding the queried array by reference would find its key rewritten under it, so + /// the entry would stop matching the point it was computed at and start matching a point it was + /// not. This probe reproduces that directly: evaluate at a position, mutate the caller's array, + /// and check that the entry still answers the original position and does not answer the mutated + /// one. + /// + [TestMethod] + public void Test_HMC_GradientReuse_StoresACopyOfTheQueriedPosition() + { + int count = 0; + var sampler = BuildSampler((x) => { count++; return Gradient(x); }); + sampler.Sample(); + ClearMemo(sampler); + + var probe = new double[] { 0.5d, -1.5d, 2.25d, -4d }; + var original = (double[])probe.Clone(); + + count = 0; + var reference = (double[])InvokeStartGradient(sampler, probe).Clone(); + Assert.AreEqual(1, count, "The first evaluation at a fresh position must reach the gradient delegate."); + + // Mutate the caller's array in place, exactly as the trajectory does to its working copy. + for (int j = 0; j < probe.Length; j++) probe[j] += 1d; + + var atOriginal = InvokeStartGradient(sampler, (double[])original.Clone()); + Assert.AreEqual(1, count, + "The memo stopped recognising the position it was filled at after the caller's array was mutated; " + + "it is holding a reference to that array rather than a copy."); + AssertSameBits(reference, atOriginal, "was not returned unchanged from the memo"); + + InvokeStartGradient(sampler, probe); + Assert.AreEqual(2, count, + "The memo answered a position it never evaluated at; its stored key followed the caller's mutation."); + } + + /// + /// The memo must record the position before the delegate runs, so a delegate that writes through + /// its argument cannot leave the memo keyed by the mutated position. + /// + /// + /// receives a position array by reference, and nothing + /// prevents an implementation from using it as scratch space during the evaluation. Were the + /// position recorded after such a delegate returned, the entry would be keyed by the mutated + /// position while holding the gradient of the original one: a later query at the mutated position + /// would falsely hit, and a later query at the original position would falsely miss. The delegate + /// here computes the gradient of its argument and then overwrites the argument, and the + /// assertions observe which keys the memo answers to afterwards. + /// + [TestMethod] + public void Test_HMC_GradientReuse_RecordsThePositionBeforeTheDelegateRuns() + { + int count = 0; + bool hostile = false; + var sampler = BuildSampler((x) => + { + count++; + var g = Gradient(x); + if (hostile) + { + // Overwrite the argument after computing its gradient, as a delegate that uses its + // argument as scratch space would. + for (int j = 0; j < x.Count; j++) x[j] = 1000d + j; + } + return g; + }); + sampler.Sample(); + ClearMemo(sampler); + hostile = true; + + var original = new double[] { 0.5d, -1.5d, 2.25d, -4d }; + var mutated = new double[] { 1000d, 1001d, 1002d, 1003d }; + + count = 0; + InvokeStartGradient(sampler, (double[])original.Clone()); + Assert.AreEqual(1, count, "The first evaluation at a fresh position must reach the gradient delegate."); + + InvokeStartGradient(sampler, (double[])original.Clone()); + Assert.AreEqual(1, count, + "A repeat of the original position must be served from the memo; the stored key followed " + + "the delegate's in-call mutation of its argument."); + + InvokeStartGradient(sampler, (double[])mutated.Clone()); + Assert.AreEqual(2, count, + "The memo answered the mutated position, which was never evaluated; the position must be " + + "recorded before the delegate runs."); + } + + /// + /// The memo must store a copy of the gradient the delegate returned, not a reference to the + /// it came back in. + /// + /// + /// is a caller-supplied delegate and nothing obliges it to + /// allocate a fresh per call; returning one reused buffer is a reasonable + /// thing for a performance-minded implementation to do. A memo holding that buffer by reference + /// would find its entry rewritten by whatever the buffer holds next. The probe fills the memo, + /// overwrites the delegate's one buffer directly, and re-queries the position. + /// + [TestMethod] + public void Test_HMC_GradientReuse_StoresACopyOfTheReturnedGradient() + { + int count = 0; + var reused = new Vector(Sd.Length); + var sampler = BuildSampler((x) => + { + count++; + for (int j = 0; j < Sd.Length; j++) reused[j] = -x[j] / (Sd[j] * Sd[j]); + return reused; + }); + sampler.Sample(); + ClearMemo(sampler); + + var position = new double[] { 0.5d, -1.5d, 2.25d, -4d }; + + count = 0; + var reference = (double[])InvokeStartGradient(sampler, position).Clone(); + Assert.AreEqual(1, count, "The first evaluation at a fresh position must reach the gradient delegate."); + + // Overwrite the delegate's one buffer, as its next evaluation anywhere would. + for (int j = 0; j < Sd.Length; j++) reused[j] = double.NaN; + + var repeated = InvokeStartGradient(sampler, (double[])position.Clone()); + Assert.AreEqual(1, count, "Repeating the identical position must be served from the memo."); + AssertSameBits(reference, repeated, + "came back holding the delegate's buffer contents; the memo is aliased to the returned vector"); + } + + /// + /// Empties chain zero's start-gradient memo so a probe cannot hit an entry a sampling run left + /// behind. + /// + /// A sampled sampler, so that its memo is allocated. + private static void ClearMemo(HMC sampler) + { + var field = typeof(HMC).GetField("_startGradientOccupied", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.IsNotNull(field, "The private field _startGradientOccupied was not found on HMC."); + var occupied = field!.GetValue(sampler) as bool[]; + Assert.IsNotNull(occupied, "The private field _startGradientOccupied was not a bool[]."); + occupied![0] = false; + } + + /// + /// Calls the sampler's private start-gradient memo for chain zero. + /// + /// A sampled sampler. + /// The position to evaluate at. + /// The gradient the memo returned. On a hit the array is owned by the memo. + private static double[] InvokeStartGradient(HMC sampler, double[] position) + { + var method = typeof(HMC).GetMethod("StartGradient", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.IsNotNull(method, "The private method StartGradient was not found on HMC."); + var result = method!.Invoke(sampler, new object[] { 0, position }) as Vector; + Assert.IsNotNull(result, "StartGradient did not return a Vector."); + return result!.Array; + } + + /// + /// Asserts two gradients agree on every bit of every coordinate. + /// + /// The reference gradient. + /// The gradient to check. + /// A description of the failure, completing "Coordinate {j} ...". + private static void AssertSameBits(double[] expected, double[] actual, string what) + { + for (int j = 0; j < expected.Length; j++) + { + Assert.AreEqual(BitConverter.DoubleToInt64Bits(expected[j]), BitConverter.DoubleToInt64Bits(actual[j]), + $"Coordinate {j} {what}."); + } + } + + /// + /// Reference draw bit patterns, captured before the start-gradient memo was added. Ordered by + /// chain, then draw, then coordinate. + /// + private static readonly long[] ReferenceDraws = + { + 4598790761935071958L, -4618981460931078777L, -4633255124910629032L, -4617162068170500676L, + -4624529944371995155L, -4617749420758822309L, -4626235032242337149L, -4615164204388161845L, + -4628983265917522267L, -4618908764902722070L, -4625110010327077502L, -4614951484470846344L, + 4598954263896678186L, -4617097697679525926L, -4649749985272669732L, -4618414882460949285L, + -4624860888538835852L, -4623810609633856776L, 4592026127623997532L, -4615929701390368898L, + -4621191476304810502L, 4604801501498482217L, -4613202572418810060L, -4605665759072173374L, + -4629068366436945104L, -4618626112238693940L, 4614750598294948781L, -4605596466180995177L, + 4591244730581707978L, 4596090026709588294L, 4613171933681998900L, -4606128366636146336L, + 4595930585635372142L, -4629060288817124607L, 4612735735303026817L, -4604204730577397544L, + 4594366844203156640L, -4627079147440009976L, -4610561632631668905L, -4600361212597767362L, + 4594366844203156640L, -4627079147440009976L, -4610561632631668905L, -4600361212597767362L, + -4636155716436921976L, -4624339647361356827L, 4612338231985503694L, -4601005187574736344L, + -4634973842059191088L, -4620627630749799633L, 4612267695448330660L, -4600836608777488564L, + -4627659637541364119L, -4622850209188883201L, 4609583962443052758L, -4600612900520531445L, + -4628528992263354328L, 4600694626607139696L, -4610677816921661479L, -4601882932559591974L, + -4638620556040634800L, -4619916645768072882L, -4611517246567096082L, -4601317546231322976L, + -4626808121326507195L, -4620250639947265398L, 4612009310764256968L, -4606366362579769275L, + -4626808121326507195L, -4620250639947265398L, 4612009310764256968L, -4606366362579769275L, + 4598410045685915280L, -4622157382559180487L, 4611388786152615160L, -4605968032698001500L, + -4624449941804639026L, 4605092849502181424L, 4609566208919303122L, -4606738776715140986L, + -4627906937310126020L, -4616720237793338336L, 4611908397114699630L, 4613421123650436916L, + -4637441883597509066L, 4609533003703851103L, 4611105707947881364L, 4611214443729672069L, + 4592728123375942821L, 4610830707918042185L, 4607185877474830950L, 4613316316417758951L, + -4628049799448325686L, -4619060982015456801L, -4612859193130813537L, -4608328719210657963L, + -4631200667548008512L, 4607232683977472203L, -4618021602528479124L, -4603454475928677768L, + 4586358561313321022L, -4627629114948788311L, -4613640643895376738L, -4602369865473133708L, + 4592987823185115278L, -4624290272479213034L, -4612261317450630715L, -4601833935265813524L, + 4599949205595933104L, -4635305139867655008L, 4610930562709882120L, -4599080917220753613L, + -4623348452407011981L, 4606151590101388335L, -4619642956006844966L, -4599718869619806443L, + -4623348452407011981L, 4606151590101388335L, -4619642956006844966L, -4599718869619806443L, + 4600515961342593418L, -4615720506001707236L, -4607014327469146078L, -4602706230880192716L, + 4600199425511467748L, -4615856985277434193L, 4616366937767583751L, -4609338645062503337L, + 4593081008405489748L, -4615981226981559728L, 4614939328883772403L, -4609524976034779626L, + -4629787526051756144L, 4605721730834660398L, -4617157942930840285L, -4600762894074213924L, + -4640220860144088144L, -4615965449033942918L, -4613160886502937701L, -4600612414082812880L, + 4591101748398142419L, 4600537153890364586L, 4609624430212270331L, -4598857016835340532L, + 4582956471600378512L, 4599167091731948549L, 4608573541217824228L, -4598921277526999901L, + -4621782872919811902L, 4598058065089355576L, -4617920349762308280L, -4598646684179451416L, + 4600419081759212744L, -4619502348791681535L, -4608041254443229700L, -4597129485098255032L, + -4621790777160238445L, -4615838884775658076L, -4606515638330080586L, -4597060943261626173L, + }; + } +} diff --git a/docs/sampling/mcmc.md b/docs/sampling/mcmc.md index e3e2b6fb..76663798 100644 --- a/docs/sampling/mcmc.md +++ b/docs/sampling/mcmc.md @@ -476,6 +476,7 @@ In log space, the source code computes this as: - The step size $\varepsilon$ is **jittered**: each iteration draws $\varepsilon \sim \text{Uniform}(0, \, 2\varepsilon_0)$ where $\varepsilon_0$ is the `StepSize` property. This avoids resonant trajectories. - The number of leapfrog steps $L$ is **jittered**: each iteration draws $L \sim \text{UniformDiscrete}(1, \, 2L_0)$ where $L_0$ is the `Steps` property. +- A trajectory of $L$ steps costs at most $L + 1$ gradient evaluations: the closing half-step of each leapfrog step is fused with the opening half-step of the next, and after a chain's first transition the opening evaluation is served from a per-chain memo of the previous transition's closing evaluation. The gradient delegate receives a private working array, never the chain state itself, so a delegate that writes through its argument cannot corrupt the chain. - The mass vector $M$ is diagonal (default: identity). Users can set it via the `mass` constructor parameter. - If no gradient function is provided, numerical finite differences are used via `NumericalDerivative.Gradient`, with probes clamped to prior bounds. @@ -551,7 +552,7 @@ This is implemented via log-sum-exp arithmetic for numerical stability. where: -- $\delta = 0.80$ is the target acceptance rate (`DELTA_TARGET`) +- $\delta$ is the target acceptance rate (the `TargetAcceptanceRate` property, default 0.80) - $\gamma = 0.05$ is the adaptation regularization (`GAMMA`) - $t_0 = 10$ prevents early instability (`T0`) - $\mu = \log(10 \cdot \varepsilon_0)$ is the bias point, with $\varepsilon_0$ the initial step size From 7875480e38f2b3418e1e03f6846c72e64839ad2a Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Thu, 27 Aug 2026 17:54:00 -0600 Subject: [PATCH 130/222] Guard the weighted moment poles and the percentile cancellation --- Numerics/Data/Statistics/Statistics.cs | 32 +++++++++++--- .../Data/Statistics/Test_Statistics.cs | 42 +++++++++++++++++++ 2 files changed, 68 insertions(+), 6 deletions(-) diff --git a/Numerics/Data/Statistics/Statistics.cs b/Numerics/Data/Statistics/Statistics.cs index 7f68403d..35b3c4c1 100644 --- a/Numerics/Data/Statistics/Statistics.cs +++ b/Numerics/Data/Statistics/Statistics.cs @@ -960,7 +960,8 @@ public static double StandardDeviation(IList data, IList weights /// /// Estimates the weighted skewness coefficient from the unsorted data array. - /// Returns NaN if data is empty or any entry is NaN. + /// Returns NaN if data is empty, if any entry is NaN, or if the effective sample size + /// implied by the weights does not exceed two, where the bias correction has a pole. /// /// Sample of data, no sorting is assumed. /// The non-negative, finite weight of each data entry. @@ -981,6 +982,10 @@ public static double Skewness(IList data, IList weights, WeightT if (total <= 0d) throw new ArgumentException("The weights must not all be zero.", nameof(weights)); WeightedCentralSums(data, weights, total, out _, out double s2, out double s3, out _); double n = EffectiveSampleSize(weights, total, weightType); + // The effective sample size is continuous, so the correction's pole at n = 2 is reachable + // from ordinary weights, e.g. normalized frequency weights; past it the correction flips + // sign. The unweighted estimator needs n > 2 for the same reason. + if (n <= 2d) return double.NaN; double m2 = s2 / total; double m3 = s3 / total; double g = m3 / Math.Pow(m2, 3.0d / 2.0d); @@ -991,7 +996,8 @@ public static double Skewness(IList data, IList weights, WeightT /// /// Estimates the weighted excess kurtosis from the unsorted data array. - /// Returns NaN if data is empty or any entry is NaN. + /// Returns NaN if data is empty, if any entry is NaN, or if the effective sample size + /// implied by the weights does not exceed three, where the small-sample correction has a pole. /// /// Sample of data, no sorting is assumed. /// The non-negative, finite weight of each data entry. @@ -1013,6 +1019,11 @@ public static double Kurtosis(IList data, IList weights, WeightT if (total <= 0d) throw new ArgumentException("The weights must not all be zero.", nameof(weights)); WeightedCentralSums(data, weights, total, out _, out double s2, out _, out double s4); double n = EffectiveSampleSize(weights, total, weightType); + // The effective sample size is continuous, so the correction's poles at n = 2 and n = 3 are + // reachable from ordinary weights, e.g. reliability weights concentrated on two entries; + // past them the correction changes sign. The unweighted estimator needs n > 3 for the same + // reason. + if (n <= 3d) return double.NaN; double m2 = s2 / total; double m4 = s4 / total; double a = n * (n + 1) / ((n - 1) * (n - 2) * (n - 3)); @@ -1147,16 +1158,25 @@ private static double WeightedPercentile(double[] x, double[] w, double[] prefix // Plotting positions p(i) = A(i) / (total - w(i)) are strictly increasing with // p(0) = 0 and p(m-1) = 1, so a bracket always exists. Binary search for it. + // In exact arithmetic the denominator is the sum of the other positive weights, so it is + // positive for m >= 2; in floating point a weight that dominates the total cancels it to + // zero, which would poison the search with 0/0. A zero denominator means the point holds + // essentially all the mass, so its position collapses to the boundary it sits against. + double Position(int i) + { + double d = total - w[i]; + return d > 0d ? prefix[i] / d : (i == 0 ? 0d : 1d); + } + int lo = 0, hi = m - 1; while (hi - lo > 1) { int mid = (lo + hi) >> 1; - double pMid = prefix[mid] / (total - w[mid]); - if (pMid <= k) lo = mid; + if (Position(mid) <= k) lo = mid; else hi = mid; } - double pLo = prefix[lo] / (total - w[lo]); - double pHi = prefix[hi] / (total - w[hi]); + double pLo = Position(lo); + double pHi = Position(hi); if (k <= pLo) return x[lo]; if (k >= pHi) return x[hi]; double theta = (k - pLo) / (pHi - pLo); diff --git a/Test_Numerics/Data/Statistics/Test_Statistics.cs b/Test_Numerics/Data/Statistics/Test_Statistics.cs index 3a3ded3b..e66efb17 100644 --- a/Test_Numerics/Data/Statistics/Test_Statistics.cs +++ b/Test_Numerics/Data/Statistics/Test_Statistics.cs @@ -881,6 +881,48 @@ public void Test_Weighted_DegenerateSamples() Assert.IsTrue(double.IsNaN(Numerics.Data.Statistics.Statistics.Variance(new double[] { 1d, 2d }, new double[] { 0.3d, 0.3d }, Numerics.Data.Statistics.WeightType.Frequency))); } + /// + /// The bias corrections of the weighted higher moments have poles in the effective sample + /// size: n = 2 for skewness and n = 3 for kurtosis. The effective sample size is continuous, + /// so ordinary weights - normalized frequency weights, or reliability weights concentrated on + /// two entries - can land at or past a pole, where the correction is undefined or changes + /// sign; the estimators must answer NaN there, matching the weighted variance's convention at + /// its own denominator, and stay finite just above the pole. + /// + [TestMethod] + public void Test_Weighted_HigherMoments_EffectiveSampleSizePoles() + { + // Frequency weights summing to 1.5: n = 1.5 sits past the skewness pole at n = 2, where + // the correction's sign is inverted. Unguarded, this returned -1.03 for a right-skewed + // sample. + Assert.IsTrue(double.IsNaN(Numerics.Data.Statistics.Statistics.Skewness(new double[] { 1d, 2d, 6d }, new double[] { 0.5d, 0.5d, 0.5d }))); + // Frequency weights summing to exactly 2 sit on the pole itself. + Assert.IsTrue(double.IsNaN(Numerics.Data.Statistics.Statistics.Skewness(new double[] { 1d, 6d }, new double[] { 1d, 1d }))); + // Reliability weights concentrated on two entries: n = 2.06 sits past the kurtosis pole + // at n = 3. Unguarded, this returned a large finite value of the wrong sign. + Assert.IsTrue(double.IsNaN(Numerics.Data.Statistics.Statistics.Kurtosis(new double[] { 1d, 2d, 3d, 4d, 5d }, new double[] { 1d, 1d, 0.01d, 0.01d, 0.01d }, Numerics.Data.Statistics.WeightType.Reliability))); + // Frequency weights summing to exactly 3 sit on the kurtosis pole itself. + Assert.IsTrue(double.IsNaN(Numerics.Data.Statistics.Statistics.Kurtosis(new double[] { 1d, 2d, 6d }, new double[] { 1d, 1d, 1d }))); + // Just above the poles both estimators are defined and finite. + Assert.IsGreaterThan(0d, Numerics.Data.Statistics.Statistics.Skewness(new double[] { 1d, 2d, 6d }, new double[] { 1d, 1d, 0.5d })); + Assert.IsFalse(double.IsNaN(Numerics.Data.Statistics.Statistics.Kurtosis(new double[] { 1d, 2d, 3d, 6d }, new double[] { 1d, 1d, 1d, 0.5d }))); + } + + /// + /// A weight that dominates the total cancels the plotting-position denominator + /// total - w(i) to zero in floating point, which unguarded turns every interior percentile + /// into 0/0 = NaN. For two points the exact positions are 0 and 1 regardless of the weights, + /// so the percentile must interpolate between the two values, in either dominance order. + /// + [TestMethod] + public void Test_WeightedPercentile_ExtremeDominantWeight() + { + var data = new double[] { 10d, 20d }; + Assert.AreEqual(15d, Numerics.Data.Statistics.Statistics.Percentile(data, 0.5d, new double[] { 1E300d, 1E-300d }), 0d); + Assert.AreEqual(12.5d, Numerics.Data.Statistics.Statistics.Percentile(data, 0.25d, new double[] { 1E300d, 1E-300d }), 0d); + Assert.AreEqual(15d, Numerics.Data.Statistics.Statistics.Percentile(data, 0.5d, new double[] { 1E-300d, 1E300d }), 0d); + } + /// /// The multi-percentile overload matches the scalar overload, the sorted-data flag matches /// the unsorted call, and an empty percentile list returns an empty array. From 8c4e24eb774fc6c5b89c996b906b5ae0fe246a77 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Thu, 27 Aug 2026 17:54:00 -0600 Subject: [PATCH 131/222] Harden the paired-data guards and simplification tails and restore the lookup docs --- .../Data/Paired Data/LineSimplification.cs | 7 +- .../Data/Paired Data/OrderedPairedData.cs | 89 ++++++++++++------- .../Paired Data/UncertainOrderedPairedData.cs | 4 +- .../Data/Paired Data/Test_PairedData.cs | 24 +++++ .../Test_PairedDataInterpolation.cs | 32 +++++++ .../Test_PairedDataLineSimplification.cs | 60 ++++++++++++- .../Paired Data/Test_UncertainPairedData.cs | 18 ++++ 7 files changed, 201 insertions(+), 33 deletions(-) diff --git a/Numerics/Data/Paired Data/LineSimplification.cs b/Numerics/Data/Paired Data/LineSimplification.cs index 5756edc9..a9c6c9c3 100644 --- a/Numerics/Data/Paired Data/LineSimplification.cs +++ b/Numerics/Data/Paired Data/LineSimplification.cs @@ -35,6 +35,12 @@ public static void RamerDouglasPeucker(List ordinates, double epsilon, if (ordinates.Count < 2) throw new ArgumentOutOfRangeException("Not enough points to simplify"); + // The output parameter's contract must not depend on which branch runs: the recursion + // branch appends while the endpoint branch replaced, so a pre-populated list was + // replaced or appended-to depending on the curve. Clearing up front makes the result + // the simplified curve alone on every path. + output.Clear(); + // Find the point with the maximum distance from line between the start and end double dmax = 0.0; int index = 0; @@ -67,7 +73,6 @@ public static void RamerDouglasPeucker(List ordinates, double epsilon, else { // Just return start and end points - output.Clear(); output.Add(ordinates[0]); output.Add(ordinates[ordinates.Count - 1]); } diff --git a/Numerics/Data/Paired Data/OrderedPairedData.cs b/Numerics/Data/Paired Data/OrderedPairedData.cs index aa1d67e0..4f558d2c 100644 --- a/Numerics/Data/Paired Data/OrderedPairedData.cs +++ b/Numerics/Data/Paired Data/OrderedPairedData.cs @@ -467,9 +467,11 @@ public void RemoveAt(int index) /// The number of elements to remove. public void RemoveRange(int index, int count) { - if (index < 0 || (index + count) >= _ordinates.Count) { return; } + // A range reaching the final element (index + count == Count) is a valid removal, so the + // guard must reject only ranges that run past the end, matching List.RemoveRange. + if (index < 0 || (index + count) > _ordinates.Count) { return; } List items = new List(); - for (int i = index; i < count; i++) { items.Add(_ordinates[i]); } + for (int i = index; i < index + count; i++) { items.Add(_ordinates[i]); } _ordinates.RemoveRange(index, count); Validate(); if (SuppressCollectionChanged == false) @@ -483,7 +485,8 @@ public void RemoveRange(int index, int count) public void Add(Ordinate item) { _ordinates.Add(item); - IsValid = OrdinateValid(_ordinates.Count - 1); + // only need to set valid state if it is true. if it is already false then appending can't make it true. + if (IsValid) IsValid = OrdinateValid(_ordinates.Count - 1); if (SuppressCollectionChanged == false) CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, _ordinates.Count - 1)); } @@ -862,13 +865,7 @@ private double BaseInterpolate(double value, int index, bool givenX = true, Tran /// The interpolated value. /// /// Out-of-range lookups hold the boundary ordinate; the four-argument overload can - /// extrapolate instead. Sides there are defined in value space regardless of the sort - /// orientation: Below is beyond the minimum x and Above beyond the maximum. Exactly at an - /// endpoint the boundary ordinate is returned unchanged, a single-point table always - /// holds, and a plateau (equal boundary ordinates in transform space) extends at slope - /// zero. Extrapolation on an untransformed axis is unbounded, so a caller holding a - /// bounded quantity such as a probability must clamp the result or use the NormalZ - /// transform, which is bounded by construction. + /// extrapolate instead. /// public double GetYFromX(double x, Transform xTransform = Transform.None, Transform yTransform = Transform.None) { @@ -888,10 +885,21 @@ public double GetYFromX(double x, Transform xTransform = Transform.None, Transfo /// /// The interpolated value. /// - /// This is a distinct overload rather than an optional parameter so the historical - /// three-argument signature keeps binary compatibility with assemblies compiled against - /// earlier releases. + /// Sides are defined in value space regardless of the sort orientation: Below is beyond the + /// minimum x and Above beyond the maximum. Exactly at an endpoint the boundary ordinate is + /// returned unchanged, a single-point table always holds, and a plateau (equal boundary + /// ordinates in transform space) extends at slope zero. Extrapolation extends the boundary + /// segment in the configured transform space, so the lookup value must lie in the x + /// transform's domain: a NormalZ x-transform throws for lookups outside the unit interval, + /// and a Logarithmic x-transform returns NaN for negative lookups and treats values below + /// 1E-16 as 1E-16. Extrapolation on an untransformed axis is unbounded, so a caller holding + /// a bounded quantity such as a probability must clamp the result or use the NormalZ + /// transform, which is bounded by construction. /// + /// + /// Thrown when extrapolation is requested through a NormalZ x-transform and the lookup + /// value lies outside the unit interval. + /// public double GetYFromX(double x, Transform xTransform, Transform yTransform, ExtrapolationSides extrapolation) { if (Count == 0) return double.NaN; @@ -929,11 +937,7 @@ public double GetYFromX(double x, Transform xTransform, Transform yTransform, Ex /// The interpolated value. /// /// Out-of-range lookups hold the boundary ordinate; the four-argument overload can - /// extrapolate instead. Sides there are defined in value space regardless of the sort - /// orientation: Below is beyond the minimum y and Above beyond the maximum. Exactly at an - /// endpoint the boundary ordinate is returned unchanged, a single-point table always - /// holds, and a plateau (equal boundary ordinates in transform space) extends at slope - /// zero. + /// extrapolate instead. /// public double GetXFromY(double y, Transform xTransform = Transform.None, Transform yTransform = Transform.None) { @@ -953,10 +957,21 @@ public double GetXFromY(double y, Transform xTransform = Transform.None, Transfo /// /// The interpolated value. /// - /// This is a distinct overload rather than an optional parameter so the historical - /// three-argument signature keeps binary compatibility with assemblies compiled against - /// earlier releases. + /// Sides are defined in value space regardless of the sort orientation: Below is beyond the + /// minimum y and Above beyond the maximum. Exactly at an endpoint the boundary ordinate is + /// returned unchanged, a single-point table always holds, and a plateau (equal boundary + /// ordinates in transform space) extends at slope zero. Extrapolation extends the boundary + /// segment in the configured transform space, so the lookup value must lie in the y + /// transform's domain: a NormalZ y-transform throws for lookups outside the unit interval, + /// and a Logarithmic y-transform returns NaN for negative lookups and treats values below + /// 1E-16 as 1E-16. Extrapolation on an untransformed axis is unbounded, so a caller holding + /// a bounded quantity such as a probability must clamp the result or use the NormalZ + /// transform, which is bounded by construction. /// + /// + /// Thrown when extrapolation is requested through a NormalZ y-transform and the lookup + /// value lies outside the unit interval. + /// public double GetXFromY(double y, Transform xTransform, Transform yTransform, ExtrapolationSides extrapolation) { if (Count == 0) return double.NaN; @@ -1005,8 +1020,9 @@ public double[] GetYFromX(IList xValues, Transform xTransform = Transfor /// The sides of the x-range on which out-of-range lookups extrapolate. None reproduces the historical endpoint hold. /// An array of interpolated values. /// - /// A distinct overload rather than an optional parameter, preserving the historical - /// three-argument signature's binary compatibility. + /// Each element is looked up with the scalar + /// overload, whose + /// extrapolation semantics apply per element. /// public double[] GetYFromX(IList xValues, Transform xTransform, Transform yTransform, ExtrapolationSides extrapolation) { @@ -1037,8 +1053,9 @@ public double[] GetXFromY(IList yValues, Transform xTransform = Transfor /// The sides of the y-range on which out-of-range lookups extrapolate. None reproduces the historical endpoint hold. /// An array of interpolated values. /// - /// A distinct overload rather than an optional parameter, preserving the historical - /// three-argument signature's binary compatibility. + /// Each element is looked up with the scalar + /// overload, whose + /// extrapolation semantics apply per element. /// public double[] GetXFromY(IList yValues, Transform xTransform, Transform yTransform, ExtrapolationSides extrapolation) { @@ -1248,8 +1265,8 @@ public int SequentialSearchY(double y) { return Count - 2; } - else if ((OrderY == SortOrder.Ascending && y < _ordinates[XSearchStart].Y) || - (OrderY == SortOrder.Descending && y > _ordinates[XSearchStart].Y)) + else if ((OrderY == SortOrder.Ascending && y < _ordinates[YSearchStart].Y) || + (OrderY == SortOrder.Descending && y > _ordinates[YSearchStart].Y)) { jl = 0; } @@ -1519,6 +1536,11 @@ private double PerpendicularDistance(double aX, double aY, double bX, double bY, double area = Math.Abs((aX * bY + bX * cY + cX * aY - bX * aY - cX * bY - aX * cY) * 0.5); double triangleBase = Math.Pow(Math.Pow(aX - bX, 2) + Math.Pow(aY - bY, 2), 0.5); + // A segment whose endpoints coincide has no base to divide by (0/0 = NaN); the distance + // degenerates to the point-to-endpoint distance, matching the guarded formula in + // LineSimplification.PerpendicularDistance. + if (triangleBase == 0d) + return Math.Pow(Math.Pow(cX - aX, 2) + Math.Pow(cY - aY, 2), 0.5); return area * 2 / triangleBase; } @@ -1581,8 +1603,11 @@ private double TriangleArea(Ordinate point1, Ordinate point2, Ordinate point3) /// and number of points in the search region. public OrderedPairedData LangSimplify(double tolerance, int lookAhead) { - if (_ordinates == null || lookAhead <= 1 || tolerance <= 0) - return this; + if (_ordinates == null) return this; + // The guarded return is a distinct object, matching the other simplifiers' contract + // that the result never aliases the receiver. + if (lookAhead <= 1 || tolerance <= 0) + return Clone(); List ordinates = new List(); @@ -1594,7 +1619,11 @@ public OrderedPairedData LangSimplify(double tolerance, int lookAhead) for (int i = 0; i < count; i++) { - if (i + lookAhead > count) + // The clamp must fire at the exact tail boundary too (i + lookAhead == count): + // an unclamped look-ahead there falls through RecursiveTolerance's own range guard + // unreduced and overshoots the final ordinate, which silently dropped the curve's + // last point. + if (i + lookAhead >= count) lookAhead = count - i - 1; offset = RecursiveTolerance(i, lookAhead, tolerance); diff --git a/Numerics/Data/Paired Data/UncertainOrderedPairedData.cs b/Numerics/Data/Paired Data/UncertainOrderedPairedData.cs index d513f439..037e255f 100644 --- a/Numerics/Data/Paired Data/UncertainOrderedPairedData.cs +++ b/Numerics/Data/Paired Data/UncertainOrderedPairedData.cs @@ -722,7 +722,9 @@ public void InsertRange(int index, IList items) { if (_isValid == true) { - if (OrdinateValid(index) == false) + // Each newly inserted position must be validated against its own neighbors, + // not the constant first insertion index. + if (OrdinateValid(i) == false) _isValid = false; } } diff --git a/Test_Numerics/Data/Paired Data/Test_PairedData.cs b/Test_Numerics/Data/Paired Data/Test_PairedData.cs index 4fe6d11e..881c6854 100644 --- a/Test_Numerics/Data/Paired Data/Test_PairedData.cs +++ b/Test_Numerics/Data/Paired Data/Test_PairedData.cs @@ -135,6 +135,30 @@ public void Test_ReadWriteXElement() Assert.IsTrue(_dataset4 == newDataset4); } + /// + /// Collection mutations keep the range guard and the validity flag honest: removing a + /// trailing range works (the old guard rejected index + count == Count as out of range and + /// silently removed nothing), a range running past the end still removes nothing, and + /// appending a well-ordered point after an invalid pair cannot flip the collection back to + /// valid, because appending can never repair an earlier violation. + /// + [TestMethod] + public void Test_RemoveRangeAndAddValidity() + { + var opd = new OrderedPairedData(new double[] { 1, 2, 3, 4, 5 }, new double[] { 10, 20, 30, 40, 50 }, true, SortOrder.Ascending, true, SortOrder.Ascending); + opd.RemoveRange(3, 2); + Assert.AreEqual(3, opd.Count); + Assert.AreEqual(3d, opd[2].X); + + opd.RemoveRange(2, 5); + Assert.AreEqual(3, opd.Count); + + var invalid = new OrderedPairedData(new double[] { 1, 2 }, new double[] { 20, 10 }, true, SortOrder.Ascending, true, SortOrder.Ascending); + Assert.IsFalse(invalid.IsValid); + invalid.Add(new Ordinate(3, 30)); + Assert.IsFalse(invalid.IsValid); + } + /// /// Test the various OrderedPairedData object indexing and manipulation methods /// diff --git a/Test_Numerics/Data/Paired Data/Test_PairedDataInterpolation.cs b/Test_Numerics/Data/Paired Data/Test_PairedDataInterpolation.cs index c5c3906f..62369b9a 100644 --- a/Test_Numerics/Data/Paired Data/Test_PairedDataInterpolation.cs +++ b/Test_Numerics/Data/Paired Data/Test_PairedDataInterpolation.cs @@ -524,6 +524,38 @@ public void Test_Extrapolation_NormalZTransform() Assert.IsLessThan(0.001d, low); } + /// + /// An extrapolating lookup is pushed through the lookup-axis transform, so the lookup value + /// must lie in that transform's domain. A NormalZ lookup axis throws outside the unit + /// interval, where the endpoint hold used to answer; a Logarithmic lookup axis answers NaN + /// for a negative lookup and treats lookups below 1E-16 as 1E-16. These are the documented + /// domain edges of the transforms, pinned here so a change to them is a deliberate one. + /// + [TestMethod] + public void Test_Extrapolation_LookupAxisTransformDomain() + { + // Probabilities on the lookup axis: x in (0, 1) transformed by NormalZ. + var probX = new OrderedPairedData(new double[] { 0.2d, 0.5d, 0.8d }, new double[] { 1d, 2d, 3d }, true, SortOrder.Ascending, true, SortOrder.Ascending); + Assert.Throws(() => probX.GetYFromX(-0.01d, Transform.NormalZ, Transform.None, ExtrapolationSides.Below)); + Assert.Throws(() => probX.GetYFromX(1.01d, Transform.NormalZ, Transform.None, ExtrapolationSides.Above)); + // Without extrapolation the same lookups hold the boundary ordinate. + Assert.AreEqual(1d, probX.GetYFromX(-0.01d, Transform.NormalZ, Transform.None, ExtrapolationSides.None), 0d); + Assert.AreEqual(3d, probX.GetYFromX(1.01d, Transform.NormalZ, Transform.None, ExtrapolationSides.None), 0d); + // The y-axis lookup of GetXFromY has the same domain edge. + var probY = new OrderedPairedData(new double[] { 1d, 2d, 3d }, new double[] { 0.2d, 0.5d, 0.8d }, true, SortOrder.Ascending, true, SortOrder.Ascending); + Assert.Throws(() => probY.GetXFromY(-0.01d, Transform.None, Transform.NormalZ, ExtrapolationSides.Below)); + + // A negative lookup on a Logarithmic axis is outside the domain and answers NaN. + var logs = new OrderedPairedData(new double[] { 1d, 10d, 100d }, new double[] { 2d, 20d, 200d }, true, SortOrder.Ascending, true, SortOrder.Ascending); + Assert.IsTrue(double.IsNaN(logs.GetYFromX(-5d, Transform.Logarithmic, Transform.Logarithmic, ExtrapolationSides.Below))); + // Lookups below the 1E-16 floor are treated as 1E-16, so zero and the floor answer + // identically, and finitely. + double atFloor = logs.GetYFromX(1E-16d, Transform.Logarithmic, Transform.Logarithmic, ExtrapolationSides.Below); + double atZero = logs.GetYFromX(0d, Transform.Logarithmic, Transform.Logarithmic, ExtrapolationSides.Below); + Assert.AreEqual(atFloor, atZero, 0d); + Assert.IsTrue(atZero > 0d && atZero < 1E-12); + } + /// /// GetXFromY extrapolates on the y-range sides with the same semantics as GetYFromX. /// diff --git a/Test_Numerics/Data/Paired Data/Test_PairedDataLineSimplification.cs b/Test_Numerics/Data/Paired Data/Test_PairedDataLineSimplification.cs index d8854ae9..517a092e 100644 --- a/Test_Numerics/Data/Paired Data/Test_PairedDataLineSimplification.cs +++ b/Test_Numerics/Data/Paired Data/Test_PairedDataLineSimplification.cs @@ -66,7 +66,9 @@ public void Test_VisvaligamWhyattSimplify() } /// - /// Test the Lang simplification algorithm + /// Test the Lang simplification algorithm. The count is asserted before the contents: an + /// earlier defect dropped the curve's final point, and a loop bounded by the short result's + /// own length compared only the surviving points and passed. /// [TestMethod] public void Test_LangSimplify() @@ -76,11 +78,67 @@ public void Test_LangSimplify() var test = orderedPair.LangSimplify(0.01, 2); var valid = new List() { new Ordinate(0, 0), new Ordinate(1.57, 1), new Ordinate(4.71, -1), new Ordinate(6.28, 0) }; + Assert.AreEqual(valid.Count, test.Count); for (int i = 0; i < test.Count; i++) { Assert.AreEqual(valid[i].X, test[i].X); Assert.AreEqual(valid[i].Y, test[i].Y); } } + + /// + /// The guarded early return of LangSimplify hands back a distinct object, matching the + /// other simplifiers' contract; it previously returned the receiver itself, so mutating the + /// "simplified" result silently mutated the original curve. + /// + [TestMethod] + public void Test_LangSimplify_GuardReturnsADistinctObject() + { + var data = new List() { new Ordinate(0, 0), new Ordinate(1, 1), new Ordinate(2, 0) }; + var orderedPair = new OrderedPairedData(data, true, SortOrder.Ascending, false, SortOrder.None); + + var guarded = orderedPair.LangSimplify(0d, 2); + Assert.AreNotSame(orderedPair, guarded); + Assert.AreEqual(orderedPair.Count, guarded.Count); + + guarded.Add(new Ordinate(3, 5)); + Assert.AreEqual(3, orderedPair.Count); + } + + /// + /// RamerDouglasPeucker's output parameter holds the simplified curve alone on every path. + /// The recursion branch previously appended to whatever the caller's list already held, + /// while the endpoint branch replaced it, so a pre-populated list's final content depended + /// on which branch the curve happened to take. + /// + [TestMethod] + public void Test_RamerDouglasPeucker_ClearsThePrePopulatedOutput() + { + var data = new List() { new Ordinate(0, 0), new Ordinate(3.14 / 2, 1), new Ordinate(3.14, 0), new Ordinate(3 * 3.14 / 2, -1), new Ordinate(2 * 3.14, 0) }; + var prePopulated = new List() { new Ordinate(-99, -99) }; + LineSimplification.RamerDouglasPeucker(data, 0.01, ref prePopulated); + Assert.HasCount(4, prePopulated); + Assert.AreEqual(0d, prePopulated[0].X); + + var alsoPrePopulated = new List() { new Ordinate(-99, -99) }; + LineSimplification.RamerDouglasPeucker(new List() { new Ordinate(0, 0), new Ordinate(1, 0) }, 0.01, ref alsoPrePopulated); + Assert.HasCount(2, alsoPrePopulated); + Assert.AreEqual(0d, alsoPrePopulated[0].X); + } + + /// + /// A curve whose first and last points coincide has a zero-length base segment, where the + /// perpendicular distance degenerates to the point-to-endpoint distance. Unguarded, the + /// 0/0 = NaN distance failed every comparison and the interior spike was silently dropped. + /// + [TestMethod] + public void Test_DouglasPeuckerSimplify_CoincidentEndpoints() + { + var data = new List() { new Ordinate(1, 5), new Ordinate(1, 7), new Ordinate(1, 5) }; + var orderedPair = new OrderedPairedData(data, false, SortOrder.Ascending, false, SortOrder.None); + var test = orderedPair.DouglasPeuckerSimplify(0.5); + Assert.AreEqual(3, test.Count); + Assert.AreEqual(7d, test[1].Y); + } } } diff --git a/Test_Numerics/Data/Paired Data/Test_UncertainPairedData.cs b/Test_Numerics/Data/Paired Data/Test_UncertainPairedData.cs index 7e9a857b..9bcff12a 100644 --- a/Test_Numerics/Data/Paired Data/Test_UncertainPairedData.cs +++ b/Test_Numerics/Data/Paired Data/Test_UncertainPairedData.cs @@ -218,6 +218,24 @@ public void Test_IList() } + /// + /// InsertRange validates every newly inserted position against its own neighbors. The old + /// loop re-tested the constant first insertion index on every pass, so a violation carried + /// by any later inserted ordinate left the collection reported as valid. + /// + [TestMethod] + public void Test_InsertRange_ValidatesEveryInsertedPosition() + { + var data = new UncertainOrderedPairedData(new double[] { 1, 10 }, new UnivariateDistributionBase[] { new Deterministic(1), new Deterministic(10) }, true, SortOrder.Ascending, true, SortOrder.Ascending, UnivariateDistributionType.Deterministic); + Assert.IsTrue(data.IsValid); + + // The first inserted ordinate is valid at its position; the second breaks the + // ascending x-order against its successor. + var toInsert = new List() { new UncertainOrdinate(2, new Deterministic(2)), new UncertainOrdinate(50, new Deterministic(50)) }; + data.InsertRange(1, toInsert); + Assert.IsFalse(data.IsValid); + } + /// /// Test the overloaded equality operators for the UncertainOrderedPairedData object /// From 3215988ea776e7fedbeb26695676c69ee917b55f Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Thu, 27 Aug 2026 17:54:00 -0600 Subject: [PATCH 132/222] Surface the single-factor union budget stop and hold Expm1 finite through its overflow band --- Numerics/Data/Statistics/Probability.cs | 10 ++++++++++ Numerics/Utilities/Tools.cs | 13 ++++++++++--- Test_Numerics/Utilities/Test_Tools.cs | 13 +++++++++++-- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/Numerics/Data/Statistics/Probability.cs b/Numerics/Data/Statistics/Probability.cs index c57d1be2..7ac32bb7 100644 --- a/Numerics/Data/Statistics/Probability.cs +++ b/Numerics/Data/Statistics/Probability.cs @@ -722,6 +722,11 @@ public static double NegativelyDependentUnion(IList probabilities) /// Thrown when a probability is outside [0, 1], the correlation is outside [0, 1], or the /// relative tolerance is outside the quadrature's accepted range of [1E-15, 1]. /// + /// + /// Thrown when the quadrature exhausts its function-evaluation budget before meeting the + /// requested tolerance, so a result that does not honor + /// is never returned silently. + /// public static double UnionSingleFactor(IList probabilities, double rho, double relativeTolerance = 1E-8) { if (probabilities == null || probabilities.Count == 0) @@ -776,6 +781,11 @@ public static double UnionSingleFactor(IList probabilities, double rho, ReportFailure = true }; quadrature.Integrate(); + // ReportFailure rethrows any evaluation exception, so a normal return leaves the status at + // Success or at the evaluation-budget stop. The budget stop means the requested tolerance + // was not certified, so it must not be returned as if it were. + if (quadrature.Status != Mathematics.IntegrationStatus.Success) + throw new ArithmeticException("The single-factor union quadrature exhausted its function-evaluation budget before meeting the requested tolerance."); return Tools.Clamp(quadrature.Result, 0d, 1d); } diff --git a/Numerics/Utilities/Tools.cs b/Numerics/Utilities/Tools.cs index 0be6afd9..a3c68cef 100644 --- a/Numerics/Utilities/Tools.cs +++ b/Numerics/Utilities/Tools.cs @@ -238,8 +238,11 @@ public static double Log1p(double x) /// /// Uses the compensated evaluation (u - 1) * x / log(u) with u = exp(x), which /// corrects the rounding of the exponential; when u rounds to one the input itself is - /// returned. This is the companion of for log-space probability - /// arithmetic such as survival products of many small probabilities. + /// returned. For large positive inputs the compensation's intermediate product overflows + /// while exp(x) - 1 is still finite; there the subtraction is exact to the last unit + /// anyway, so the direct difference is returned. This is the companion of + /// for log-space probability arithmetic such as survival products of + /// many small probabilities. /// public static double Expm1(double x) { @@ -247,7 +250,11 @@ public static double Expm1(double x) if (u == 1.0) return x; if (double.IsPositiveInfinity(u)) return u; if (u == 0.0) return -1.0; - return (u - 1.0) * x / Math.Log(u); + double numerator = (u - 1.0) * x; + // The product overflows only for x large enough that 1 is far below one unit in the last + // place of u, where exp(x) - 1 carries no cancellation to compensate for. + if (double.IsInfinity(numerator)) return u - 1.0; + return numerator / Math.Log(u); } /// diff --git a/Test_Numerics/Utilities/Test_Tools.cs b/Test_Numerics/Utilities/Test_Tools.cs index 68a428d8..81a7d629 100644 --- a/Test_Numerics/Utilities/Test_Tools.cs +++ b/Test_Numerics/Utilities/Test_Tools.cs @@ -605,8 +605,10 @@ public void Test_Decompress() /// /// Expm1 computes exp(x) - 1 without cancellation: tiny arguments return themselves /// exactly, small arguments match the series exp(x) - 1 = x + x^2/2 + x^3/6 to full - /// precision, deep negatives saturate at exactly -1, large arguments overflow to positive - /// infinity, and the round trip with Log1p closes. + /// precision, deep negatives saturate at exactly -1, arguments large enough that the + /// exponential itself overflows return positive infinity while the band just below, where + /// only the compensation's intermediate product overflows, stays finite, and the round trip + /// with Log1p closes. /// [TestMethod] public void Test_Expm1() @@ -624,6 +626,13 @@ public void Test_Expm1() Assert.AreEqual(double.PositiveInfinity, Tools.Expm1(800d)); Assert.AreEqual(double.PositiveInfinity, Tools.Expm1(double.PositiveInfinity)); Assert.IsTrue(double.IsNaN(Tools.Expm1(double.NaN))); + // Inside the band where exp(x) is finite but the compensation's intermediate + // (u - 1) * x overflows, the exact difference is returned: exp(x) - 1 and exp(x) agree + // to the last unit there. Unguarded, these answered positive infinity. + Assert.AreEqual(Math.Exp(705d), Tools.Expm1(705d), 0d); + Assert.AreEqual(Math.Exp(709d), Tools.Expm1(709d), 0d); + // The exponential itself first overflows just above 709.78. + Assert.AreEqual(double.PositiveInfinity, Tools.Expm1(709.8d)); foreach (double x in new[] { 1E-12, 0.5d, 3d }) { From 78880c47c511b90b7db896ae211fdf1f90440313 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Thu, 27 Aug 2026 17:54:00 -0600 Subject: [PATCH 133/222] Freeze unbisectable regions in the two-dimensional Gauss-Kronrod integrator --- .../Integration/AdaptiveGaussKronrod2D.cs | 33 ++++++++++++++++--- .../Integration/AdaptiveGuassKronrod.cs | 5 +++ .../Test_AdaptiveGaussKronrod2D.cs | 29 ++++++++++++++++ 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/Numerics/Mathematics/Integration/AdaptiveGaussKronrod2D.cs b/Numerics/Mathematics/Integration/AdaptiveGaussKronrod2D.cs index 643ccce9..a599367e 100644 --- a/Numerics/Mathematics/Integration/AdaptiveGaussKronrod2D.cs +++ b/Numerics/Mathematics/Integration/AdaptiveGaussKronrod2D.cs @@ -266,10 +266,13 @@ private sealed class Region /// error bound meets a tolerance, /// when the evaluation budget stops refinement first, and /// when every region has reached - /// (or a machine-epsilon width) without meeting a tolerance - the - /// depth-exhausted result is returned rather than silently reported as converged. The - /// computation is sequential and deterministic: identical inputs produce bit-identical - /// results. + /// (or a width bisection can no longer reduce) without meeting a + /// tolerance - the depth-exhausted result is returned rather than silently reported as + /// converged. Refinement is bounded by and + /// ; the inherited and + /// are not consulted, and + /// reports the number of region splits. The computation + /// is sequential and deterministic: identical inputs produce bit-identical results. /// public override void Integrate() { @@ -367,7 +370,9 @@ public override void Integrate() /// /// Bisects a region along its dominant-error axis, replacing it with two evaluated children. - /// A region whose split axes are both at machine-epsilon width is frozen instead. + /// A region is frozen instead when both axes are at machine-epsilon width, or when the chosen + /// axis can no longer be bisected in floating point because its midpoint rounds onto an + /// endpoint. /// /// The region list; the children are appended. /// The refinable-region heap; the children are pushed. @@ -394,16 +399,34 @@ private void SplitRegion(List regions, List<(int Index, double Error)> h splitX = true; } + // Far from the origin an axis can be wider than the absolute floor yet sit at one unit + // in the last place, where its midpoint rounds onto an endpoint and bisection would + // reproduce the region bit for bit - re-splitting an identical child once per depth + // level, at a full tensor evaluation per wasted split, until the requested depth is + // reached. The chosen axis carries the dominant error indicator, so when it can no + // longer be bisected the region's error is irreducible at floating-point resolution and + // the region is frozen with its current estimate; splitting the other axis instead could + // not reduce the dominant component and would multiply regions without converging. Region left, right; if (splitX) { double mx = 0.5 * (region.Ax + region.Bx); + if (mx <= region.Ax || mx >= region.Bx) + { + region.Frozen = true; + return; + } left = Evaluate(region.Ax, mx, region.Ay, region.By, region.Depth + 1, capture); right = Evaluate(mx, region.Bx, region.Ay, region.By, region.Depth + 1, capture); } else { double my = 0.5 * (region.Ay + region.By); + if (my <= region.Ay || my >= region.By) + { + region.Frozen = true; + return; + } left = Evaluate(region.Ax, region.Bx, region.Ay, my, region.Depth + 1, capture); right = Evaluate(region.Ax, region.Bx, my, region.By, region.Depth + 1, capture); } diff --git a/Numerics/Mathematics/Integration/AdaptiveGuassKronrod.cs b/Numerics/Mathematics/Integration/AdaptiveGuassKronrod.cs index 01330715..60e4a4cb 100644 --- a/Numerics/Mathematics/Integration/AdaptiveGuassKronrod.cs +++ b/Numerics/Mathematics/Integration/AdaptiveGuassKronrod.cs @@ -190,6 +190,11 @@ private static double[] BuildCaptureWeights() public double StandardError { get; private set; } /// + /// + /// Refinement is bounded by and + /// ; the inherited and + /// are not consulted. + /// public override void Integrate() { _squaredError = 0; diff --git a/Test_Numerics/Mathematics/Integration/Test_AdaptiveGaussKronrod2D.cs b/Test_Numerics/Mathematics/Integration/Test_AdaptiveGaussKronrod2D.cs index 0b6ce754..9e5b042c 100644 --- a/Test_Numerics/Mathematics/Integration/Test_AdaptiveGaussKronrod2D.cs +++ b/Test_Numerics/Mathematics/Integration/Test_AdaptiveGaussKronrod2D.cs @@ -330,6 +330,35 @@ public void Test_DegenerateWidth() Assert.AreEqual(1E-16, forced.Result, 1E-17); } + /// + /// Far from the origin an axis can be wider than the absolute machine-epsilon floor while + /// its width sits at one unit in the last place, where the midpoint rounds onto an endpoint + /// and bisection reproduces the region. Every node of such a region rounds onto a single + /// representable abscissa, so its error estimate is exactly zero and error-driven refinement + /// never asks to split it; the forced minimum-depth pass splits it regardless, and unguarded + /// it re-split a bit-identical child once per depth level, burning 882 evaluations per + /// wasted split. The region must freeze instead, and a splittable domain at the same offset + /// must still converge. + /// + [TestMethod] + public void Test_LargeOffsetDomain() + { + // The representable spacing at 1e16 is 2, so this x-domain is one unit in the last place + // wide and cannot be bisected. Unguarded, MinDepth = 30 cost about 27,000 evaluations. + var ulpWide = new AdaptiveGaussKronrod2D((x, y) => 1d, 1E16, 1E16 + 2d, 0, 1) { MinDepth = 30 }; + ulpWide.Integrate(); + Assert.AreEqual(IntegrationStatus.Success, ulpWide.Status); + Assert.AreEqual(2d, ulpWide.Result, 1E-8); + Assert.IsLessThan(2000, ulpWide.FunctionEvaluations); + + // A two-ulp domain bisects once into representable halves and converges to the exact + // area at the default settings. + var flat = new AdaptiveGaussKronrod2D((x, y) => 1d, 1E16, 1E16 + 4d, 0, 1); + flat.Integrate(); + Assert.AreEqual(IntegrationStatus.Success, flat.Status); + Assert.AreEqual(4d, flat.Result, 1E-8); + } + /// /// The recorder reports the final composite rule: weights sum to the domain area, weighted /// function values reproduce the result, the node count is a whole number of regions, and From 7d37e8aadceaa9e556be1c3bcead3329895d2409 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Thu, 27 Aug 2026 17:54:11 -0600 Subject: [PATCH 134/222] Name the offending sensitivity sample and finish the Sobol constructor docs --- Numerics/Data/Statistics/GlobalSensitivity.cs | 4 +++- Numerics/Sampling/SobolSequence.cs | 11 +++++++---- .../Data/Statistics/Test_GlobalSensitivity.cs | 7 +++++-- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Numerics/Data/Statistics/GlobalSensitivity.cs b/Numerics/Data/Statistics/GlobalSensitivity.cs index 3e0e1b49..b1b74b4b 100644 --- a/Numerics/Data/Statistics/GlobalSensitivity.cs +++ b/Numerics/Data/Statistics/GlobalSensitivity.cs @@ -217,8 +217,10 @@ private static void ValidateSamples(IList x, IList y, int bins) if (x.Count < bins) throw new ArgumentException("The sample must be at least as long as the bin count.", nameof(x)); for (int i = 0; i < x.Count; i++) { - if (!Tools.IsFinite(x[i]) || !Tools.IsFinite(y[i])) + if (!Tools.IsFinite(x[i])) throw new ArgumentOutOfRangeException(nameof(x), "Sample values must be finite."); + if (!Tools.IsFinite(y[i])) + throw new ArgumentOutOfRangeException(nameof(y), "Sample values must be finite."); } } diff --git a/Numerics/Sampling/SobolSequence.cs b/Numerics/Sampling/SobolSequence.cs index 4d48e138..195dadb2 100644 --- a/Numerics/Sampling/SobolSequence.cs +++ b/Numerics/Sampling/SobolSequence.cs @@ -40,7 +40,9 @@ public class SobolSequence /// Constructs a new Sobol Sequence. /// /// Optional. The spatial dimension. Default = 1. - /// + /// + /// Thrown when the dimension is less than one or greater than the maximum supported dimension. + /// public SobolSequence(int dimension = 1) { if (dimension < 1 || dimension > MAX_DIMENSION) @@ -60,7 +62,9 @@ public SobolSequence(int dimension = 1) /// /// The spatial dimension. /// The pseudorandom seed for the scrambling. - /// + /// + /// Thrown when the dimension is less than one or greater than the maximum supported dimension. + /// /// /// /// The randomization applies Matousek's linear matrix scrambling followed by a random @@ -74,8 +78,7 @@ public SobolSequence(int dimension = 1) /// /// The same seed always reproduces the same sequence. The seeded draw order is part of that /// contract: for each dimension in order, the sub-diagonal matrix bits row by row, then the - /// shift bits. The parameterless-seed constructor generates the original unrandomized - /// sequence and is unaffected. + /// shift bits. Constructing without a seed generates the unscrambled sequence. /// /// References: /// diff --git a/Test_Numerics/Data/Statistics/Test_GlobalSensitivity.cs b/Test_Numerics/Data/Statistics/Test_GlobalSensitivity.cs index 94504d82..b517fd13 100644 --- a/Test_Numerics/Data/Statistics/Test_GlobalSensitivity.cs +++ b/Test_Numerics/Data/Statistics/Test_GlobalSensitivity.cs @@ -195,8 +195,11 @@ public void Test_GuardMatrix() Assert.Throws(() => GlobalSensitivity.FirstOrderSobol(x, new double[] { 1d }, 2)); Assert.Throws(() => GlobalSensitivity.FirstOrderSobol(x, y, 1)); Assert.Throws(() => GlobalSensitivity.FirstOrderSobol(x, y, 5)); - Assert.Throws(() => GlobalSensitivity.FirstOrderSobol(new double[] { 1d, double.NaN, 3d, 4d }, y, 2)); - Assert.Throws(() => GlobalSensitivity.Pawn(x, new double[] { 1d, 2d, double.PositiveInfinity, 4d }, 2)); + // The finiteness guards name the sample the offending value came from. + var nonFiniteInput = Assert.Throws(() => GlobalSensitivity.FirstOrderSobol(new double[] { 1d, double.NaN, 3d, 4d }, y, 2)); + Assert.AreEqual("x", nonFiniteInput.ParamName); + var nonFiniteOutput = Assert.Throws(() => GlobalSensitivity.Pawn(x, new double[] { 1d, 2d, double.PositiveInfinity, 4d }, 2)); + Assert.AreEqual("y", nonFiniteOutput.ParamName); Assert.Throws(() => GlobalSensitivity.BorgonovoDelta(x, y, 2, 1)); Assert.Throws(() => GlobalSensitivity.BorgonovoDelta(x, y, 2, 5)); Assert.AreEqual(0d, GlobalSensitivity.FirstOrderSobol(x, new double[] { 3d, 3d, 3d, 3d }, 2), 0d); From c6c6ebf8b1a6eed9e444d07ef7a33f8294cd4b16 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Thu, 27 Aug 2026 17:54:11 -0600 Subject: [PATCH 135/222] Validate the k-NN query shape and make the remaining mean reductions deterministic --- Numerics/Data/Time Series/TimeSeries.cs | 4 ++- .../Supervised/KNearestNeighbors.cs | 9 +++++-- .../Machine Learning/Supervised/Test_kNN.cs | 25 +++++++++++++++++++ 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/Numerics/Data/Time Series/TimeSeries.cs b/Numerics/Data/Time Series/TimeSeries.cs index c3bdf29c..46cb4800 100644 --- a/Numerics/Data/Time Series/TimeSeries.cs +++ b/Numerics/Data/Time Series/TimeSeries.cs @@ -1512,7 +1512,9 @@ public double[] Percentiles(IList kValues) monthlySummary[index - 1, 4] = Statistics.Statistics.Percentile(monthlyData, 0.75, true); monthlySummary[index - 1, 5] = Statistics.Statistics.Percentile(monthlyData, 0.95, true); monthlySummary[index - 1, 6] = monthlyData[monthlyData.Count - 1]; - monthlySummary[index - 1, 7] = Statistics.Statistics.ParallelMean(monthlyData); + // The mean is accumulated sequentially so the reduction is deterministic on every + // host regardless of processor count. + monthlySummary[index - 1, 7] = Statistics.Statistics.Mean(monthlyData); }); return monthlySummary; } diff --git a/Numerics/Machine Learning/Supervised/KNearestNeighbors.cs b/Numerics/Machine Learning/Supervised/KNearestNeighbors.cs index b271aa2c..ac6146eb 100644 --- a/Numerics/Machine Learning/Supervised/KNearestNeighbors.cs +++ b/Numerics/Machine Learning/Supervised/KNearestNeighbors.cs @@ -242,7 +242,10 @@ public KNearestNeighbors(Matrix x, Vector y, int k) /// The test matrix of predictors private int[]? kNN(Matrix xTrain, Vector yTrain, Matrix xTest) { - if (NumberOfFeatures != xTrain.NumberOfColumns) return null!; + // The guard must compare the query to the training matrix, matching kNNPredict; a query + // with the wrong column count would otherwise compute partial-dimension distances or + // index past the end of the training rows. + if (xTest.NumberOfColumns != xTrain.NumberOfColumns) return null!; int R = xTest.NumberOfRows; var result = new int[R * K]; for (int i = 0; i < R; i++) @@ -388,7 +391,9 @@ public KNearestNeighbors(Matrix x, Vector y, int k) for (int j = 0; j < percentiles.Length; j++) output[idx, j] = Statistics.Percentile(values, percentiles[j], true); - output[idx, 3] = Statistics.ParallelMean(values); + // The mean is accumulated sequentially so the reduction is deterministic on every + // host regardless of processor count. + output[idx, 3] = Statistics.Mean(values); }); return output; diff --git a/Test_Numerics/Machine Learning/Supervised/Test_kNN.cs b/Test_Numerics/Machine Learning/Supervised/Test_kNN.cs index f6164a75..8e43df4b 100644 --- a/Test_Numerics/Machine Learning/Supervised/Test_kNN.cs +++ b/Test_Numerics/Machine Learning/Supervised/Test_kNN.cs @@ -168,6 +168,31 @@ public void Test_GetNeighbors_MultiRow() Assert.IsGreaterThanOrEqualTo(6, neighbors[3], $"Second query's 2nd nearest should be in cluster B, got index {neighbors[3]}"); } + /// + /// A query whose column count differs from the training matrix must be rejected with a null + /// result, matching Predict. GetNeighbors' old guard compared the training matrix against + /// itself, so a narrower query silently computed partial-dimension distances and a wider + /// query threw an IndexOutOfRangeException from inside the distance helper. + /// + [TestMethod] + public void Test_GetNeighbors_QueryShapeMismatch_ReturnsNull() + { + var x1 = new double[] { 0, 1, 0, 1, 2, 0, 100, 101, 100, 101, 102, 100 }; + var x2 = new double[] { 0, 0, 1, 1, 0, 2, 100, 100, 101, 101, 100, 102 }; + var xTrain = new Matrix(new List { x1, x2 }); + var yTrain = new Vector(new double[] { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 }); + var knn = new KNearestNeighbors(xTrain, yTrain, 2); + + // One column instead of two: the 1D overload builds a column of one-feature queries. + Assert.IsNull(knn.GetNeighbors(new double[] { 0.5 })); + // Predict already rejected the same shape; the two entry points must agree. + Assert.IsNull(knn.Predict(new double[] { 0.5 })); + // Three columns instead of two. + Assert.IsNull(knn.GetNeighbors(new double[,] { { 0.5, 0.5, 0.5 } })); + // The matching shape still answers. + Assert.IsNotNull(knn.GetNeighbors(new double[,] { { 0.5, 0.5 } })); + } + /// /// Verify that exact distance ties are resolved by the lowest training-row index. /// From f6437cab6fd3f45ce2fa92ccb23a2c05c8dd2608 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Thu, 27 Aug 2026 17:54:11 -0600 Subject: [PATCH 136/222] Reject Jenks natural breaks data without two distinct values --- .../Unsupervised/JenksNaturalBreaks.cs | 20 +++++++++++++++++++ .../Unsupervised/Test_JenksNaturalBreaks.cs | 19 ++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/Numerics/Machine Learning/Unsupervised/JenksNaturalBreaks.cs b/Numerics/Machine Learning/Unsupervised/JenksNaturalBreaks.cs index 43ff0dcf..408e0d2b 100644 --- a/Numerics/Machine Learning/Unsupervised/JenksNaturalBreaks.cs +++ b/Numerics/Machine Learning/Unsupervised/JenksNaturalBreaks.cs @@ -36,6 +36,11 @@ public class JenksNaturalBreaks /// The input data array to be classified. /// The number of desired clusters (or classes). /// Determines if the data array is sorted. Default = false. + /// Thrown when the data array is null. + /// + /// Thrown when the data array is empty, when the cluster count exceeds the data length, or + /// when more than one cluster is requested of data with fewer than two distinct values. + /// public JenksNaturalBreaks(IList data, int numberOfClusters, bool isDataSorted = false) { if (data == null) throw new ArgumentNullException(nameof(data), "The data array is null."); @@ -49,6 +54,11 @@ public JenksNaturalBreaks(IList data, int numberOfClusters, bool isDataS { Array.Sort(sortedData); } + // With every value identical, every candidate split has zero variance and the dynamic + // program's walkback produces a negative class limit, so the failure would otherwise + // surface as an index exception from inside the fitted algorithm. + if (numberOfClusters > 1 && sortedData[0] == sortedData[sortedData.Length - 1]) + throw new ArgumentException("The data must contain at least two distinct values to form more than one cluster.", nameof(data)); SortedData = sortedData; NumberOfClusters = numberOfClusters; @@ -61,6 +71,11 @@ public JenksNaturalBreaks(IList data, int numberOfClusters, bool isDataS /// The input data array to be classified. /// The number of desired clusters (or classes). /// Determines if the data array is sorted. Default = false. + /// Thrown when the data array is null. + /// + /// Thrown when the data array is empty, when the cluster count exceeds the data length, or + /// when more than one cluster is requested of data with fewer than two distinct values. + /// public JenksNaturalBreaks(IList data, int numberOfClusters, bool isDataSorted = false) { if (data == null) throw new ArgumentNullException(nameof(data), "The data array is null."); @@ -74,6 +89,11 @@ public JenksNaturalBreaks(IList data, int numberOfClusters, bool isDataSo { Array.Sort(sortedData); } + // With every value identical, every candidate split has zero variance and the dynamic + // program's walkback produces a negative class limit, so the failure would otherwise + // surface as an index exception from inside the fitted algorithm. + if (numberOfClusters > 1 && sortedData[0] == sortedData[sortedData.Length - 1]) + throw new ArgumentException("The data must contain at least two distinct values to form more than one cluster.", nameof(data)); SortedData = new double[sortedData.Length]; for (int i = 0; i < sortedData.Length; i++) diff --git a/Test_Numerics/Machine Learning/Unsupervised/Test_JenksNaturalBreaks.cs b/Test_Numerics/Machine Learning/Unsupervised/Test_JenksNaturalBreaks.cs index 3ee838e1..00278f84 100644 --- a/Test_Numerics/Machine Learning/Unsupervised/Test_JenksNaturalBreaks.cs +++ b/Test_Numerics/Machine Learning/Unsupervised/Test_JenksNaturalBreaks.cs @@ -61,5 +61,24 @@ public void Test_Jenks_9Classes() } } + /// + /// Data with a single distinct value cannot form more than one cluster: every candidate + /// split has zero variance, and the dynamic program's walkback previously produced a + /// negative class limit that surfaced as an IndexOutOfRangeException from inside the fitted + /// algorithm. The degenerate input must be rejected up front, while a single-cluster fit of + /// the same data remains valid. + /// + [TestMethod] + public void Test_Jenks_AllIdenticalValues() + { + var identical = new double[20]; + for (int i = 0; i < identical.Length; i++) { identical[i] = 3.5; } + Assert.Throws(() => new JenksNaturalBreaks(identical, 3)); + + var single = new JenksNaturalBreaks(identical, 1); + Assert.HasCount(1, single.Clusters); + Assert.AreEqual(3.5, single.Breaks[0], 0d); + } + } } From 95c5a3e96764585f5f7143d9213932c8816d383d Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Thu, 27 Aug 2026 17:54:11 -0600 Subject: [PATCH 137/222] Compute the k-means centroid before a first-iteration convergence exit --- Numerics/Machine Learning/Unsupervised/KMeans.cs | 10 ++++++++++ .../Machine Learning/Unsupervised/Test_KMeans.cs | 15 +++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/Numerics/Machine Learning/Unsupervised/KMeans.cs b/Numerics/Machine Learning/Unsupervised/KMeans.cs index 05aa73c5..99bcc9f3 100644 --- a/Numerics/Machine Learning/Unsupervised/KMeans.cs +++ b/Numerics/Machine Learning/Unsupervised/KMeans.cs @@ -163,7 +163,17 @@ public void Train(int seed = -1, bool kMeansPlusPlus= true) } // Stop when the E-step doesn't change the assignment of any data point if (labelsChanged == false) + { + // With a single cluster, and for any fit whose first assignment matches the + // zero-initialized labels, the loop exits before a centroid has ever been + // computed, leaving the initializer's seed point as the reported mean. Run the + // M-step once so the reported means are the centroids of the final labels; on a + // later iteration convergence means the labels did not change, so the centroids + // already reflect them and the reported means are untouched. + if (Iterations == 1) + Means = GetCentroids(Labels); break; + } // Perform M-step // Calculate new centroids from the clusters diff --git a/Test_Numerics/Machine Learning/Unsupervised/Test_KMeans.cs b/Test_Numerics/Machine Learning/Unsupervised/Test_KMeans.cs index 7654a5e3..97429be4 100644 --- a/Test_Numerics/Machine Learning/Unsupervised/Test_KMeans.cs +++ b/Test_Numerics/Machine Learning/Unsupervised/Test_KMeans.cs @@ -89,5 +89,20 @@ public void Test_KMeans_ClusterCountValidation() Assert.ThrowsExactly(() => new KMeans(rows, 13)); } + /// + /// A single-cluster fit converges on its first E-step, which previously exited before any + /// centroid had been computed, so the reported mean was whatever data point the k-means++ + /// initializer happened to seed (this fixture reported 10). The M-step must run once so the + /// reported mean is the mean of the assigned points. + /// + [TestMethod] + public void Test_KMeans_SingleCluster_ReportsTheSampleMean() + { + var km = new KMeans(new double[] { 1, 2, 3, 10, 11, 12 }, 1); + km.Train(7); + Assert.AreEqual(1, km.Iterations); + Assert.AreEqual(6.5, km.Means[0, 0], 1E-12); + } + } } From 55bde0d0bdce2fe76957ae3af6c9476a1516f952 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Thu, 27 Aug 2026 17:54:11 -0600 Subject: [PATCH 138/222] Scale the augmented Lagrangian objective for maximization --- .../Constrained/AugmentedLagrange.cs | 12 +++- .../Constrained/Test_AugmentedLagrange.cs | 57 +++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/Numerics/Mathematics/Optimization/Constrained/AugmentedLagrange.cs b/Numerics/Mathematics/Optimization/Constrained/AugmentedLagrange.cs index 6227fc26..26d6b1bf 100644 --- a/Numerics/Mathematics/Optimization/Constrained/AugmentedLagrange.cs +++ b/Numerics/Mathematics/Optimization/Constrained/AugmentedLagrange.cs @@ -94,9 +94,19 @@ public AugmentedLagrange(Func objectiveFunction, Optimizer opt /// /// The Augmented Lagrangian objective function. /// + /// The parameter values to evaluate. + /// The scaled primary objective plus the constraint penalties. + /// + /// The primary objective enters on the optimizer's scaled convention, exactly as + /// applies it, so a maximization negates it here while the + /// constraint penalties stay direction-neutral and are always added. The inner search then + /// always minimizes this function. Without the scale the inner search minimized the raw + /// objective regardless of the requested direction, so a maximization reported the + /// constrained minimum. + /// private double augmentedLagrangianFunction(double[] x) { - double phi = _primaryObjectiveFunction(x); + double phi = functionScale * _primaryObjectiveFunction(x); double rho2 = 0.5 * rho; int lambdaIdx = 0, muIdx = 0, nuIdx = 0; diff --git a/Test_Numerics/Mathematics/Optimization/Constrained/Test_AugmentedLagrange.cs b/Test_Numerics/Mathematics/Optimization/Constrained/Test_AugmentedLagrange.cs index daf6562b..917b9206 100644 --- a/Test_Numerics/Mathematics/Optimization/Constrained/Test_AugmentedLagrange.cs +++ b/Test_Numerics/Mathematics/Optimization/Constrained/Test_AugmentedLagrange.cs @@ -245,5 +245,62 @@ public void Test_MixedConstraints_Binding() Assert.AreEqual(1.0, solver.BestParameterSet.Values[0], 0.1); Assert.AreEqual(3.0, solver.BestParameterSet.Values[1], 0.1); } + + /// + /// Maximization drives the inner search in the requested direction. The augmented objective + /// previously entered the inner minimization unscaled, so a maximization reported the + /// constrained minimum with a Success status: this construct returned x = -10 at the lower + /// bound instead of the constrained maximum at x = 1. + /// + [TestMethod] + public void Test_Maximize_InequalityConstraint() + { + var constraint = new Constraint((x) => x[0], 1, 1.0, ConstraintType.LesserThanOrEqualTo); + Func func = (double[] x) => -Math.Pow(x[0] - 3, 2); + var innerSolver = new BFGS(func, 1, new double[] { -5 }, new double[] { -10 }, new double[] { 10 }); + var solver = new AugmentedLagrange(func, innerSolver, new IConstraint[] { constraint }); + solver.Maximize(); + + Assert.AreEqual(1.0, solver.BestParameterSet.Values[0], 1E-3); + Assert.AreEqual(-4.0, func(solver.BestParameterSet.Values), 1E-3); + } + + /// + /// A maximization whose constraint is inactive at the optimum reaches the unconstrained + /// maximum; previously it reported a box corner instead. + /// + [TestMethod] + public void Test_Maximize_InactiveConstraint() + { + var constraint = new Constraint((x) => x[0] + x[1], 2, 20.0, ConstraintType.LesserThanOrEqualTo); + Func func = (double[] x) => -(Math.Pow(x[0] - 1, 2) + Math.Pow(x[1] - 3, 2)); + var innerSolver = new BFGS(func, 2, new double[] { 5, 5 }, new double[] { 0, 0 }, new double[] { 10, 10 }); + var solver = new AugmentedLagrange(func, innerSolver, new IConstraint[] { constraint }); + solver.Maximize(); + + Assert.AreEqual(1.0, solver.BestParameterSet.Values[0], 1E-3); + Assert.AreEqual(3.0, solver.BestParameterSet.Values[1], 1E-3); + Assert.AreEqual(0.0, func(solver.BestParameterSet.Values), 1E-4); + } + + /// + /// Maximization subject to an equality constraint lands on the constrained stationary + /// point. + /// + [TestMethod] + public void Test_Maximize_EqualityConstraint() + { + // The unconstrained maximum sits at (4, 4); its projection onto x + y = 4 is (2, 2), + // where the objective is -8. + var constraint = new Constraint((x) => x[0] + x[1], 2, 4.0, ConstraintType.EqualTo); + Func func = (double[] x) => -(Math.Pow(x[0] - 4, 2) + Math.Pow(x[1] - 4, 2)); + var innerSolver = new BFGS(func, 2, new double[] { 0, 0 }, new double[] { -10, -10 }, new double[] { 10, 10 }); + var solver = new AugmentedLagrange(func, innerSolver, new IConstraint[] { constraint }); + solver.Maximize(); + + Assert.AreEqual(2.0, solver.BestParameterSet.Values[0], 1E-3); + Assert.AreEqual(2.0, solver.BestParameterSet.Values[1], 1E-3); + Assert.AreEqual(-8.0, func(solver.BestParameterSet.Values), 1E-3); + } } } From aa2c6194453582e7a146bc13b9d849b98c4ea358 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Thu, 27 Aug 2026 17:54:11 -0600 Subject: [PATCH 139/222] Assert the generalized linear model information criteria --- .../Supervised/Test_GeneralizedLinearModel.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Test_Numerics/Machine Learning/Supervised/Test_GeneralizedLinearModel.cs b/Test_Numerics/Machine Learning/Supervised/Test_GeneralizedLinearModel.cs index d10f075b..164721ff 100644 --- a/Test_Numerics/Machine Learning/Supervised/Test_GeneralizedLinearModel.cs +++ b/Test_Numerics/Machine Learning/Supervised/Test_GeneralizedLinearModel.cs @@ -61,6 +61,13 @@ public void Test_SimpleLinearRegression() Assert.AreEqual(se, true_se, 1E-3); Assert.AreEqual(df, true_df); + // The information criteria are asserted so the reference block below cannot go stale: + // an earlier revision of that block carried an AIC of 71.18 from a since-changed + // likelihood convention, which nothing checked. + Assert.AreEqual(343.2561, GLM.AIC, 1E-3); + Assert.AreEqual(343.3213, GLM.AICc, 1E-3); + Assert.AreEqual(349.7183, GLM.BIC, 1E-3); + // Test summary output table var summary = GLM.Summary(); for (int i = 0; i < summary.Count; i++) @@ -79,7 +86,7 @@ β1 0.28057 0.04744 5.914 3.34E-009 *** Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1 Residual standard error: 0.6026 on 185 degrees of freedom - AIC: 71.1801 AICc: 71.2453 BIC: 77.6423 + AIC: 343.2561 AICc: 343.3213 BIC: 349.7183 Residuals: Min 1Q Median 3Q Max From 2bfbc35a62d5cfc2d7c563075f36ce944c273b27 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Thu, 27 Aug 2026 17:54:12 -0600 Subject: [PATCH 140/222] Refresh the README and docs feature and count claims --- README.md | 12 ++++++------ docs/index.md | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index abfaec43..e0686dc2 100644 --- a/README.md +++ b/README.md @@ -27,23 +27,23 @@ Or search for [RMC.Numerics](https://www.nuget.org/packages/RMC.Numerics/) in th | Section | Topics | |---------|--------| -| [Mathematics](docs/mathematics/integration.md) | Integration, differentiation, optimization, root finding, linear algebra, ODE solvers, special functions | +| [Mathematics](docs/mathematics/integration.md) | Adaptive one- and two-dimensional integration, differentiation, optimization, root finding, linear algebra, ODE solvers, special functions | | [Data](docs/data/interpolation.md) | Interpolation, linear regression, time series analysis | -| [Statistics](docs/statistics/descriptive.md) | Descriptive statistics, goodness-of-fit metrics, hypothesis tests | -| [Distributions](docs/distributions/univariate.md) | 40+ univariate distributions, parameter estimation, uncertainty analysis, copulas, multivariate distributions | +| [Statistics](docs/statistics/descriptive.md) | Descriptive and weighted statistics, goodness-of-fit metrics, hypothesis tests, global sensitivity analysis | +| [Distributions](docs/distributions/univariate.md) | 43 univariate distributions, parameter estimation, uncertainty analysis, copulas, multivariate distributions | | [Machine Learning](docs/machine-learning/machine-learning.md) | GLM, decision trees, random forests, KNN, naive Bayes, k-means, GMM | -| [Sampling](docs/sampling/mcmc.md) | MCMC (RWMH, ARWMH, DE-MCz, HMC, NUTS, Gibbs), random generation, convergence diagnostics | +| [Sampling](docs/sampling/mcmc.md) | MCMC (RWMH, ARWMH, DE-MCz, HMC, NUTS, Gibbs), random generation, scrambled quasi-random sequences, convergence diagnostics | | [References](docs/references.md) | Consolidated bibliography | ## Prerequisites - .NET 8+ runtime (or .NET Framework 4.8.1 on Windows). Install the [.NET SDK](https://dotnet.microsoft.com/download) if you don't already have it. - - Microsoft .NET SDK 10.1.326.7603 is available on the App Portal for Corps users + - The Microsoft .NET SDK is available on the App Portal for Corps users. ## Support USACE-RMC is committed to maintaining and supporting the library with regular updates, bug fixes, and enhancements. -The repository includes a unit testing library with over 1,000 tests that also serve as usage examples for the classes and methods in the library. +The repository includes a unit testing library with over 2,300 tests that also serve as usage examples for the classes and methods in the library. ## Contributing diff --git a/docs/index.md b/docs/index.md index 4d21db08..8f6981fc 100644 --- a/docs/index.md +++ b/docs/index.md @@ -15,7 +15,7 @@ The library is designed for engineers, scientists, and researchers who need reli ## Key Features ### Probability Distributions -- 40+ univariate probability distributions with PDF, CDF, and inverse CDF +- 43 univariate probability distributions with PDF, CDF, and inverse CDF - Multiple parameter estimation methods (Method of Moments, L-Moments, Maximum Likelihood) - Uncertainty analysis via bootstrap resampling - Bivariate copulas for dependency modeling @@ -147,7 +147,7 @@ var results = sampler.Output; | [Goodness-of-Fit](statistics/goodness-of-fit.md) | Model evaluation metrics | | [Hypothesis Tests](statistics/hypothesis-tests.md) | Statistical hypothesis testing | | **Distributions** | | -| [Univariate Distributions](distributions/univariate.md) | 40+ probability distributions with PDF, CDF, and quantile functions | +| [Univariate Distributions](distributions/univariate.md) | 43 probability distributions with PDF, CDF, and quantile functions | | [Parameter Estimation](distributions/parameter-estimation.md) | Fitting distributions to data | | [Uncertainty Analysis](distributions/uncertainty-analysis.md) | Bootstrap and confidence intervals | | [Copulas](distributions/copulas.md) | Dependency modeling with copulas | From 8e4f08ae05fbc48ae9ca82e23599516d7107dcc3 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Thu, 27 Aug 2026 18:00:58 -0600 Subject: [PATCH 141/222] Prepare v2.2.0 release --- .github/workflows/Snapshot.yml | 4 ++-- CITATION.cff | 4 ++-- Numerics/Numerics.csproj | 6 +++--- codemeta.json | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/Snapshot.yml b/.github/workflows/Snapshot.yml index 01ffb957..7680e9a2 100644 --- a/.github/workflows/Snapshot.yml +++ b/.github/workflows/Snapshot.yml @@ -10,8 +10,8 @@ jobs: with: dotnet-version: '10.0.x' project-names: 'Numerics' - # Snapshot workflow appends .-dev, so this tracks the next development patch after v2.1.4. - version: '2.1.5' + # Snapshot workflow appends .-dev, so this tracks the next development patch after v2.2.0. + version: '2.2.1' # PR integration already runs the full multi-target test suite. run-tests: false nuget-source: 'https://www.hec.usace.army.mil/nexus/repository/consequences-nuget-public/' diff --git a/CITATION.cff b/CITATION.cff index 6572bd79..229dd878 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -2,8 +2,8 @@ cff-version: 1.2.0 message: "If you use this software, please cite our article in the Journal of Open Source Software." type: software title: "Numerics: A .NET Library for Numerical Computing, Statistical Analysis, and Risk Assessment" -version: "2.1.4" -date-released: "2026-07-17" +version: "2.2.0" +date-released: "2026-08-27" license: 0BSD repository-code: "https://github.com/USACE-RMC/Numerics" url: "https://github.com/USACE-RMC/Numerics" diff --git a/Numerics/Numerics.csproj b/Numerics/Numerics.csproj index af134884..fd205df0 100644 --- a/Numerics/Numerics.csproj +++ b/Numerics/Numerics.csproj @@ -28,9 +28,9 @@ - 2.1.4 - Version 2.1.4 adds non-throwing univariate distribution factory creation, preserves valid zero-inflated mixture weights, hardens Box-Cox and Yeo-Johnson fitting and inversion against invalid candidates, and enforces ascending empirical distribution ordinates with expanded regression coverage. - 2.1.4.0 + 2.2.0 + Version 2.2.0 adds weighted statistics, given-data global sensitivity estimators, a two-dimensional adaptive Gauss-Kronrod integrator, seeded Sobol scrambling, conditional copula functions with serialization and factories, composable univariate functions, sided transform-aware paired-data extrapolation, and adaptive NUTS with gradient reuse in NUTS and HMC, corrects validation, tie-correction, optimization, and machine learning edge cases, and pins seeded streams and deterministic reductions with expanded regression coverage. + 2.2.0.0 diff --git a/codemeta.json b/codemeta.json index f44b2759..55687461 100644 --- a/codemeta.json +++ b/codemeta.json @@ -4,9 +4,9 @@ "name": "Numerics: A .NET Library for Numerical Computing, Statistical Analysis, and Risk Assessment", "alternateName": "Numerics", "description": "A free and open-source .NET library providing numerical methods, probability distributions, statistical analysis, and Bayesian inference tools for quantitative risk assessment in water resources engineering.", - "version": "2.1.4", + "version": "2.2.0", "dateCreated": "2023-09-28", - "dateModified": "2026-07-17", + "dateModified": "2026-08-27", "license": "https://spdx.org/licenses/0BSD", "codeRepository": "https://github.com/USACE-RMC/Numerics", "issueTracker": "https://github.com/USACE-RMC/Numerics/issues", From 91ccaf9eb45639c48d7413df1f8733c70f74a29b Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 28 Aug 2026 07:54:37 -0600 Subject: [PATCH 142/222] Carry the extrapolation policy on the tabular function and empirical distribution wrappers The sided extrapolation shipped on OrderedPairedData.GetYFromX/GetXFromY was unreachable from the two evaluation wrappers, which always called the 3-arg endpoint-hold forms. Both wrappers now expose an ExtrapolationSides Extrapolation property (default None, bit-identical to the historical hold) forwarded into every internal lookup. TabularFunction forwards one policy through both lookup directions of the same extended boundary segments; the AllowNegativeYValues floor still binds. EmpiricalDistribution defines its sides in X space regardless of the stored probability orientation (the survival form maps its lookup sides internally), keeps the CDF clamped to [0, 1], keeps InverseCDF total and monotone over the extended tails (the 1e-16 floors evaluate the extended lookup at the floor on enabled sides, and the endpoint clamp applies only to disabled sides), copies the policy on Clone, and deliberately retains the table-span PDF support. The policy serializes only when non-default, so every stored form is unchanged. Tests: default-None value pins on both wrappers in every transform space, hand-computed sided extensions both directions, the CDF unit-interval clamp under a linear probability axis, descending-orientation parity with the ascending twin, inverse monotonicity across the extended tails, clone and conditional-presence serialization round trips, and no-regression pins on the distributions that use the empirical machinery under the hood (Mixture and CompetingRisks internals stay at the default hold; KernelDensity carries its own table and gates, unreachable by the policy). --- .../Univariate/EmpiricalDistribution.cs | 77 ++++++- Numerics/Functions/TabularFunction.cs | 26 ++- .../Univariate/Test_CompetingRisks.cs | 27 +++ .../Univariate/Test_EmpiricalDistribution.cs | 199 ++++++++++++++++++ .../Univariate/Test_KernelDensity.cs | 15 ++ .../Distributions/Univariate/Test_Mixture.cs | 31 +++ Test_Numerics/Functions/Test_Functions.cs | 82 ++++++++ .../Test_UnivariateFunctionFactory.cs | 33 +++ 8 files changed, 479 insertions(+), 11 deletions(-) diff --git a/Numerics/Distributions/Univariate/EmpiricalDistribution.cs b/Numerics/Distributions/Univariate/EmpiricalDistribution.cs index 4539fd01..003efd2b 100644 --- a/Numerics/Distributions/Univariate/EmpiricalDistribution.cs +++ b/Numerics/Distributions/Univariate/EmpiricalDistribution.cs @@ -178,6 +178,28 @@ public EmpiricalDistribution(IList sample, PlottingPositions.PlottingPos /// public Transform ProbabilityTransform { get; set; } = Transform.NormalZ; + /// + /// The extrapolation policy applied to out-of-range lookups, defined in X space: + /// Below extends beyond the smallest table X value and Above beyond the largest, + /// matching the / semantics regardless of the + /// stored probability orientation. Default = None, which reproduces the historical + /// endpoint hold exactly. + /// + /// + /// + /// Extension is linear in the configured transform spaces on the table's boundary + /// segments. output is always clamped to [0, 1] — a probability + /// axis under a None transform extends linearly and can leave the unit interval, while + /// the normal-Z transform back-transforms into (0, 1) by itself. + /// stays monotone and total over the extended tails: + /// probabilities at or beyond the 1e-16 floors evaluate the extended lookup at the floor + /// rather than holding the endpoint, and the endpoint clamp is applied only on sides + /// whose extension is disabled. deliberately retains its + /// table-span support. + /// + /// + public ExtrapolationSides Extrapolation { get; set; } = ExtrapolationSides.None; + /// public override int NumberOfParameters { @@ -497,14 +519,16 @@ public override double CDF(double X) { if (_parametersValid == false) ValidateData(opd, _pValues, true); double p = 0; + // The lookup axis is X, so the X-space extrapolation sides forward unchanged in + // either probability orientation; the [0,1] clamp below bounds any extended output. if (opd.OrderY == SortOrder.Ascending || opd.OrderY == SortOrder.None) { - p = opd.GetYFromX(X, XTransform, ProbabilityTransform); + p = opd.GetYFromX(X, XTransform, ProbabilityTransform, Extrapolation); } else { // If descending then it is a survival function - p = 1d - opd.GetYFromX(X, XTransform, ProbabilityTransform); + p = 1d - opd.GetYFromX(X, XTransform, ProbabilityTransform, Extrapolation); } return p < 0d ? 0d : p > 1d ? 1d : p; } @@ -519,25 +543,44 @@ public override double InverseCDF(double probability) double min = Minimum; double max = Maximum; - if (probability <= 1E-16) return min; - if (probability >= 1 - 1E-16) return max; + // The far-tail floors stay total under extrapolation: an enabled X side evaluates the + // extended lookup at the floor (keeping the normal-Z transform finite and the inverse + // monotone) instead of holding the endpoint. + if (probability <= 1E-16) + { + if ((Extrapolation & ExtrapolationSides.Below) == 0) return min; + probability = 1E-16; + } + if (probability >= 1 - 1E-16) + { + if ((Extrapolation & ExtrapolationSides.Above) == 0) return max; + probability = 1 - 1E-16; + } double x = 0; if (opd.OrderY == SortOrder.Ascending || opd.OrderY == SortOrder.None) { - x = opd.GetXFromY(probability, XTransform, ProbabilityTransform); + x = opd.GetXFromY(probability, XTransform, ProbabilityTransform, Extrapolation); } else { - // If descending then it is a survival function - x = opd.GetXFromY(1d - probability, XTransform, ProbabilityTransform); + // If descending then it is a survival function. The lookup runs on the stored + // (exceedance) value axis, where the small stored values sit at large X — so the + // X-space policy maps to the lookup axis with its sides swapped. + var mapped = ExtrapolationSides.None; + if ((Extrapolation & ExtrapolationSides.Below) != 0) mapped |= ExtrapolationSides.Above; + if ((Extrapolation & ExtrapolationSides.Above) != 0) mapped |= ExtrapolationSides.Below; + x = opd.GetXFromY(1d - probability, XTransform, ProbabilityTransform, mapped); } - return x < min ? min : x > max ? max : x; + // Clamp only the sides whose extension is disabled, in X space. + if (x < min && (Extrapolation & ExtrapolationSides.Below) == 0) return min; + if (x > max && (Extrapolation & ExtrapolationSides.Above) == 0) return max; + return x; } /// public override UnivariateDistributionBase Clone() { - return new EmpiricalDistribution(XValues, ProbabilityValues) { XTransform = XTransform, ProbabilityTransform = ProbabilityTransform }; + return new EmpiricalDistribution(XValues, ProbabilityValues) { XTransform = XTransform, ProbabilityTransform = ProbabilityTransform, Extrapolation = Extrapolation }; } /// @@ -829,6 +872,12 @@ public override XElement ToXElement() result.SetAttributeValue(nameof(Type), Type.ToString()); result.SetAttributeValue(nameof(XTransform), XTransform.ToString()); result.SetAttributeValue(nameof(ProbabilityTransform), ProbabilityTransform.ToString()); + // Conditional presence: the attribute is written only when non-default so that every + // pre-existing serialized form remains byte-identical. + if (Extrapolation != ExtrapolationSides.None) + { + result.SetAttributeValue(nameof(Extrapolation), Extrapolation.ToString()); + } var xValues = new string[XValues.Count]; var pValues = new string[ProbabilityValues.Count]; @@ -908,6 +957,16 @@ public static EmpiricalDistribution FromXElement(XElement xElement) throw new ArgumentException("The serialized empirical distribution has an invalid probability transform.", nameof(xElement)); distribution.ProbabilityTransform = probabilityTransform; } + + // Optional-but-validated (conditional presence): absent reads as None. + var extrapolationAttribute = xElement.Attribute(nameof(Extrapolation)); + if (extrapolationAttribute != null) + { + if (!Enum.TryParse(extrapolationAttribute.Value, out ExtrapolationSides extrapolation) + || !Enum.IsDefined(typeof(ExtrapolationSides), extrapolation)) + throw new ArgumentException("The serialized empirical distribution has an invalid extrapolation policy.", nameof(xElement)); + distribution.Extrapolation = extrapolation; + } return distribution; } diff --git a/Numerics/Functions/TabularFunction.cs b/Numerics/Functions/TabularFunction.cs index 5d4e6942..541de7d5 100644 --- a/Numerics/Functions/TabularFunction.cs +++ b/Numerics/Functions/TabularFunction.cs @@ -48,6 +48,20 @@ public TabularFunction(UncertainOrderedPairedData pairedData) /// public Transform YTransform { get; set; } = Transform.None; + /// + /// The extrapolation policy applied to out-of-range lookups. Default = None, which + /// reproduces the historical endpoint hold exactly. + /// + /// + /// One policy governs both lookup directions of the same extended boundary segments: + /// extends on the x-axis sides, and + /// extends on the y-lookup sides. Extension is + /// linear in the configured transform space (see + /// ), + /// and the floor still binds on extended forward lookups. + /// + public ExtrapolationSides Extrapolation { get; set; } = ExtrapolationSides.None; + /// public int NumberOfParameters => 1; @@ -139,7 +153,7 @@ public double Function(double x) { // Validate parameters if (ParametersValid == false) ValidateParameters(new double[] {0}, true); - double y = opd.GetYFromX(x, XTransform, YTransform); + double y = opd.GetYFromX(x, XTransform, YTransform, Extrapolation); y = AllowNegativeYValues == false && (double.IsNaN(y) || y < 0) ? 0 : y; return y; } @@ -150,7 +164,7 @@ public double InverseFunction(double y) // Validate parameters if (ParametersValid == false) ValidateParameters(new double[] { 0 }, true); y = AllowNegativeYValues == false && (double.IsNaN(y) || y < 0) ? 0 : y; - return opd.GetXFromY(y, XTransform, YTransform); + return opd.GetXFromY(y, XTransform, YTransform, Extrapolation); } /// @@ -165,6 +179,12 @@ public XElement ToXElement() var result = new XElement(nameof(TabularFunction)); result.SetAttributeValue(nameof(XTransform), XTransform.ToString()); result.SetAttributeValue(nameof(YTransform), YTransform.ToString()); + // Conditional presence: the attribute is written only when non-default so that every + // pre-existing serialized form remains byte-identical. + if (Extrapolation != ExtrapolationSides.None) + { + result.SetAttributeValue(nameof(Extrapolation), Extrapolation.ToString()); + } result.SetAttributeValue(nameof(AllowNegativeYValues), AllowNegativeYValues.ToString()); result.SetAttributeValue(nameof(Minimum), Minimum.ToString("G17", CultureInfo.InvariantCulture)); result.SetAttributeValue(nameof(Maximum), Maximum.ToString("G17", CultureInfo.InvariantCulture)); @@ -191,6 +211,8 @@ public static TabularFunction FromXElement(XElement xElement) function.XTransform = xTransform; if (Enum.TryParse(xElement.Attribute(nameof(YTransform))?.Value, out Transform yTransform)) function.YTransform = yTransform; + if (Enum.TryParse(xElement.Attribute(nameof(Extrapolation))?.Value, out ExtrapolationSides extrapolation)) + function.Extrapolation = extrapolation; if (bool.TryParse(xElement.Attribute(nameof(AllowNegativeYValues))?.Value, out bool allowNegative)) function.AllowNegativeYValues = allowNegative; if (double.TryParse(xElement.Attribute(nameof(Minimum))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double minimum)) diff --git a/Test_Numerics/Distributions/Univariate/Test_CompetingRisks.cs b/Test_Numerics/Distributions/Univariate/Test_CompetingRisks.cs index 14cb1bad..fe86d4f4 100644 --- a/Test_Numerics/Distributions/Univariate/Test_CompetingRisks.cs +++ b/Test_Numerics/Distributions/Univariate/Test_CompetingRisks.cs @@ -1241,5 +1241,32 @@ private static void AssertEmpiricalInverseMatchesRootSolve(CompetingRisks compet } #endregion + + /// + /// The empirical machinery under the hood is unchanged by the extrapolation property: + /// the cumulative incidence functions are built at the default policy, the empirical CDF + /// keeps its default far-tail endpoint hold, and no extrapolation attribute appears in + /// the serialized form. + /// + [TestMethod] + public void Test_CompetingRisks_EmpiricalUnderTheHood_NoRegression() + { + var cr = new CompetingRisks(new UnivariateDistributionBase[] { new Normal(10, 2), new Normal(12, 3) }); + var cifs = cr.CumulativeIncidenceFunctions(); + for (int i = 0; i < cifs.Count; i++) + { + Assert.AreEqual(ExtrapolationSides.None, cifs[i].Extrapolation); + } + + cr.CreateEmpiricalCDF(); + // 1 - 1E-17 rounds to exactly 1.0, which takes the container's own support guard — + // so the far-tail hold is probed at the guard floor and the last double below one. + double low = cr.InverseCDF(1E-17); + double high = cr.InverseCDF(1d - 1E-16); + Assert.IsFalse(double.IsNaN(low) || double.IsInfinity(low)); + Assert.IsFalse(double.IsNaN(high) || double.IsInfinity(high)); + Assert.AreEqual(low, cr.InverseCDF(1E-16), 0d); + Assert.DoesNotContain("Extrapolation", cr.ToXElement().ToString()); + } } } diff --git a/Test_Numerics/Distributions/Univariate/Test_EmpiricalDistribution.cs b/Test_Numerics/Distributions/Univariate/Test_EmpiricalDistribution.cs index 01a27277..c34f3b08 100644 --- a/Test_Numerics/Distributions/Univariate/Test_EmpiricalDistribution.cs +++ b/Test_Numerics/Distributions/Univariate/Test_EmpiricalDistribution.cs @@ -461,5 +461,204 @@ public void Test_NonFiniteProbability_ThrowsWhenUsed() Assert.Throws(() => distribution.CDF(125d)); } + + /// + /// The extrapolation default (None) must reproduce the historical endpoint hold exactly — + /// including the far-tail inverse guards — so every existing model is bit-identical. This + /// is the bit-identity gate for the distribution's extrapolation property. + /// + [TestMethod] + public void Test_Empirical_Extrapolation_DefaultNone_MatchesEndpointHold() + { + var dist = new EmpiricalDistribution(new[] { 100d, 200d, 300d }, new[] { 0.1d, 0.5d, 0.9d }); + Assert.AreEqual(ExtrapolationSides.None, dist.Extrapolation); + + Assert.AreEqual(0.1d, dist.CDF(50d)); + Assert.AreEqual(0.9d, dist.CDF(400d)); + Assert.AreEqual(100d, dist.InverseCDF(0.01d)); + Assert.AreEqual(300d, dist.InverseCDF(0.99d)); + Assert.AreEqual(100d, dist.InverseCDF(0d)); + Assert.AreEqual(300d, dist.InverseCDF(1d)); + Assert.AreEqual(100d, dist.InverseCDF(1E-17)); + Assert.AreEqual(300d, dist.InverseCDF(1d - 1E-17)); + Assert.AreEqual(0d, dist.PDF(50d)); + Assert.AreEqual(0d, dist.PDF(400d)); + } + + /// + /// Sided extension on the default normal-Z probability axis, hand-computed from the + /// boundary segments in z-space, both directions, including the one-sided holds. The + /// extrapolation sides are defined in X space. + /// + [TestMethod] + public void Test_Empirical_Extrapolation_SidedExtension_NormalZ() + { + var dist = new EmpiricalDistribution(new[] { 100d, 200d, 300d }, new[] { 0.1d, 0.5d, 0.9d }) + { + Extrapolation = ExtrapolationSides.Both + }; + double z01 = Normal.StandardZ(0.1); + double z05 = Normal.StandardZ(0.5); + double z09 = Normal.StandardZ(0.9); + + // CDF extends the boundary segments linearly in z and back-transforms into (0, 1). + double validLow = Normal.StandardCDF(z01 + (z05 - z01) / 100d * (50d - 100d)); + double validHigh = Normal.StandardCDF(z09 + (z09 - z05) / 100d * (400d - 300d)); + Assert.AreEqual(validLow, dist.CDF(50d), 1E-12); + Assert.AreEqual(validHigh, dist.CDF(400d), 1E-12); + Assert.IsTrue(dist.CDF(50d) > 0d && dist.CDF(400d) < 1d); + + // InverseCDF extends the same segments beyond the table span. + double validXLow = 100d + (200d - 100d) / (z05 - z01) * (Normal.StandardZ(0.01) - z01); + double validXHigh = 200d + (300d - 200d) / (z09 - z05) * (Normal.StandardZ(0.99) - z05); + Assert.AreEqual(validXLow, dist.InverseCDF(0.01d), 1E-9); + Assert.AreEqual(validXHigh, dist.InverseCDF(0.99d), 1E-9); + + // The extended pair stays consistent. + Assert.AreEqual(0.01d, dist.CDF(dist.InverseCDF(0.01d)), 1E-10); + Assert.AreEqual(0.99d, dist.CDF(dist.InverseCDF(0.99d)), 1E-10); + + // One-sided policies hold the other side, in X space. + var below = new EmpiricalDistribution(new[] { 100d, 200d, 300d }, new[] { 0.1d, 0.5d, 0.9d }) + { + Extrapolation = ExtrapolationSides.Below + }; + Assert.AreEqual(validLow, below.CDF(50d), 1E-12); + Assert.AreEqual(0.9d, below.CDF(400d)); + Assert.AreEqual(validXLow, below.InverseCDF(0.01d), 1E-9); + Assert.AreEqual(300d, below.InverseCDF(0.99d)); + + var above = new EmpiricalDistribution(new[] { 100d, 200d, 300d }, new[] { 0.1d, 0.5d, 0.9d }) + { + Extrapolation = ExtrapolationSides.Above + }; + Assert.AreEqual(0.1d, above.CDF(50d)); + Assert.AreEqual(validHigh, above.CDF(400d), 1E-12); + Assert.AreEqual(100d, above.InverseCDF(0.01d)); + Assert.AreEqual(validXHigh, above.InverseCDF(0.99d), 1E-9); + } + + /// + /// A probability axis under a None transform extends linearly and can leave the unit + /// interval; the CDF must clamp its output to [0, 1] under extrapolation. + /// + [TestMethod] + public void Test_Empirical_Extrapolation_CdfClamp_LinearProbabilityAxis() + { + var dist = new EmpiricalDistribution(new[] { 0d, 1d }, new[] { 0.2d, 0.8d }) + { + ProbabilityTransform = Transform.None, + Extrapolation = ExtrapolationSides.Both + }; + Assert.AreEqual(1d, dist.CDF(2d)); + Assert.AreEqual(0d, dist.CDF(-1d)); + Assert.AreEqual(0.5d, dist.CDF(0.5d), 1E-12); + } + + /// + /// A survival-form table (descending stored probabilities) must extend the same X sides + /// as its ascending twin: the complemented CDF branch and the X-side to lookup-side + /// mapping in the inverse are both exercised. + /// + [TestMethod] + public void Test_Empirical_Extrapolation_DescendingOrientation_MatchesAscending() + { + var ascending = new EmpiricalDistribution(new[] { 100d, 200d, 300d }, new[] { 0.1d, 0.5d, 0.9d }) + { + Extrapolation = ExtrapolationSides.Both + }; + var survival = new EmpiricalDistribution( + new[] { 100d, 200d, 300d }, + new[] { 0.9d, 0.5d, 0.1d }, + SortOrder.Ascending, + SortOrder.Descending) + { + Extrapolation = ExtrapolationSides.Both + }; + + // Same distribution, either storage orientation: extended values agree. + Assert.AreEqual(ascending.CDF(50d), survival.CDF(50d), 1E-12); + Assert.AreEqual(ascending.CDF(400d), survival.CDF(400d), 1E-12); + Assert.AreEqual(ascending.InverseCDF(0.01d), survival.InverseCDF(0.01d), 1E-8); + Assert.AreEqual(ascending.InverseCDF(0.99d), survival.InverseCDF(0.99d), 1E-8); + + // One-sided X-space semantics on the survival form: Below extends only the low-X end. + var survivalBelow = new EmpiricalDistribution( + new[] { 100d, 200d, 300d }, + new[] { 0.9d, 0.5d, 0.1d }, + SortOrder.Ascending, + SortOrder.Descending) + { + Extrapolation = ExtrapolationSides.Below + }; + Assert.IsLessThan(100d, survivalBelow.InverseCDF(0.01d)); + Assert.AreEqual(300d, survivalBelow.InverseCDF(0.99d)); + Assert.IsLessThan(0.1d, survivalBelow.CDF(50d)); + Assert.AreEqual(0.9d, survivalBelow.CDF(400d)); + } + + /// + /// The far-tail inverse guards stay total under extrapolation — probabilities at or + /// beyond the 1e-16 floors evaluate the extended lookup at the floor instead of holding + /// the endpoint — and the inverse stays monotone across both boundaries. + /// + [TestMethod] + public void Test_Empirical_Extrapolation_InverseMonotoneAndGuards() + { + var dist = new EmpiricalDistribution(new[] { 100d, 200d, 300d }, new[] { 0.1d, 0.5d, 0.9d }) + { + Extrapolation = ExtrapolationSides.Both + }; + + double x0 = dist.InverseCDF(0d); + double x1 = dist.InverseCDF(1d); + Assert.IsTrue(Tools.IsFinite(x0) && x0 < 100d); + Assert.IsTrue(Tools.IsFinite(x1) && x1 > 300d); + Assert.AreEqual(dist.InverseCDF(1E-17), x0, 0d); + Assert.AreEqual(dist.InverseCDF(1d - 1E-17), x1, 0d); + + var grid = new[] { 0d, 1E-16, 1E-10, 1E-4, 0.01d, 0.1d, 0.3d, 0.5d, 0.7d, 0.9d, 0.99d, 1d - 1E-4, 1d - 1E-10, 1d - 1E-16, 1d }; + for (int i = 1; i < grid.Length; i++) + { + Assert.IsGreaterThanOrEqualTo(dist.InverseCDF(grid[i - 1]), dist.InverseCDF(grid[i]), + $"InverseCDF must be monotone across the extended tail at u = {grid[i]}."); + } + } + + /// + /// The policy survives Clone, serializes only when non-default (conditional presence), + /// restores by name, defaults when absent, and rejects an unparseable value. + /// + [TestMethod] + public void Test_Empirical_Extrapolation_CloneAndSerialization() + { + var dist = new EmpiricalDistribution(new[] { 100d, 200d, 300d }, new[] { 0.1d, 0.5d, 0.9d }); + + // Default: no attribute, and set-then-clear restores the exact serialized form. + var defaultXml = dist.ToXElement(); + Assert.IsNull(defaultXml.Attribute(nameof(EmpiricalDistribution.Extrapolation))); + string baseline = defaultXml.ToString(); + dist.Extrapolation = ExtrapolationSides.Both; + dist.Extrapolation = ExtrapolationSides.None; + Assert.AreEqual(baseline, dist.ToXElement().ToString()); + + // Clone carries the policy. + dist.Extrapolation = ExtrapolationSides.Both; + var clone = (EmpiricalDistribution)dist.Clone(); + Assert.AreEqual(ExtrapolationSides.Both, clone.Extrapolation); + + // Non-default round-trips by enum name. + var xml = dist.ToXElement(); + Assert.AreEqual(nameof(ExtrapolationSides.Both), xml.Attribute(nameof(EmpiricalDistribution.Extrapolation))?.Value); + var restored = EmpiricalDistribution.FromXElement(xml); + Assert.AreEqual(ExtrapolationSides.Both, restored.Extrapolation); + Assert.AreEqual(dist.InverseCDF(0.01d), restored.InverseCDF(0.01d), 1E-12); + + // Absent attribute reads as the default; an invalid value throws (the strict pattern). + Assert.AreEqual(ExtrapolationSides.None, EmpiricalDistribution.FromXElement(defaultXml).Extrapolation); + var invalid = dist.ToXElement(); + invalid.SetAttributeValue(nameof(EmpiricalDistribution.Extrapolation), "Sideways"); + Assert.Throws(() => EmpiricalDistribution.FromXElement(invalid)); + } } } diff --git a/Test_Numerics/Distributions/Univariate/Test_KernelDensity.cs b/Test_Numerics/Distributions/Univariate/Test_KernelDensity.cs index f10f5b6f..f6b8e449 100644 --- a/Test_Numerics/Distributions/Univariate/Test_KernelDensity.cs +++ b/Test_Numerics/Distributions/Univariate/Test_KernelDensity.cs @@ -221,6 +221,21 @@ public void ModeAndCDF_AreBitReproducibleAcrossInstances() } } + /// + /// Kernel density does not compose the empirical distribution — it carries its own + /// lookup table with hard support gates — so the empirical extrapolation property cannot + /// reach it: the gates and the serialized form are unchanged. + /// + [TestMethod] + public void Test_KernelDensity_EmpiricalUnderTheHood_NoRegression() + { + var sample = new double[] { 8d, 9d, 10d, 11d, 12d, 10.5d, 9.5d, 10.2d }; + var kde = new KernelDensity(sample); + Assert.AreEqual(0d, kde.CDF(kde.Minimum - 1d)); + Assert.AreEqual(1d, kde.CDF(kde.Maximum + 1d)); + Assert.DoesNotContain("Extrapolation", kde.ToXElement().ToString()); + } + diff --git a/Test_Numerics/Distributions/Univariate/Test_Mixture.cs b/Test_Numerics/Distributions/Univariate/Test_Mixture.cs index fe61d5bd..7edc04ab 100644 --- a/Test_Numerics/Distributions/Univariate/Test_Mixture.cs +++ b/Test_Numerics/Distributions/Univariate/Test_Mixture.cs @@ -3,6 +3,7 @@ using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics; +using Numerics.Data; using Numerics.Distributions; namespace Distributions.Univariate @@ -563,5 +564,35 @@ public void Test_Mixture_EM_ImpossibleRowThrowsWithRowContext() StringAssert.Contains(exception.Message, "zero or nonfinite"); } + /// + /// The empirical machinery under the hood is unchanged by the extrapolation property: + /// the internal empirical CDF keeps its default far-tail endpoint hold, no extrapolation + /// attribute appears in the serialized form, and an extension-enabled empirical child + /// governs itself through the mixture's direct CDF. + /// + [TestMethod] + public void Test_Mixture_EmpiricalUnderTheHood_NoRegression() + { + var mixture = new Mixture(new[] { 0.4d, 0.6d }, new UnivariateDistributionBase[] { new Normal(10, 2), new Normal(20, 3) }); + mixture.CreateEmpiricalCDF(); + // 1 - 1E-17 rounds to exactly 1.0, which takes the container's own support guard — + // so the far-tail hold is probed at the guard floor and the last double below one. + double low = mixture.InverseCDF(1E-17); + double high = mixture.InverseCDF(1d - 1E-16); + Assert.IsFalse(double.IsNaN(low) || double.IsInfinity(low)); + Assert.IsFalse(double.IsNaN(high) || double.IsInfinity(high)); + Assert.AreEqual(low, mixture.InverseCDF(1E-16), 0d); + Assert.DoesNotContain("Extrapolation", mixture.ToXElement().ToString()); + + // A policy-carrying empirical child composes through the mixture unchanged. + var child = new EmpiricalDistribution(new[] { 100d, 200d, 300d }, new[] { 0.1d, 0.5d, 0.9d }) + { + Extrapolation = ExtrapolationSides.Both + }; + var single = new Mixture(new[] { 1d }, new UnivariateDistributionBase[] { child }); + Assert.AreEqual(child.CDF(400d), single.CDF(400d), 1E-12); + Assert.IsLessThan(1d, single.CDF(400d)); + } + } } diff --git a/Test_Numerics/Functions/Test_Functions.cs b/Test_Numerics/Functions/Test_Functions.cs index 99c2f4b7..924102ca 100644 --- a/Test_Numerics/Functions/Test_Functions.cs +++ b/Test_Numerics/Functions/Test_Functions.cs @@ -223,5 +223,87 @@ public void Test_Tabular_Function() Assert.AreEqual(75, X4, 1E-6); } + /// + /// The extrapolation default (None) must reproduce the historical endpoint hold exactly, + /// in every transform space, for both lookup directions. This is the bit-identity gate for + /// the tabular function's extrapolation property. + /// + [TestMethod] + public void Test_Tabular_Extrapolation_DefaultNone_MatchesEndpointHold() + { + var XArray = new double[] { 50, 100, 150, 200, 250 }; + var YArray = new UnivariateDistributionBase[] { new Deterministic(100), new Deterministic(200), new Deterministic(300), new Deterministic(400), new Deterministic(500) }; + var opd = new UncertainOrderedPairedData(XArray, YArray, true, SortOrder.Ascending, true, SortOrder.Ascending, UnivariateDistributionType.Deterministic); + + var func = new TabularFunction(opd); + Assert.AreEqual(ExtrapolationSides.None, func.Extrapolation); + + // Out-of-range lookups hold the endpoint ordinates, both directions. + Assert.AreEqual(100.0, func.Function(10.0)); + Assert.AreEqual(500.0, func.Function(1000.0)); + Assert.AreEqual(50.0, func.InverseFunction(10.0)); + Assert.AreEqual(250.0, func.InverseFunction(1000.0)); + + // Same hold under a logarithmic lookup axis. + var logFunc = new TabularFunction(opd) { XTransform = Transform.Logarithmic }; + Assert.AreEqual(100.0, logFunc.Function(10.0)); + Assert.AreEqual(500.0, logFunc.Function(1000.0)); + + // Exact endpoints are returned unchanged even when extrapolation is enabled. + var bothFunc = new TabularFunction(opd) { Extrapolation = ExtrapolationSides.Both }; + Assert.AreEqual(100.0, bothFunc.Function(50.0)); + Assert.AreEqual(500.0, bothFunc.Function(250.0)); + } + + /// + /// Sided extension of the boundary segments, hand-computed in each transform space, for + /// both lookup directions, including the one-sided holds and the negative-Y floor. + /// + [TestMethod] + public void Test_Tabular_Extrapolation_SidedExtension() + { + var XArray = new double[] { 50, 100, 150, 200, 250 }; + var YArray = new UnivariateDistributionBase[] { new Deterministic(100), new Deterministic(200), new Deterministic(300), new Deterministic(400), new Deterministic(500) }; + var opd = new UncertainOrderedPairedData(XArray, YArray, true, SortOrder.Ascending, true, SortOrder.Ascending, UnivariateDistributionType.Deterministic); + + // Linear space: both boundary segments have slope 2. + var both = new TabularFunction(opd) { Extrapolation = ExtrapolationSides.Both }; + Assert.AreEqual(50.0, both.Function(25.0), 1E-10); + Assert.AreEqual(600.0, both.Function(300.0), 1E-10); + Assert.AreEqual(25.0, both.InverseFunction(50.0), 1E-10); + Assert.AreEqual(300.0, both.InverseFunction(600.0), 1E-10); + + // One-sided policies hold the other side. + var below = new TabularFunction(opd) { Extrapolation = ExtrapolationSides.Below }; + Assert.AreEqual(50.0, below.Function(25.0), 1E-10); + Assert.AreEqual(500.0, below.Function(300.0)); + var above = new TabularFunction(opd) { Extrapolation = ExtrapolationSides.Above }; + Assert.AreEqual(100.0, above.Function(25.0)); + Assert.AreEqual(600.0, above.Function(300.0), 1E-10); + + // Logarithmic lookup axis: extension is linear in (ln x, y). + var logBoth = new TabularFunction(opd) { XTransform = Transform.Logarithmic, Extrapolation = ExtrapolationSides.Both }; + double validBelow = 100 + (200 - 100) / (Math.Log(100) - Math.Log(50)) * (Math.Log(25) - Math.Log(50)); + double validAbove = 400 + (500 - 400) / (Math.Log(250) - Math.Log(200)) * (Math.Log(500) - Math.Log(200)); + Assert.AreEqual(validBelow, logBoth.Function(25.0), 1E-10); + Assert.AreEqual(validAbove, logBoth.Function(500.0), 1E-10); + + // Normal-Z ordinate axis: extension is linear in z and back-transforms into (0, 1). + var pArray = new double[] { 1, 2, 3 }; + var pYArray = new UnivariateDistributionBase[] { new Deterministic(0.2), new Deterministic(0.5), new Deterministic(0.8) }; + var pOpd = new UncertainOrderedPairedData(pArray, pYArray, true, SortOrder.Ascending, true, SortOrder.Ascending, UnivariateDistributionType.Deterministic); + var zFunc = new TabularFunction(pOpd) { YTransform = Transform.NormalZ, Extrapolation = ExtrapolationSides.Both }; + double z02 = Normal.StandardZ(0.2); + double z05 = Normal.StandardZ(0.5); + double validZ = Normal.StandardCDF(z02 + (z05 - z02) / (2.0 - 1.0) * (0.0 - 1.0)); + double yZ = zFunc.Function(0.0); + Assert.AreEqual(validZ, yZ, 1E-12); + Assert.IsTrue(yZ > 0.0 && yZ < 1.0); + + // The negative-Y floor still binds under extension. + var floored = new TabularFunction(opd) { Extrapolation = ExtrapolationSides.Both, AllowNegativeYValues = false }; + Assert.AreEqual(0.0, floored.Function(-25.0)); + } + } } diff --git a/Test_Numerics/Functions/Test_UnivariateFunctionFactory.cs b/Test_Numerics/Functions/Test_UnivariateFunctionFactory.cs index 6ac1ff8e..9f601a79 100644 --- a/Test_Numerics/Functions/Test_UnivariateFunctionFactory.cs +++ b/Test_Numerics/Functions/Test_UnivariateFunctionFactory.cs @@ -138,6 +138,39 @@ public void Test_TabularFunction_RoundTrip() Assert.Throws(() => TabularFunction.FromXElement(new XElement(nameof(TabularFunction)))); } + /// + /// The extrapolation attribute is written only when non-default, so every pre-existing + /// serialized form stays byte-identical; a non-default value round-trips, and an absent + /// attribute reads as None. + /// + [TestMethod] + public void Test_TabularFunction_Extrapolation_ConditionalPresence() + { + var table = new UncertainOrderedPairedData( + new[] { new UncertainOrdinate(1d, new Normal(10, 2)), new UncertainOrdinate(100d, new Normal(20, 2)) }, + true, SortOrder.Ascending, false, SortOrder.None, UnivariateDistributionType.Normal); + + // Default: no attribute, and the element is byte-identical to a pre-property form. + var original = new TabularFunction(table); + var defaultXml = original.ToXElement(); + Assert.IsNull(defaultXml.Attribute(nameof(TabularFunction.Extrapolation))); + string baseline = defaultXml.ToString(); + original.Extrapolation = ExtrapolationSides.Both; + original.Extrapolation = ExtrapolationSides.None; + Assert.AreEqual(baseline, original.ToXElement().ToString()); + + // Non-default: attribute present by enum name and restored through the factory. + original.Extrapolation = ExtrapolationSides.Above; + var xml = original.ToXElement(); + Assert.AreEqual(nameof(ExtrapolationSides.Above), xml.Attribute(nameof(TabularFunction.Extrapolation))?.Value); + var restored = (TabularFunction)UnivariateFunctionFactory.CreateFromXElement(xml); + Assert.AreEqual(ExtrapolationSides.Above, restored.Extrapolation); + + // Absent attribute reads as the default. + var legacy = TabularFunction.FromXElement(defaultXml); + Assert.AreEqual(ExtrapolationSides.None, legacy.Extrapolation); + } + /// /// Test that the factory rejects null and unknown serialized forms. /// From 6e0b11cd90bf5a1f215e38fb8b67ff61fcdbe6d7 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 28 Aug 2026 08:41:45 -0600 Subject: [PATCH 143/222] Record trailing tie runs in RanksInPlace and make ParallelMean sequential A tie run reaching the final sorted element never closed inside the loop, so its length was silently dropped from the ties output; the closing RanksTies call now records it. ParallelMean delegates to the sequential Mean: the PLINQ partition order depended on the processor count, so the same data produced different last bits on different machines, and the method has no production callers that need parallel summation. The doc example counted tied groups with t > 1, which missed every two-element group since each entry stores run length minus one. --- Numerics/Data/Statistics/Statistics.cs | 30 +++++++--- .../Data/Statistics/Test_Statistics.cs | 55 +++++++++++++++++++ docs/statistics/descriptive.md | 6 +- 3 files changed, 82 insertions(+), 9 deletions(-) diff --git a/Numerics/Data/Statistics/Statistics.cs b/Numerics/Data/Statistics/Statistics.cs index 35b3c4c1..b09573ea 100644 --- a/Numerics/Data/Statistics/Statistics.cs +++ b/Numerics/Data/Statistics/Statistics.cs @@ -136,16 +136,22 @@ public static double Mean(IList data) } /// - /// Computes the arithmetic sample mean from the unsorted data array by first enabling parallelization of the array. + /// Computes the arithmetic sample mean from the unsorted data array. /// Returns NaN if data is empty or any entry is NaN. /// /// Sample of data, no sorting is assumed. + /// + /// This method delegates to and is retained for API + /// compatibility. The former PLINQ implementation summed per-partition and combined the + /// partial sums, so the result depended on the partition count the runtime chose from the + /// processor count; because floating-point addition is not associative, the same data could + /// produce different last bits on different machines. A sequential sum is bit-reproducible + /// everywhere, and the arrays this method sees are far too small for parallel summation to + /// pay for its overhead. + /// public static double ParallelMean(IList data) { - if (data.Count == 0) return double.NaN; - - double sum = data.AsParallel().Sum(); - return sum / data.Count; + return Mean(data); } /// @@ -730,7 +736,10 @@ public static double[] RanksInPlace(double[] data) /// Returns the rank of each entry of the unsorted data array. /// /// The array of sample of data, no sorting is assumed. - /// Output. The number of ties in the data. + /// Output. A sparse array of tie-run lengths: the entry at a tie run's last + /// position in the sorted order holds the run length minus one, and every other entry is zero. + /// A group of k equal values therefore reports k - 1, so the number of tied groups is the count + /// of entries greater than zero, not greater than one. public static double[] RanksInPlace(double[] data, out double [] ties) { if (data == null) throw new ArgumentNullException(nameof(data)); @@ -760,7 +769,7 @@ public static double[] RanksInPlace(double[] data, out double [] ties) if (i == previousIndex + 1) { ranks[index[previousIndex]] = i; - t = 0; + t = 0; } else { @@ -773,6 +782,13 @@ public static double[] RanksInPlace(double[] data, out double [] ties) } RanksTies(ranks, index, previousIndex, work.Length); + // The loop records a run's length only when the run closes at a later, distinct value, so a + // tie run containing the largest values never closes inside the loop and its length would be + // silently dropped without this trailing write. + if (t > 0) + { + ties[work.Length - 1] = t; + } return ranks; } diff --git a/Test_Numerics/Data/Statistics/Test_Statistics.cs b/Test_Numerics/Data/Statistics/Test_Statistics.cs index e66efb17..f1c3538b 100644 --- a/Test_Numerics/Data/Statistics/Test_Statistics.cs +++ b/Test_Numerics/Data/Statistics/Test_Statistics.cs @@ -83,6 +83,31 @@ public void Test_ParallelMean() Assert.AreEqual(regMean, test, 1E-6); } + /// + /// Verify that ParallelMean is bitwise identical to the sequential Mean. + /// + /// + /// The former PLINQ implementation combined per-partition sums in a machine-dependent + /// order, so its last bits varied with the processor count (measured up to 29 ULP from the + /// sequential sum at n = 100,000). The method now delegates to the sequential mean, so the + /// two must agree exactly on a magnitude-spanning sample, not merely to a tolerance. + /// + [TestMethod] + public void Test_ParallelMean_MatchesSequentialMeanExactly() + { + var data = new double[1000]; + for (int i = 0; i < data.Length; i++) + { + // Deterministic values spanning several orders of magnitude so any + // reassociation of the summation order would change the last bits. + data[i] = Math.Pow(10d, (i % 7) - 3) * (1d + i / 997d); + } + + double parallel = Numerics.Data.Statistics.Statistics.ParallelMean(data); + double sequential = Numerics.Data.Statistics.Statistics.Mean(data); + Assert.AreEqual(sequential, parallel, 0d); + } + /// /// Test the GeometricMean method against R's "geometric.mean()" method from the "psych" package. /// @@ -661,6 +686,36 @@ public void Test_RanksInPlace_Ties() } } + /// + /// Verify that a tie run reaching the final sorted element records its length in the ties array. + /// + /// + /// The tie-length write used to happen only when a run closed at a later, distinct value inside + /// the loop, so a run containing the largest values never closed and its length was silently + /// dropped: for this fixture the buggy code returned ties[6] = 0 instead of 2, while the rank + /// averaging itself was already correct. Hand-computed oracle: sorted data are + /// {1, 2, 3, 3, 5, 5, 5}; the {3, 3} run closes at sorted position 3 with length - 1 = 1, and + /// the trailing {5, 5, 5} run ends at sorted position 6 with length - 1 = 2. + /// + [TestMethod] + public void Test_RanksInPlace_Ties_TrailingRunIsRecorded() + { + var data = new double[] { 1.0, 3.0, 3.0, 2.0, 5.0, 5.0, 5.0 }; + var ranks = Numerics.Data.Statistics.Statistics.RanksInPlace(data, out var ties); + + var validRanks = new double[] { 1.0, 3.5, 3.5, 2.0, 6.0, 6.0, 6.0 }; + for (int i = 0; i < validRanks.Length; i++) + { + Assert.AreEqual(validRanks[i], ranks[i]); + } + + var validTies = new double[] { 0, 0, 0, 1, 0, 0, 2 }; + for (int i = 0; i < validTies.Length; i++) + { + Assert.AreEqual(validTies[i], ties[i]); + } + } + /// /// Test the Entropy function against the value derived from the direct function: sum of p*ln(p) /// diff --git a/docs/statistics/descriptive.md b/docs/statistics/descriptive.md index 1031fd77..d9faa9f8 100644 --- a/docs/statistics/descriptive.md +++ b/docs/statistics/descriptive.md @@ -472,11 +472,13 @@ for (int i = 0; i < data.Length; i++) Console.WriteLine($"{data[i],5:F1} | {ranks[i],4:F1}"); } -// Ranks with ties reported +// Ranks with ties reported. Each entry of "ties" holds a tie run's length minus one at the +// run's last sorted position (zero elsewhere), so a tied group of any size reports a value +// greater than zero. double[] dataCopy2 = (double[])data.Clone(); double[] ranks2 = Statistics.RanksInPlace(dataCopy2, out double[] ties); -Console.WriteLine($"\nNumber of tied groups: {ties.Count(t => t > 1)}"); +Console.WriteLine($"\nNumber of tied groups: {ties.Count(t => t > 0)}"); ``` ## Entropy From 21e63db5bbd52396d21dae58cbe1e9482467b606 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 28 Aug 2026 08:41:46 -0600 Subject: [PATCH 144/222] Align UncertainOrdinate X equality with Ordinate and document the probe asymmetry The equality operator compared X with exact inequality while Ordinate allows a machine-epsilon slack and treats a NaN coordinate as equal, so the two classes disagreed on the same conceptual coordinate. The mean-vs-median central-probe asymmetry between OrdinateValid and OrdinateErrors is documented as deliberate: the median is always bracketed by the percentile probes, while the mean of a skewed distribution need not be, so the validity test is intentionally stricter. --- .../Data/Paired Data/UncertainOrdinate.cs | 24 ++++++++++++-- .../Paired Data/Test_UncertainOrdinate.cs | 31 +++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/Numerics/Data/Paired Data/UncertainOrdinate.cs b/Numerics/Data/Paired Data/UncertainOrdinate.cs index 89d6c9ad..c8cddd83 100644 --- a/Numerics/Data/Paired Data/UncertainOrdinate.cs +++ b/Numerics/Data/Paired Data/UncertainOrdinate.cs @@ -150,6 +150,16 @@ public Ordinate GetOrdinate() /// Boolean identifying if the ordinate to compare is the next or previous ordinate in a series. /// Allow different distribution types. Default = false. /// A boolean indicating if the ordinate is valid or not given the criteria. + /// + /// The central-tendency probe here is the MEAN (), while the + /// companion + /// probes the MEDIAN. The asymmetry is deliberate: the median always lies between the two + /// percentile probes tested above and below it, but for a skewed Y distribution the mean can + /// fall outside that interval, so probing the mean here makes the validity test strictly more + /// demanding. The observable consequence is that a skewed pair can report invalid while the + /// median-based error probe returns no matching message. Do not align the two probes without + /// re-deriving the monotonicity guarantees for skewed distributions. + /// public bool OrdinateValid(UncertainOrdinate ordinateToCompare, bool strictX, bool strictY, SortOrder xOrder, SortOrder yOrder, bool compareOrdinateIsNext, bool allowDifferentTypes = false) { // @@ -190,6 +200,13 @@ public bool OrdinateValid(UncertainOrdinate ordinateToCompare, bool strictX, boo /// Boolean identifying if the ordinate to compare is the next or previous ordinate in a series. /// Allow different distribution types. Default = false. /// A list of error messages given the criteria. + /// + /// The central-tendency probe here is the MEDIAN (GetOrdinate(0.5)), while + /// + /// probes the MEAN. See the remarks on that method for why the asymmetry is deliberate: the + /// median is always bracketed by the percentile probes, whereas the mean of a skewed Y + /// distribution need not be, so the validity test is intentionally the stricter of the two. + /// public List OrdinateErrors(UncertainOrdinate ordinateToCompare, bool strictX, bool strictY, SortOrder xOrder, SortOrder yOrder, bool compareOrdinateIsNext, bool allowDifferentTypes = false) { var result = new List(); @@ -265,8 +282,11 @@ public List OrdinateErrors() /// True if two objects are numerically equal; otherwise, False. public static bool operator ==(UncertainOrdinate left, UncertainOrdinate right) { - //if (left == null || right == null) return false; - if (left.X != right.X) + // Match Ordinate's equality convention for the shared X coordinate: allow a machine-epsilon + // slack, and (as Ordinate documents for its own operator) a NaN coordinate compares equal + // because the rejection test below is false for NaN. The former exact inequality made the + // two classes disagree on the same conceptual coordinate. + if (Math.Abs(left.X - right.X) > Tools.DoubleMachineEpsilon) return false; if (left.Y is null && right.Y is null) return true; diff --git a/Test_Numerics/Data/Paired Data/Test_UncertainOrdinate.cs b/Test_Numerics/Data/Paired Data/Test_UncertainOrdinate.cs index 1fe04289..ceaf303a 100644 --- a/Test_Numerics/Data/Paired Data/Test_UncertainOrdinate.cs +++ b/Test_Numerics/Data/Paired Data/Test_UncertainOrdinate.cs @@ -201,5 +201,36 @@ public void Test_ToXElement() Assert.AreEqual(X, x); Assert.IsTrue(distribution == dist); } + + /// + /// Verify the equality operator compares X with the same machine-epsilon tolerance as Ordinate. + /// + /// + /// The operator used to compare X with exact inequality while Ordinate's operator allows a + /// DoubleMachineEpsilon slack and (per its own documented convention) treats a NaN coordinate + /// as equal to anything. Before the alignment, an X pair differing by exactly one machine + /// epsilon compared unequal here and equal on Ordinate, and a NaN X compared unequal to + /// everything. Both classes now share one convention for the same conceptual X coordinate. + /// + [TestMethod] + public void Test_EqualityOperator_XComparisonMatchesOrdinateConvention() + { + var distribution = new Normal(10, 2); + + // A difference of exactly DoubleMachineEpsilon is within Ordinate's tolerance + // (its test rejects only strictly greater differences). + var left = new UncertainOrdinate(0d, distribution); + var right = new UncertainOrdinate(Numerics.Tools.DoubleMachineEpsilon, distribution); + Assert.IsTrue(left == right); + Assert.IsFalse(left != right); + + // A difference clearly above the tolerance still compares unequal. + var far = new UncertainOrdinate(1d, distribution); + Assert.IsFalse(left == far); + + // Ordinate's documented NaN convention: a NaN coordinate is equal in this test. + var nan = new UncertainOrdinate(double.NaN, distribution); + Assert.IsTrue(nan == left); + } } } From 548666b99c1edecc5d437fa7e875b830c246e75f Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 28 Aug 2026 08:41:56 -0600 Subject: [PATCH 145/222] Factorize the RWMH proposal once and translate only the mean per iteration The proposal covariance is fixed for a whole run, yet every chain iteration re-ran an O(D^3) Cholesky factorization of the unchanged matrix through SetParameters. Each chain's proposal is now factorized once at initialization and moved with the new MultivariateNormal.SetMean, which validates the mean the same way SetParameters does and keeps every covariance-derived quantity. Draws are bit-identical, pinned by a seeded regression test whose literals were captured from the pre-refactor implementation; an invalid proposal covariance now throws at initialization instead of from inside the first chain iteration. --- .../Multivariate/MultivariateNormal.cs | 27 +++++++ Numerics/Sampling/MCMC/RWMH.cs | 23 +++--- Test_Numerics/Sampling/MCMC/Test_RWMH.cs | 72 ++++++++++++++++++- 3 files changed, 113 insertions(+), 9 deletions(-) diff --git a/Numerics/Distributions/Multivariate/MultivariateNormal.cs b/Numerics/Distributions/Multivariate/MultivariateNormal.cs index b5dcb37d..a36aa6ac 100644 --- a/Numerics/Distributions/Multivariate/MultivariateNormal.cs +++ b/Numerics/Distributions/Multivariate/MultivariateNormal.cs @@ -439,6 +439,33 @@ public void SetParameters(double[] mean, double[,] covariance) SetParametersCore(mean, covariance, singularValues); } + /// + /// Sets the mean vector μ (mu) while keeping the current covariance matrix and its factorization. + /// + /// The mean vector μ (mu) for the distribution. Its length must equal . + /// Thrown when the mean is null, contains a + /// non-finite value, or its length does not match the current dimension. + /// + /// A translation of the distribution changes no covariance-derived quantity — the factorization, + /// the log determinant, and the normalizing constant all depend on Σ alone — so a caller that + /// moves the distribution around a fixed covariance (for example a random walk proposal) can + /// avoid refactorizing an unchanged matrix on every step. The mean checks here are the same ones + /// applies. + /// + public void SetMean(double[] mean) + { + if (mean == null) + throw new ArgumentOutOfRangeException(nameof(mean), "Mean vector must not be null."); + if (mean.Length != Dimension) + throw new ArgumentOutOfRangeException(nameof(mean), "Mean length must match covariance dimension."); + for (int i = 0; i < mean.Length; i++) + { + if (double.IsNaN(mean[i]) || double.IsInfinity(mean[i])) + throw new ArgumentOutOfRangeException(nameof(mean), "Mean values must be finite."); + } + _mean = mean; + } + /// /// Applies already-validated parameters, reusing the decomposition built during validation. /// diff --git a/Numerics/Sampling/MCMC/RWMH.cs b/Numerics/Sampling/MCMC/RWMH.cs index a3f64016..110aa688 100644 --- a/Numerics/Sampling/MCMC/RWMH.cs +++ b/Numerics/Sampling/MCMC/RWMH.cs @@ -57,17 +57,22 @@ protected override void ValidateCustomSettings() /// protected override void InitializeCustomSettings() { - // Set up multivariate Normal distributions for each chain - mvn = new MultivariateNormal[NumberOfChains]; - for (int i = 0; i < NumberOfChains; i++) - { - mvn[i] = new MultivariateNormal(NumberOfParameters); - } // Set up proposal matrix if (Initialize == InitializationType.MAP && _mapSuccessful && _MVN != null) { ProposalSigma = new Matrix(_MVN.Covariance); } + // Set up multivariate Normal distributions for each chain. The proposal covariance is + // fixed for the whole run, so each chain's proposal is factorized exactly once here and + // ChainIteration then translates only the mean, keeping the factorization. A covariance + // that fails the factorization therefore throws here, before sampling starts, instead of + // from inside the first chain iteration. + mvn = new MultivariateNormal[NumberOfChains]; + for (int i = 0; i < NumberOfChains; i++) + { + mvn[i] = new MultivariateNormal(NumberOfParameters); + mvn[i].SetParameters(new double[NumberOfParameters], ProposalSigma.Array); + } } /// @@ -76,8 +81,10 @@ protected override ParameterSet ChainIteration(int index, ParameterSet state) // Update the sample count SampleCount[index] += 1; - // Get proposal vector - mvn[index].SetParameters(state.Values, ProposalSigma.Array); + // Get proposal vector. The proposal covariance was factorized once at initialization, so + // only the mean moves with the chain state — a translation changes nothing the + // factorization derives from the covariance. + mvn[index].SetMean(state.Values); var xp = mvn[index].InverseCDF(_chainPRNGs[index].NextDoubles(NumberOfParameters)); for (int i = 0; i < NumberOfParameters; i++) diff --git a/Test_Numerics/Sampling/MCMC/Test_RWMH.cs b/Test_Numerics/Sampling/MCMC/Test_RWMH.cs index 3e1d32f6..e7f467bf 100644 --- a/Test_Numerics/Sampling/MCMC/Test_RWMH.cs +++ b/Test_Numerics/Sampling/MCMC/Test_RWMH.cs @@ -69,7 +69,7 @@ double logLH(double[] x) Assert.AreEqual(11488.50, results.ParameterResults[0].SummaryStatistics.LowerCI, 0.05 * 11488.50); Assert.AreEqual(12671.08, results.ParameterResults[0].SummaryStatistics.Median, 0.05 * 12671.08); Assert.AreEqual(13801.45, results.ParameterResults[0].SummaryStatistics.UpperCI, 0.05 * 13801.45); - // Sigma + // Sigma Assert.AreEqual(4844.09, results.ParameterResults[1].SummaryStatistics.Mean, 0.05 * 4844.09); Assert.AreEqual(519.08, results.ParameterResults[1].SummaryStatistics.StandardDeviation, 0.05 * 519.08); Assert.AreEqual(4077.80, results.ParameterResults[1].SummaryStatistics.LowerCI, 0.05 * 4077.80); @@ -77,5 +77,75 @@ double logLH(double[] x) Assert.AreEqual(5771.81, results.ParameterResults[1].SummaryStatistics.UpperCI, 0.05 * 5771.81); } + /// + /// Pins seeded RWMH draws bitwise so the proposal distribution's mean-only update path can be + /// proven equivalent to the former full re-parameterization. + /// + /// + /// The proposal covariance is fixed for a whole RWMH run, yet every chain iteration used to call + /// MultivariateNormal.SetParameters with it, re-running an O(D^3) Cholesky factorization + /// of an unchanged matrix on every transition. The refactor factorizes each chain's proposal once + /// and translates only the mean per iteration, which must not change a single drawn value. The + /// expected literals below were captured from the pre-refactor implementation (seed 12345, + /// 2 chains, 200 iterations, 100 warmup, Randomize initialization) and every one must reproduce + /// exactly — no tolerance. + /// + [TestMethod] + public void Test_RWMH_MeanOnlyProposalUpdate_ReproducesReferenceDrawsExactly() + { + double[] sample = new double[] { 6290d, 2700d, 13100d, 16900d, 14600d, 9600d, 7740d, 8490d, 8130d, 12000d, 17200d, 15000d, 12400d, 6960d, 6500d, 5840d, 10400d, 18800d, 21400d, 22600d, 14200d, 11000d, 12800d, 15700d, 4740d, 6950d, 11800d, 12100d, 20600d, 14600d, 14600d, 8900d, 10600d, 14200d, 14100d, 14100d, 12500d, 7530d, 13400d, 17600d, 13400d, 19200d, 16900d, 15500d, 14500d, 21900d, 10400d, 7460d }; + + var normDist = new Normal(); + var constraints = normDist.GetParameterConstraints(sample); + var muPrior = new Uniform(constraints.Item2[0], constraints.Item3[0]); + var sigmaPrior = new Uniform(constraints.Item2[1], constraints.Item3[1]); + var priors = new List { muPrior, sigmaPrior }; + + double logLH(double[] x) + { + var dist = new Normal(x[0], x[1]); + return dist.LogLikelihood(sample); + } + + var proposal = new Matrix(2); + proposal[0, 0] = 500d * 500d; + proposal[1, 1] = 300d * 300d; + + var sampler = new RWMH(priors, logLH, proposal) + { + Initialize = MCMCSampler.InitializationType.Randomize, + PRNGSeed = 12345, + NumberOfChains = 2, + Iterations = 200, + WarmupIterations = 100, + ThinningInterval = 1, + OutputLength = 100, + }; + sampler.Sample(); + + Assert.AreEqual(11934.435149791721, sampler.Output[0][0].Values[0], 0d); + Assert.AreEqual(4526.5732644023383, sampler.Output[0][0].Values[1], 0d); + Assert.AreEqual(-474.22548961374116, sampler.Output[0][0].Fitness, 0d); + Assert.AreEqual(12095.33865486568, sampler.Output[0][1].Values[0], 0d); + Assert.AreEqual(4982.8206043145146, sampler.Output[0][1].Values[1], 0d); + Assert.AreEqual(12317.827258667343, sampler.Output[0][2].Values[0], 0d); + Assert.AreEqual(5032.5525299089159, sampler.Output[0][2].Values[1], 0d); + Assert.AreEqual(12178.391385589634, sampler.Output[0][sampler.Output[0].Count - 1].Values[0], 0d); + Assert.AreEqual(4339.4907777225499, sampler.Output[0][sampler.Output[0].Count - 1].Values[1], 0d); + Assert.AreEqual(178, sampler.AcceptCount[0]); + + Assert.AreEqual(20109.085873154781, sampler.Output[1][0].Values[0], 0d); + Assert.AreEqual(28890.963429995856, sampler.Output[1][0].Values[1], 0d); + Assert.AreEqual(21498.176703183646, sampler.Output[1][2].Values[0], 0d); + Assert.AreEqual(28440.722339269822, sampler.Output[1][2].Values[1], 0d); + Assert.AreEqual(19608.601940250355, sampler.Output[1][sampler.Output[1].Count - 1].Values[0], 0d); + Assert.AreEqual(25961.674646968706, sampler.Output[1][sampler.Output[1].Count - 1].Values[1], 0d); + Assert.AreEqual(209, sampler.AcceptCount[1]); + + Assert.AreEqual(12729.094200262518, sampler.MAP.Values[0], 0d); + Assert.AreEqual(4543.2396366299008, sampler.MAP.Values[1], 0d); + Assert.AreEqual(-473.59481939858011, sampler.MAP.Fitness, 0d); + } + } } From 7b9a32e881b01566fc75ad74b0c1c38a9274a5ba Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 28 Aug 2026 08:41:57 -0600 Subject: [PATCH 146/222] Make the SNIS resampling sort stable List.Sort is an unstable introsort, so draws with tied fitness - commonly many -Infinity values under wide priors - permuted nondeterministically and the resampled output was not reproducible across runs or platforms. OrderBy keeps tied draws in their original draw order. --- Numerics/Sampling/MCMC/SNIS.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Numerics/Sampling/MCMC/SNIS.cs b/Numerics/Sampling/MCMC/SNIS.cs index 349ad285..ab05bdc3 100644 --- a/Numerics/Sampling/MCMC/SNIS.cs +++ b/Numerics/Sampling/MCMC/SNIS.cs @@ -94,6 +94,11 @@ protected override void ValidateSettings() /// clamped non-negative, so the ordering affects which sample each plotting position selects /// rather than the validity of the draw. /// + /// + /// The sort is stable, so draws with tied fitness — commonly many -Infinity values under wide + /// priors — keep their original draw order and a seeded run resamples the same output on every + /// run and platform. + /// /// public override void Sample() { @@ -183,7 +188,10 @@ public override void Sample() // The list is sorted ascending on Fitness while the CDF below accumulates Weight; the two // keys coincide only when no importance distribution is supplied. See the remarks on Sample(). - MarkovChains[0].Sort((x, y) => x.Fitness.CompareTo(y.Fitness)); + // OrderBy is a stable sort, so tied fitness values (commonly many -Infinity draws under wide + // priors) keep their original draw order and the resampled output is reproducible across + // runs and platforms; List.Sort is an unstable introsort whose tie order is not. + MarkovChains[0] = MarkovChains[0].OrderBy(x => x.Fitness).ToList(); var cdf = new double[Iterations]; cdf[0] = Math.Max(0.0, MarkovChains[0][0].Weight); From d3054a80e8536449906f1fe08ef83dc4db8190d0 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 28 Aug 2026 08:41:57 -0600 Subject: [PATCH 147/222] Apply the discarded M-step positive-definite repair to the mixture covariances MakeSymmetricPositiveDefinite is pure - it returns a symmetrized copy with a trace-scaled ridge - and the M-step discarded that return value, so the repair its own comment promised was a no-op and the next E-step's Cholesky was protected only by the diagonal floor. Fitted covariances now carry the base ridge of about 1E-10 of the mean diagonal; a contract test recomputes the M-step externally from the public responsibilities and asserts the ridge to the bit. --- .../Unsupervised/GaussianMixtureModel.cs | 7 +- .../Machine Learning/Unsupervised/Test_GMM.cs | 79 +++++++++++++++++++ 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/Numerics/Machine Learning/Unsupervised/GaussianMixtureModel.cs b/Numerics/Machine Learning/Unsupervised/GaussianMixtureModel.cs index dba186ba..ccbcc5e2 100644 --- a/Numerics/Machine Learning/Unsupervised/GaussianMixtureModel.cs +++ b/Numerics/Machine Learning/Unsupervised/GaussianMixtureModel.cs @@ -355,8 +355,11 @@ private void MStep() Sigmas[k][d, d] = Math.Max(Sigmas[k][d, d], 1E-6 * colVar); } - // Ensure the full covariance matrix remains symmetric positive-definite - MatrixRegularization.MakeSymmetricPositiveDefinite(Sigmas[k]); + // Ensure the full covariance matrix remains symmetric positive-definite. The helper is + // pure — it returns a symmetrized copy with a trace-scaled ridge — so its result must be + // assigned; a discarded call leaves the repair a no-op and the next E-step's Cholesky + // factorization protected only by the diagonal floor above. + Sigmas[k] = MatrixRegularization.MakeSymmetricPositiveDefinite(Sigmas[k]); } } diff --git a/Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs b/Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs index 81902825..deeee97a 100644 --- a/Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs +++ b/Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs @@ -98,5 +98,84 @@ public void Test_GMM_DegenerateFixture_StaysFinite() Assert.IsFalse(double.IsNaN(gmm.Means[k, d]), $"mean [{k},{d}] is NaN"); } + /// + /// Verify that the M-step's symmetric positive-definite repair is actually applied to the + /// stored covariance matrices. + /// + /// + /// MatrixRegularization.MakeSymmetricPositiveDefinite is pure — it returns a symmetrized copy + /// with a trace-scaled ridge — and the M-step used to discard that return value, so the repair + /// its own comment promised was a silent no-op and only the diagonal floor protected the next + /// E-step's Cholesky factorization. This test recomputes the M-step covariance externally from + /// the public responsibilities after a single EM iteration and asserts the stored matrices carry + /// the repair's base ridge (1E-10 of the mean diagonal) exactly; the pre-fix code reproduced the + /// external value WITHOUT the ridge and failed these assertions by exactly that amount. + /// + [TestMethod] + public void Test_GMM_MStep_PositiveDefiniteRepairIsApplied() + { + var data = new double[,] + { + { 1.0, 2.1 }, { 1.2, 1.9 }, { 0.8, 2.3 }, { 1.1, 2.0 }, { 0.9, 1.8 }, { 1.3, 2.2 }, + { 8.0, 9.1 }, { 8.2, 8.9 }, { 7.8, 9.3 }, { 8.1, 9.0 }, { 7.9, 8.8 }, { 8.3, 9.2 } + }; + var gmm = new GaussianMixtureModel(data, 2) { MaxIterations = 1 }; + gmm.Train(seed: 42); + + int n = data.GetLength(0); + int dims = data.GetLength(1); + + // Per-dimension population variance of the whole sample, matching the M-step's floor. + var colVar = new double[dims]; + for (int d = 0; d < dims; d++) + { + double colMean = 0; + for (int i = 0; i < n; i++) + colMean += data[i, d]; + colMean /= n; + double v = 0; + for (int i = 0; i < n; i++) + v += (data[i, d] - colMean) * (data[i, d] - colMean); + colVar[d] = v / n; + } + + for (int k = 0; k < 2; k++) + { + double wgt = 0d; + for (int i = 0; i < n; i++) + wgt += gmm.LikelihoodMatrix[i, k]; + Assert.IsTrue(wgt > 0, "Fixture precondition: both components carry responsibility."); + + // Recompute the raw M-step covariance from the responsibilities and stored means. + var expected = new double[dims, dims]; + for (int d = 0; d < dims; d++) + { + for (int j = 0; j < dims; j++) + { + double sum = 0; + for (int i = 0; i < n; i++) + sum += gmm.LikelihoodMatrix[i, k] * (data[i, d] - gmm.Means[k, d]) * (data[i, j] - gmm.Means[k, j]); + expected[d, j] = sum / wgt; + } + } + // Apply the diagonal floor, then the repair's base ridge (the first Cholesky attempt + // succeeds for these well-separated clusters, so exactly one base ridge is added). + double trace = 0; + for (int d = 0; d < dims; d++) + { + expected[d, d] = System.Math.Max(expected[d, d], 1E-6 * colVar[d]); + trace += expected[d, d]; + } + double baseRidge = 1e-10 * trace / dims; + for (int d = 0; d < dims; d++) + expected[d, d] += baseRidge; + + for (int d = 0; d < dims; d++) + for (int j = 0; j < dims; j++) + Assert.AreEqual(expected[d, j], gmm.Sigmas[k][d, j], 0d, + $"Sigma[{k}][{d},{j}] must carry the positive-definite repair."); + } + } + } } From 95f8765044d7e3a3788455562d57f091b5606208 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 28 Aug 2026 08:42:09 -0600 Subject: [PATCH 148/222] Stop regression tree growth at pure nodes Regression growth set the label count to the sample count, so the pure-node guard only fired on single-row nodes, and a zero-gain split of a pure node still beat the double.MinValue best-gain seed: a default regression tree recursed to one observation per leaf. Counting distinct responses for both modes applies scikit-learn's rule - a node is never split once it is pure. The pinned golden trees and forest predictions are unchanged because the golden dataset has no exactly tied responses; the new twelve-point two-value fixture, which formerly grew a 23-node chain, now stops at the root split. --- .../Supervised/DecisionTree.cs | 12 +++++-- .../Supervised/Test_DecisionTree.cs | 35 +++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/Numerics/Machine Learning/Supervised/DecisionTree.cs b/Numerics/Machine Learning/Supervised/DecisionTree.cs index 5f308464..94ba3c06 100644 --- a/Numerics/Machine Learning/Supervised/DecisionTree.cs +++ b/Numerics/Machine Learning/Supervised/DecisionTree.cs @@ -228,7 +228,12 @@ public void Train() private DecisionNode GrowTree(int[] indices, int lo, int hi, int depth) { int numberOfSamples = hi - lo; - int numberOfLabels = IsRegression ? numberOfSamples : CountDistinctLabels(indices, lo, hi); + // Count distinct responses for BOTH modes so the pure-node guard below can fire. Regression + // used to substitute the sample count, which made the guard redundant with the minimum split + // size, and a zero-gain split of a pure node still beat the double.MinValue seed — a default + // regression tree therefore recursed to one observation per leaf. Counting distinct values + // applies scikit-learn's rule: a node is never split once it is pure. + int numberOfLabels = CountDistinctLabels(indices, lo, hi); // The feature subset is drawn for every node, split or leaf, so the generator consumes // exactly one draw per node and the draw schedule is independent of the stopping conditions. @@ -259,12 +264,13 @@ private DecisionNode GrowTree(int[] indices, int lo, int hi, int depth) } /// - /// Counts the distinct classification labels within an index range. + /// Counts the distinct response values within an index range — class labels for classification, + /// response values for regression. /// /// The training row indices. /// The inclusive start of the range. /// The exclusive end of the range. - /// The number of distinct labels, counting NaN labels as one label. + /// The number of distinct values, counting NaN responses as one value. private int CountDistinctLabels(int[] indices, int lo, int hi) { var labels = new HashSet(); diff --git a/Test_Numerics/Machine Learning/Supervised/Test_DecisionTree.cs b/Test_Numerics/Machine Learning/Supervised/Test_DecisionTree.cs index fb087f34..664ce5e4 100644 --- a/Test_Numerics/Machine Learning/Supervised/Test_DecisionTree.cs +++ b/Test_Numerics/Machine Learning/Supervised/Test_DecisionTree.cs @@ -109,5 +109,40 @@ public void Test_DecisionTree_Regression() } + /// + /// Verify that a regression node whose responses are all equal becomes a leaf instead of + /// splitting further. + /// + /// + /// Regression growth used to set the label count to the SAMPLE count, so the pure-node guard + /// only fired on single-row nodes, and a zero-gain split of a pure node still beat the + /// double.MinValue seed: this twelve-point, two-value fixture grew a 23-node right-leaning + /// chain with one observation per leaf. Counting distinct responses for both modes applies + /// scikit-learn's rule — a node is never split once it is pure — so the same fixture now + /// stops at the root split with two pure leaves. + /// + [TestMethod] + public void Test_DecisionTree_Regression_PureNodeBecomesLeaf() + { + var x = new Matrix(new List { new double[] { 1, 2, 3, 4, 5, 6, 100, 101, 102, 103, 104, 105 } }); + var y = new Vector(new double[] { 10, 10, 10, 10, 10, 10, 100, 100, 100, 100, 100, 100 }); + var dt = new DecisionTree(x, y, 7) { Features = 1 }; + dt.Train(); + + int CountNodes(DecisionNode n) + { + return n == null ? 0 : 1 + CountNodes(n.Left) + CountNodes(n.Right); + } + + Assert.AreEqual(3, CountNodes(dt.Root), "A pure node must not be split further."); + Assert.IsFalse(dt.Root.IsLeafNode); + Assert.IsTrue(dt.Root.Left.IsLeafNode); + Assert.IsTrue(dt.Root.Right.IsLeafNode); + double lower = System.Math.Min(dt.Root.Left.Value, dt.Root.Right.Value); + double upper = System.Math.Max(dt.Root.Left.Value, dt.Root.Right.Value); + Assert.AreEqual(10d, lower, 0d); + Assert.AreEqual(100d, upper, 0d); + } + } } From 54d0f60025e35d1362a3787ddec1d0f1d961a03e Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 28 Aug 2026 08:42:10 -0600 Subject: [PATCH 149/222] Implement the dfpmin parameter-change exit in BFGS TOLX was declared but never consumed, so a run whose line search returned the starting point repeated the identical non-progressing iteration - zero step, zero gradient difference, unchanged search direction - until the evaluation budget was exhausted. The Numerical Recipes exit stops when the largest relative parameter step falls below TOLX. Every optimizer accuracy and bookkeeping assertion in the suite, including MultiStart and MLSL whose local runs share their evaluation budget with BFGS, passes unchanged. --- .../Mathematics/Optimization/Local/BFGS.cs | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/Numerics/Mathematics/Optimization/Local/BFGS.cs b/Numerics/Mathematics/Optimization/Local/BFGS.cs index 09ecc318..0f991498 100644 --- a/Numerics/Mathematics/Optimization/Local/BFGS.cs +++ b/Numerics/Mathematics/Optimization/Local/BFGS.cs @@ -104,16 +104,12 @@ protected override void Optimize() { int D = NumberOfParameters; double EPS = Tools.DoubleMachineEpsilon; - // TOLX matches Numerical Recipes' dfpmin but is not consumed here: dfpmin's outer - // parameter-change exit, which stops when the largest relative parameter step falls below - // TOLX, is not implemented. Convergence is decided solely by CheckConvergence's relative - // function-change test, so a run whose parameters stagnate while the function value still - // moves iterates to MaxIterations. Implementing the exit would change the points the global - // searches report: MultiStart and MLSL share their evaluation budget with the local runs - // they launch, and the additional iterations act as extra sampling for their best-point - // tracking. (The TOLX local in LineSearchArmijo is dfpmin's unrelated inner step-size - // floor, and that routine is not called by Optimize, which uses the strong Wolfe - // LineSearch.) + // TOLX is Numerical Recipes' dfpmin outer parameter-change tolerance: the loop below exits + // when the largest relative parameter step falls below it, so a line search that returns + // the starting point (a stagnated warm start) terminates immediately instead of repeating + // the identical non-progressing iteration until MaxIterations. (The TOLX local in + // LineSearchArmijo is dfpmin's unrelated inner step-size floor, and that routine is not + // called by Optimize, which uses the strong Wolfe LineSearch.) double TOLX = 4 * EPS, STPMX = 100.0; bool cancel = false, check = false; @@ -155,7 +151,7 @@ protected override void Optimize() } // The new function evaluation occurs in line search; save the function value in fp for the next line search. - // It is usually safe to ignore the value of check. + // It is usually safe to ignore the value of check. fp = fret; for (int i = 0; i < D; i++) { @@ -163,7 +159,23 @@ protected override void Optimize() p[i] = pnew[i]; } - // Save the old gradient, and get the new gradient. + // Numerical Recipes dfpmin: exit when the largest relative parameter step falls below + // TOLX. Without this test a stalled line search leaves xi and dg at zero, the inverse + // Hessian update is skipped, the search direction never changes, and the loop repeats + // the identical iteration until the evaluation budget is exhausted. + double test = 0.0; + for (int i = 0; i < D; i++) + { + double temp = Math.Abs(xi[i]) / Math.Max(Math.Abs(p[i]), 1.0); + if (temp > test) test = temp; + } + if (test < TOLX) + { + UpdateStatus(OptimizationStatus.Success); + return; + } + + // Save the old gradient, and get the new gradient. for (int i = 0; i < D; i++) dg[i] = g[i]; g = Gradient != null ? Gradient(p) : NumericalDerivative.Gradient((x) => Evaluate(x, ref cancel), p, LowerBounds, UpperBounds); From 175e8845baf39fe56f211e8aacedd408ee1db6cc Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 28 Aug 2026 08:42:10 -0600 Subject: [PATCH 150/222] Scale the correlated-search windows with table size so the hunt path is reachable Interpolater's window was pinned to exactly 1 by a Math.Min that should have been Math.Max, and OrderedPairedData's X and Y windows were never assigned past zero, so the correlated test reported true only on an exact index repeat and the hunt search the smart-search machinery is named for was effectively unreachable. Both now use the Numerical Recipes N^0.25 heuristic (never below 1); hunt and bisection return the same bracket, pinned by sweep tests comparing the smart path against cold bisection. --- .../Interpolation/Support/Interpolater.cs | 14 +++++----- .../Data/Paired Data/OrderedPairedData.cs | 22 ++++++++------- .../Data/Interpolation/Test_Linear.cs | 27 +++++++++++++++++++ .../Test_PairedDataInterpolation.cs | 25 +++++++++++++++++ 4 files changed, 71 insertions(+), 17 deletions(-) diff --git a/Numerics/Data/Interpolation/Support/Interpolater.cs b/Numerics/Data/Interpolation/Support/Interpolater.cs index 73291bfc..16837271 100644 --- a/Numerics/Data/Interpolation/Support/Interpolater.cs +++ b/Numerics/Data/Interpolation/Support/Interpolater.cs @@ -43,9 +43,10 @@ public Interpolater(IList xValues, IList yValues, SortOrder sort } this.XValues = xValues; this.YValues = yValues; - // Always exactly 1: the constructor requires Count >= 2, so (int)Math.Pow(Count, 0.25) is at - // least 1 and Math.Min(1, ...) is 1. See the remarks on deltaStart. - deltaStart = Math.Min(1, (int)Math.Pow((double)Count, 0.25)); + // Scale the correlated-search window with the table size (Numerical Recipes' N^0.25 hunt + // heuristic). Math.Max keeps the window at least 1; the former Math.Min pinned it to + // exactly 1 for every table, which starved the hunt path. See the remarks on deltaStart. + deltaStart = Math.Max(1, (int)Math.Pow((double)Count, 0.25)); SortOrder = sortOrder; } @@ -65,10 +66,9 @@ public Interpolater(IList xValues, IList yValues, SortOrder sort /// treated as correlated, selecting the hunt search over bisection. /// /// - /// The constructor assigns Math.Min(1, (int)Math.Pow(Count, 0.25)), which is always - /// exactly 1: the constructor requires Count >= 2, so the right-hand term is never - /// below 1. The window does not grow with the table size, at any . The value - /// affects only which search path runs — hunt and bisection return the same bracket for the same + /// The constructor assigns Math.Max(1, (int)Math.Pow(Count, 0.25)), the Numerical + /// Recipes N^0.25 hunt heuristic, so the window grows with the table size. The value affects + /// only which search path runs — hunt and bisection return the same bracket for the same /// input — so it is a performance characteristic rather than a correctness one. /// and are public and settable, so a /// consumer that wants different search behaviour can steer the search directly. diff --git a/Numerics/Data/Paired Data/OrderedPairedData.cs b/Numerics/Data/Paired Data/OrderedPairedData.cs index 4f558d2c..6090f06e 100644 --- a/Numerics/Data/Paired Data/OrderedPairedData.cs +++ b/Numerics/Data/Paired Data/OrderedPairedData.cs @@ -49,25 +49,27 @@ public class OrderedPairedData : IList, INotifyCollectionChanged /// treated as correlated, selecting the hunt search over bisection. /// /// - /// This field is never assigned after initialization, so the correlated test in - /// reports correlated only when a search lands on exactly the same - /// index as the previous one. The value affects only which search path runs — hunt and bisection - /// return the same bracket for the same input — so it is a performance characteristic rather than - /// a correctness one. and are public and + /// Computed from the collection size on every read as the Numerical Recipes N^0.25 hunt + /// heuristic (never below 1), matching the Interpolater's window, so the correlated test in + /// can actually select the hunt path; the former constant 0 + /// reported correlated only when a search landed on exactly the same index as the previous + /// one. The value affects only which search path runs — hunt and bisection return the same + /// bracket for the same input — so it is a performance characteristic rather than a + /// correctness one. and are public and /// settable, so a consumer can steer the search directly. /// - private int XdeltaStart = 0; + private int XdeltaStart => Math.Max(1, (int)Math.Pow(Count, 0.25)); /// /// The maximum distance between consecutive y-search results for which those searches are still /// treated as correlated, selecting the hunt search over bisection. /// /// - /// Never assigned after initialization, so reports correlated only - /// on an exact index repeat. The effect is confined to which search path runs, never to the - /// bracket returned; see . + /// Computed from the collection size on every read, exactly like ; + /// the former constant 0 reported correlated only on an exact index repeat. The effect is + /// confined to which search path runs, never to the bracket returned. /// - private int YdeltaStart = 0; + private int YdeltaStart => Math.Max(1, (int)Math.Pow(Count, 0.25)); /// /// Determines which search method to use. If values are correlated, use the Hunt method. diff --git a/Test_Numerics/Data/Interpolation/Test_Linear.cs b/Test_Numerics/Data/Interpolation/Test_Linear.cs index 3e50204f..d6ab3571 100644 --- a/Test_Numerics/Data/Interpolation/Test_Linear.cs +++ b/Test_Numerics/Data/Interpolation/Test_Linear.cs @@ -81,6 +81,33 @@ public void Test_Hunt() Assert.AreEqual(127, lo); } + /// + /// Verify that correlated smart searches, which now genuinely reach the hunt path, return the + /// same interpolated values as cold bisection searches. + /// + /// + /// The correlated-search window scales as Count^0.25 (5 for this 1000-point table), so a sweep + /// stepping about one index per query keeps the correlated flag set after the first call and the + /// smart instance answers from the hunt path. The former Math.Min typo pinned the window to 1, + /// which left the hunt branch effectively unreachable; hunt and bisection must return the same + /// bracket, so the two instances must agree exactly. + /// + [TestMethod] + public void Test_SmartSearch_HuntPathMatchesColdBisection() + { + var values = new double[1000]; + for (int i = 1; i <= 1000; i++) + values[i - 1] = i; + + var smart = new Linear(values, values); + for (double x = 500.3; x <= 540.0; x += 1.7) + { + double huntValue = smart.Interpolate(x); + double coldValue = new Linear(values, values).Interpolate(x); + Assert.AreEqual(coldValue, huntValue, 0d); + } + } + /// /// Test the most basic implementation of the linear class and its interpolation function with one value /// diff --git a/Test_Numerics/Data/Paired Data/Test_PairedDataInterpolation.cs b/Test_Numerics/Data/Paired Data/Test_PairedDataInterpolation.cs index 62369b9a..284b2159 100644 --- a/Test_Numerics/Data/Paired Data/Test_PairedDataInterpolation.cs +++ b/Test_Numerics/Data/Paired Data/Test_PairedDataInterpolation.cs @@ -44,6 +44,31 @@ public void Test_Sequential() } + /// + /// Verify that correlated smart searches, which now genuinely reach the hunt path, return the + /// same bracket indexes as direct bisection searches. + /// + /// + /// The correlated-search window scales as Count^0.25 (5 for this 1000-point collection), so a + /// sweep stepping about one index per query keeps the correlated flag set after the first call + /// and answers from the hunt path. The former + /// never-assigned window of 0 reported correlated only on an exact index repeat, which left the + /// hunt branch effectively unreachable; hunt and bisection must return the same bracket. + /// + [TestMethod()] + public void Test_SmartSearch_HuntPathMatchesBisection() + { + var opd = new OrderedPairedData(true, SortOrder.Ascending, false, SortOrder.Ascending); + for (int i = 1; i <= 1000; i++) + opd.Add(new Ordinate(i, i)); + + for (double value = 500.3; value <= 540.0; value += 1.7) + { + Assert.AreEqual(opd.BisectionSearchX(value), opd.SearchX(value)); + Assert.AreEqual(opd.BisectionSearchY(value), opd.SearchY(value)); + } + } + /// /// Test the bisection search method within the OrderedPairedData class /// From 94491301691b625433f9d7dee4c0afebfabf0683 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 28 Aug 2026 08:49:32 -0600 Subject: [PATCH 151/222] Allow a negative log-space mean in the LogNormal and LP3 parameter constraints The location parameter of both families is the mean of the log10-transformed data, which is legitimately negative whenever the data are mostly below 1 (a snow-water-equivalent record, for example). GetParameterConstraints floored its lower bound at machine epsilon and collapsed the upper bound toward ceil(mean + 1), so a sub-unity sample seeded an initial value below its own lower bound - and, for a mean at or below -1, an inverted bound pair - which made every downstream consumer report the inputs invalid before a fit could start. The mean bounds are now symmetric about zero from the magnitude of the initial value, matching Normal's location bounds, and MinimumOfParameters reports negative infinity for the location like LnNormal already did. LnNormal itself is parameterized by the real-space mean and needed no change. --- .../Distributions/Univariate/LogNormal.cs | 19 +++++--- .../Univariate/LogPearsonTypeIII.cs | 19 +++++--- .../Univariate/Test_LogNormal.cs | 46 ++++++++++++++++++- .../Univariate/Test_LogPearsonTypeIII.cs | 25 +++++++++- 4 files changed, 93 insertions(+), 16 deletions(-) diff --git a/Numerics/Distributions/Univariate/LogNormal.cs b/Numerics/Distributions/Univariate/LogNormal.cs index b7c2e51b..e5c943ca 100644 --- a/Numerics/Distributions/Univariate/LogNormal.cs +++ b/Numerics/Distributions/Univariate/LogNormal.cs @@ -256,7 +256,9 @@ public override double Maximum /// public override double[] MinimumOfParameters { - get { return [0.0d, 0.0d]; } + // The mean of the log10-transformed variable is a location parameter and can be any + // finite value; only the log-space standard deviation is bounded below by zero. + get { return [double.NegativeInfinity, 0.0d]; } } /// @@ -454,13 +456,16 @@ public Tuple GetParameterConstraints(IList // Estimate initial values using the method of moments (a.k.a product moments). var mom = IndirectMethodOfMoments(sample); initialVals = new double[] { mom[0], mom[1] }; - // Get bounds of mean - double real = Math.Exp(initialVals[0] / K); - lowerVals[0] = Tools.DoubleMachineEpsilon; - upperVals[0] = Math.Ceiling(Math.Log(Math.Pow(10d, Math.Ceiling(Math.Log10(real) + 1d)), Base)); - upperVals[0] = double.IsNaN(upperVals[0]) ? 5 : upperVals[0]; + // Get bounds of mean. The mean is a location parameter on the log scale and is + // legitimately negative whenever the data are mostly below 1, so the bounds are + // symmetric about zero from the magnitude of the initial value, matching Normal's + // location bounds; the former machine-epsilon floor rejected any sub-unity sample + // before a fit could start. + if (initialVals[0] == 0d) initialVals[0] = Tools.DoubleMachineEpsilon; + lowerVals[0] = -Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); + upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); // Get bounds of standard deviation - real = Math.Exp(initialVals[1] / K); + double real = Math.Exp(initialVals[1] / K); lowerVals[1] = Tools.DoubleMachineEpsilon; upperVals[1] = Math.Ceiling(Math.Log(Math.Pow(10d, Math.Ceiling(Math.Log10(real) + 1d)), Base)); upperVals[1] = double.IsNaN(upperVals[1]) ? 4 : upperVals[1]; diff --git a/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs b/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs index fd4bd642..5497bb6a 100644 --- a/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs +++ b/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs @@ -399,7 +399,9 @@ public override double Maximum /// public override double[] MinimumOfParameters { - get { return [0.0d, 0.0d, double.NegativeInfinity]; } + // The mean of the log10-transformed variable is a location parameter and can be any + // finite value; only the log-space standard deviation is bounded below by zero. + get { return [double.NegativeInfinity, 0.0d, double.NegativeInfinity]; } } /// @@ -690,13 +692,16 @@ public Tuple GetParameterConstraints(IList // Estimate initial values using the method of moments. var mom = IndirectMethodOfMoments(sample); initialVals = [mom[0], mom[1], mom[2]]; - // Get bounds of mean - double real = Math.Exp(initialVals[0] / K); - lowerVals[0] = Tools.DoubleMachineEpsilon; - upperVals[0] = Math.Ceiling(Math.Log(Math.Pow(10d, Math.Ceiling(Math.Log10(real) + 1d)), Base)); - upperVals[0] = double.IsNaN(upperVals[0]) ? 5 : upperVals[0]; + // Get bounds of mean. The mean is a location parameter on the log scale and is + // legitimately negative whenever the data are mostly below 1, so the bounds are + // symmetric about zero from the magnitude of the initial value, matching Normal's + // location bounds; the former machine-epsilon floor rejected any sub-unity sample + // before a fit could start. + if (initialVals[0] == 0d) initialVals[0] = Tools.DoubleMachineEpsilon; + lowerVals[0] = -Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); + upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); // Get bounds of standard deviation - real = Math.Exp(initialVals[1] / K); + double real = Math.Exp(initialVals[1] / K); lowerVals[1] = Tools.DoubleMachineEpsilon; upperVals[1] = Math.Ceiling(Math.Log(Math.Pow(10d, Math.Ceiling(Math.Log10(real) + 1d)), Base)); upperVals[1] = double.IsNaN(upperVals[1]) ? 4 : upperVals[1]; diff --git a/Test_Numerics/Distributions/Univariate/Test_LogNormal.cs b/Test_Numerics/Distributions/Univariate/Test_LogNormal.cs index 0cf6bae5..7cde7b49 100644 --- a/Test_Numerics/Distributions/Univariate/Test_LogNormal.cs +++ b/Test_Numerics/Distributions/Univariate/Test_LogNormal.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Distributions; @@ -276,5 +276,49 @@ public void Test_InverseCDF() var LogN2 = new LogNormal(1.5, 2.5); Assert.AreEqual(40183.99248, LogN.InverseCDF(0.8), 1e-05); } + /// + /// Verify the parameter constraints admit a negative log10-space mean. + /// + /// + /// The location parameter is the mean of the log10-transformed data, which is legitimately + /// negative whenever the data are mostly below 1. The former constraints floored the lower + /// bound at machine epsilon and collapsed the upper bound toward ceil(mu + 1), so a sub-unity + /// sample produced an initial value below its own lower bound (and, for mu at or below -1, + /// an inverted lower/upper pair) and every downstream consumer reported the inputs invalid. + /// The bounds are now symmetric about zero from the magnitude of the initial value, matching + /// the Normal distribution's location bounds. + /// + [TestMethod] + public void Test_LogNormal_ParameterConstraints_AllowNegativeLogMean() + { + var shallow = new double[] { 0.12, 0.31, 0.45, 0.08, 0.90, 1.4, 0.25, 0.6, 0.5, 0.75 }; + var deep = new double[] { 0.004, 0.012, 0.008, 0.02, 0.006, 0.015, 0.003, 0.01, 0.007, 0.011 }; + + foreach (var sample in new[] { shallow, deep }) + { + var constraints = new LogNormal().GetParameterConstraints(sample); + var initials = constraints.Item1; + var lowers = constraints.Item2; + var uppers = constraints.Item3; + Assert.IsTrue(initials[0] < 0d, "Fixture precondition: the log10 mean is negative."); + Assert.IsTrue(lowers[0] < uppers[0], "The mean bounds must not be inverted."); + Assert.IsTrue(initials[0] >= lowers[0] && initials[0] <= uppers[0], + "The initial mean must sit inside its own bounds."); + Assert.IsTrue(lowers[1] < uppers[1] && initials[1] >= lowers[1] && initials[1] <= uppers[1], + "The standard deviation bounds must contain the initial value."); + } + } + + /// + /// Verify the parameter minimum metadata and validation admit a negative log10-space mean. + /// + [TestMethod] + public void Test_LogNormal_NegativeLogMean_IsValid() + { + Assert.AreEqual(double.NegativeInfinity, new LogNormal().MinimumOfParameters[0]); + var dist = new LogNormal(); + Assert.IsNull(dist.ValidateParameters(new[] { -1.5, 0.3 }, false)); + } + } } diff --git a/Test_Numerics/Distributions/Univariate/Test_LogPearsonTypeIII.cs b/Test_Numerics/Distributions/Univariate/Test_LogPearsonTypeIII.cs index 3fc6cf36..7844d234 100644 --- a/Test_Numerics/Distributions/Univariate/Test_LogPearsonTypeIII.cs +++ b/Test_Numerics/Distributions/Univariate/Test_LogPearsonTypeIII.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Distributions; @@ -382,5 +382,28 @@ public void Test_LinearMoments_SignedSmallAndZeroSkew() Assert.AreEqual(0.3d, recovered[1], 1E-5); Assert.AreEqual(-0.1d, recovered[2], 1E-4); } + /// + /// Verify the parameter constraints admit a negative log10-space mean. + /// + /// + /// See the matching LogNormal test: the location parameter is the mean of the + /// log10-transformed data, negative whenever the data are mostly below 1, and the former + /// machine-epsilon lower bound rejected any such sample before a fit could start. + /// + [TestMethod] + public void Test_LP3_ParameterConstraints_AllowNegativeLogMean() + { + var sample = new double[] { 0.12, 0.31, 0.45, 0.08, 0.90, 1.4, 0.25, 0.6, 0.5, 0.75, 0.2, 0.33 }; + var constraints = new LogPearsonTypeIII().GetParameterConstraints(sample); + var initials = constraints.Item1; + var lowers = constraints.Item2; + var uppers = constraints.Item3; + Assert.IsTrue(initials[0] < 0d, "Fixture precondition: the log10 mean is negative."); + Assert.IsTrue(lowers[0] < uppers[0], "The mean bounds must not be inverted."); + Assert.IsTrue(initials[0] >= lowers[0] && initials[0] <= uppers[0], + "The initial mean must sit inside its own bounds."); + Assert.AreEqual(double.NegativeInfinity, new LogPearsonTypeIII().MinimumOfParameters[0]); + } + } } From 3d2405b84c09e23139406bf782a7b952a7468ec1 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 28 Aug 2026 09:20:13 -0600 Subject: [PATCH 152/222] Expose the peaks-over-threshold smoothing preprocessing as SmoothedSeries The smoothing branch is extracted from PeaksOverThresholdSeries unchanged and called by it, so threshold-selection diagnostics can operate on the exact series the extraction thresholds. BestFit's POT diagnostic plots formerly read the raw source series while the extraction thresholded the smoothed one, so the threshold annotation and the diagnostic curves sat on different value scales and never responded to smoothing edits. --- Numerics/Data/Time Series/TimeSeries.cs | 47 ++++++++++++------ .../Data/Time Series/Test_TimeSeries.cs | 49 +++++++++++++++++++ 2 files changed, 82 insertions(+), 14 deletions(-) diff --git a/Numerics/Data/Time Series/TimeSeries.cs b/Numerics/Data/Time Series/TimeSeries.cs index 46cb4800..75a6e87b 100644 --- a/Numerics/Data/Time Series/TimeSeries.cs +++ b/Numerics/Data/Time Series/TimeSeries.cs @@ -2102,6 +2102,37 @@ public TimeSeries QuarterlySeries(BlockFunctionType blockFunction = BlockFunctio return result; } + /// + /// Returns the smoothed series a peaks-over-threshold analysis operates on. + /// + /// The smoothing function type. + /// The time period to perform smoothing over. If time interval is 1-hour, and period is 12. The smoothing will be computed over a moving 12 hour block. + /// The smoothed series, or a clone of this series when no smoothing applies. + /// + /// This is the exact preprocessing applies before + /// comparing values to the threshold, exposed so threshold-selection diagnostics can operate + /// on the same series the extraction thresholds — plotting diagnostics computed from the raw + /// series would sit on a different value scale than the threshold whenever smoothing is + /// configured. Moving average and moving sum with a period of 1 are identity clones; + /// differencing applies at every period. + /// + public TimeSeries SmoothedSeries(SmoothingFunctionType smoothingFunction, int period = 1) + { + if (smoothingFunction == SmoothingFunctionType.MovingAverage) + { + return period == 1 ? Clone() : MovingAverage(period); + } + if (smoothingFunction == SmoothingFunctionType.MovingSum) + { + return period == 1 ? Clone() : MovingSum(period); + } + if (smoothingFunction == SmoothingFunctionType.Difference) + { + return Difference(period); + } + return Clone(); + } + /// /// Returns a peaks-over-threshold (POT) series. /// @@ -2127,21 +2158,9 @@ public TimeSeries QuarterlySeries(BlockFunctionType blockFunction = BlockFunctio public TimeSeries PeaksOverThresholdSeries(double threshold, int minStepsBetweenEvents = 1, SmoothingFunctionType smoothingFunction = SmoothingFunctionType.None, int period = 1) { // Create smoothed time series - TimeSeries smoothedSeries = Clone(); - if (smoothingFunction == SmoothingFunctionType.MovingAverage) - { - smoothedSeries = period == 1 ? Clone() : MovingAverage(period); - } - else if (smoothingFunction == SmoothingFunctionType.MovingSum) - { - smoothedSeries = period == 1 ? Clone() : MovingSum(period); - } - else if (smoothingFunction == SmoothingFunctionType.Difference) - { - smoothedSeries = Difference(period); - } + TimeSeries smoothedSeries = SmoothedSeries(smoothingFunction, period); - // First, create the cluster indexes. + // First, create the cluster indexes. int i = 0, idx, idxMax; var clusters = new List(); diff --git a/Test_Numerics/Data/Time Series/Test_TimeSeries.cs b/Test_Numerics/Data/Time Series/Test_TimeSeries.cs index 3a24a7d5..ef2ecfad 100644 --- a/Test_Numerics/Data/Time Series/Test_TimeSeries.cs +++ b/Test_Numerics/Data/Time Series/Test_TimeSeries.cs @@ -1571,5 +1571,54 @@ public void Test_RemoveAt_WithDuplicateOrdinates_RemovesRequestedIndex() #endregion + /// + /// Verify SmoothedSeries reproduces the exact preprocessing PeaksOverThresholdSeries applies. + /// + /// + /// The smoothing branch was extracted from PeaksOverThresholdSeries so threshold-selection + /// diagnostics can operate on the same series the extraction thresholds; this test pins the + /// branch behavior — moving average and moving sum smooth for periods above 1 and clone at + /// a period of 1, differencing applies at every period, and None clones — and that the + /// smoothed values genuinely differ from the raw values when smoothing is configured. + /// + [TestMethod] + public void Test_SmoothedSeries_MatchesPeaksOverThresholdPreprocessing() + { + var series = new TimeSeries(TimeInterval.OneDay, new DateTime(2020, 1, 1), new double[] { 5, 9, 2, 14, 7, 11, 3, 16, 8, 12 }); + + // Moving average over 3 steps matches the direct transform and differs from the raw values. + var smoothed = series.SmoothedSeries(SmoothingFunctionType.MovingAverage, 3); + var direct = series.MovingAverage(3); + Assert.AreEqual(direct.Count, smoothed.Count); + bool anyDifferent = false; + for (int i = 0; i < direct.Count; i++) + { + Assert.AreEqual(direct[i].Value, smoothed[i].Value, 0d); + if (!double.IsNaN(smoothed[i].Value) && smoothed[i].Value != series[i].Value) + anyDifferent = true; + } + Assert.IsTrue(anyDifferent, "Smoothing must change the diagnostic value scale."); + + // A period of 1 is an identity clone for moving average and moving sum. + var identity = series.SmoothedSeries(SmoothingFunctionType.MovingAverage, 1); + for (int i = 0; i < series.Count; i++) + Assert.AreEqual(series[i].Value, identity[i].Value, 0d); + + // Moving sum and differencing route to their transforms. + var movingSum = series.SmoothedSeries(SmoothingFunctionType.MovingSum, 3); + var directSum = series.MovingSum(3); + for (int i = 0; i < directSum.Count; i++) + Assert.AreEqual(directSum[i].Value, movingSum[i].Value, 0d); + var difference = series.SmoothedSeries(SmoothingFunctionType.Difference, 1); + var directDifference = series.Difference(1); + for (int i = 0; i < directDifference.Count; i++) + Assert.AreEqual(directDifference[i].Value, difference[i].Value, 0d); + + // None clones the series. + var none = series.SmoothedSeries(SmoothingFunctionType.None); + for (int i = 0; i < series.Count; i++) + Assert.AreEqual(series[i].Value, none[i].Value, 0d); + } + } } From fa91884289c10ef0a600df31080f06f5d1a0c9de Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 28 Aug 2026 10:03:08 -0600 Subject: [PATCH 153/222] Expose seeded scrambled Sobol driving points on the Vegas integrator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vegas gains a nullable SobolSeed: null (the default) keeps the historical unrandomized driving sequence bit-for-bit, while a seed rebuilds the driver as a Matousek-scrambled SobolSequence(dimensions, seed) from its start — restoring a caller's reproducibility contract that the unrandomized sequence, being seed-independent, cannot honor. Assigning rebuilds the sequence, so callers set the seed before integrating; the sequence then continues across warm-up and recording calls exactly as the unrandomized one does, and the property applies only while UseSobolSequence is true. Tests: the default's bit-inertness (an explicit null seed reproduces the untouched integrator exactly), seeded reproducibility and divergence, and the tail-focus Jacobian identity under the seeded driver — the heavy-tail integral stays unbiased and the handed weights stay on the domain measure per evaluation batch at every tail-focus setting, with identical seeds reproducing bit-for-bit. --- Numerics/Mathematics/Integration/Vegas.cs | 22 +++++++- .../Mathematics/Integration/Test_Vegas.cs | 32 +++++++++++ .../Test_VegasTailFocusJacobian.cs | 54 +++++++++++++++++++ 3 files changed, 107 insertions(+), 1 deletion(-) diff --git a/Numerics/Mathematics/Integration/Vegas.cs b/Numerics/Mathematics/Integration/Vegas.cs index 9f844854..8f663afc 100644 --- a/Numerics/Mathematics/Integration/Vegas.cs +++ b/Numerics/Mathematics/Integration/Vegas.cs @@ -86,6 +86,7 @@ public Vegas(Func function, int dimensions, IList function, int dimensions, IList - /// Determines whether to use a Sobol sequence or a pseudo-Random number generator. + /// Determines whether to use a Sobol sequence or a pseudo-Random number generator. /// public bool UseSobolSequence { get; set; } = true; + /// + /// The seed for Matousek-scrambled Sobol driving points, or null (the default) for the + /// unrandomized sequence — the seeded scrambling restores a caller's reproducibility + /// contract that the unrandomized sequence, being seed-independent, cannot honor. + /// Assigning rebuilds the driving sequence from its start, so set it before + /// integrating; the sequence then continues across warm-up and recording calls exactly + /// as the unrandomized one does. Applies only while is + /// true. + /// + public int? SobolSeed + { + get { return _sobolSeed; } + set + { + _sobolSeed = value; + _sobol = value.HasValue ? new SobolSequence(Dimensions, value.Value) : new SobolSequence(Dimensions); + } + } + /// /// Determines whether to check convergence and exit when integrating. /// diff --git a/Test_Numerics/Mathematics/Integration/Test_Vegas.cs b/Test_Numerics/Mathematics/Integration/Test_Vegas.cs index f81bd77f..a1d33b50 100644 --- a/Test_Numerics/Mathematics/Integration/Test_Vegas.cs +++ b/Test_Numerics/Mathematics/Integration/Test_Vegas.cs @@ -326,6 +326,38 @@ public void Test_HighDimension() Assert.AreEqual(1d, vegas.Result, 0.01d); } + + /// + /// Test the seeded scrambled-Sobol driver's default inertness and reproducibility: an + /// explicit null seed reproduces the unrandomized default bit-for-bit, identical seeds + /// reproduce each other, distinct seeds and the unrandomized sequence all diverge, and + /// every configuration lands on the analytic integral. + /// + [TestMethod] + public void Test_SobolSeed_DefaultInert_And_Reproducible() + { + static double Run(int? seed, bool assign) + { + var vegas = new Vegas((x, w) => x[0] * x[0] + x[1] * x[1], 2, + new[] { 0d, 0d }, new[] { 1d, 1d }); + if (assign) vegas.SobolSeed = seed; + vegas.Integrate(); + return vegas.Result; + } + + double untouched = Run(null, assign: false); + double explicitNull = Run(null, assign: true); + double seeded = Run(123, assign: true); + double seededRepeat = Run(123, assign: true); + double seededOther = Run(456, assign: true); + + Assert.AreEqual(untouched, explicitNull, 0d, "An explicit null seed must reproduce the unrandomized default bit-for-bit."); + Assert.AreEqual(seeded, seededRepeat, 0d, "Identical Sobol seeds must reproduce bit-for-bit."); + Assert.AreNotEqual(seeded, seededOther, "Distinct Sobol seeds must diverge."); + Assert.AreNotEqual(untouched, seeded, "A scrambled sequence must diverge from the unrandomized one."); + Assert.AreEqual(2d / 3d, untouched, 0.01d); + Assert.AreEqual(2d / 3d, seeded, 0.01d); + } } } diff --git a/Test_Numerics/Mathematics/Integration/Test_VegasTailFocusJacobian.cs b/Test_Numerics/Mathematics/Integration/Test_VegasTailFocusJacobian.cs index 0116765f..e01c77e5 100644 --- a/Test_Numerics/Mathematics/Integration/Test_VegasTailFocusJacobian.cs +++ b/Test_Numerics/Mathematics/Integration/Test_VegasTailFocusJacobian.cs @@ -78,6 +78,60 @@ public void Test_WeightSum_EqualsDomainVolumeAtEveryGamma() } } + /// + /// Runs the same heavy-tail integrand driven by seeded scrambled Sobol points. + /// + /// The power-transform tail-focus parameter γ. + /// The scrambled-Sobol driving seed. + /// The integral estimate, the total weight handed out, and the call count. + private static (double Result, double WeightSum, long Calls) RunSobol(double gamma, int seed) + { + double weightSum = 0d; + long calls = 0; + var vegas = new Vegas((x, w) => + { + weightSum += w; + calls++; + return 21d * Math.Pow(1d - x[0], 20d); + }, 1, new[] { 0d }, new[] { 1d }) + { + UseSobolSequence = true, + SobolSeed = seed, + TailFocusParameter = gamma, + IndependentEvaluations = 10, + FunctionCalls = 10000, + }; + vegas.Integrate(); + Assert.AreEqual(IntegrationStatus.Success, vegas.Status, $"γ = {gamma}: the seeded-Sobol integration must succeed."); + return (vegas.Result, weightSum, calls); + } + + /// + /// Test that the seeded scrambled-Sobol driver keeps the tail-focused integral unbiased + /// and the handed weights on the domain measure at every γ — the Jacobian identity is + /// driver-independent — while restoring reproducibility: identical seeds reproduce the + /// estimate bit-for-bit and distinct seeds diverge (the property the unrandomized + /// sequence, being seed-independent, cannot honor). + /// + [TestMethod] + public void Test_SobolSeed_UnbiasedWeightsAndReproducibleAtEveryGamma() + { + foreach (double gamma in new[] { 1d, 4d, 10d }) + { + var run = RunSobol(gamma, 12345); + Assert.AreEqual(1d, run.Result, 0.03d, $"γ = {gamma}: the seeded-Sobol tail-focused integral must stay unbiased."); + double volumePerBatch = run.WeightSum / (run.Calls / 10000d); + Assert.AreEqual(1d, volumePerBatch, 0.1d, + $"γ = {gamma}: the seeded-Sobol weights must sum to the domain volume per evaluation batch."); + } + + var first = RunSobol(4d, 12345); + var repeat = RunSobol(4d, 12345); + var other = RunSobol(4d, 54321); + Assert.AreEqual(first.Result, repeat.Result, 0d, "Identical Sobol seeds must reproduce bit-for-bit."); + Assert.AreNotEqual(first.Result, other.Result, "Distinct Sobol seeds must diverge."); + } + /// /// Test that the tail-focus parameter and the rare-event configuration reject /// non-finite or non-positive values, leaving the configured state unchanged. From f0e0e6d3848ca2039e040c9764d2d94158c09b65 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 28 Aug 2026 10:32:01 -0600 Subject: [PATCH 154/222] Make ParallelMean genuinely parallel with a deterministic fixed-chunk reduction The earlier change delegated to the sequential mean, which was reproducible but abandoned the parallelism the method is named for. Large samples are now split into a fixed number of chunks with balanced ranges - never derived from the processor count - each summed sequentially into its own slot in parallel and merged serially in chunk order, matching the bootstrap's jackknife reduction. The summation tree depends only on the sample length, so the result is bit-identical on every machine, core count, and scheduler; a shared Tools.ParallelAdd accumulator would be race-free but commits additions in thread-scheduler order, which is the same non-reproducibility PLINQ had. Small samples fall through to the sequential mean. A test recomputes the exact 64-chunk tree sequentially and requires bitwise agreement from the parallel path on a magnitude-spanning 100,000-value sample. --- Numerics/Data/Statistics/Statistics.cs | 57 +++++++++++++++--- .../Data/Statistics/Test_Statistics.cs | 58 ++++++++++++++++++- 2 files changed, 103 insertions(+), 12 deletions(-) diff --git a/Numerics/Data/Statistics/Statistics.cs b/Numerics/Data/Statistics/Statistics.cs index b09573ea..5aa7cee6 100644 --- a/Numerics/Data/Statistics/Statistics.cs +++ b/Numerics/Data/Statistics/Statistics.cs @@ -136,22 +136,61 @@ public static double Mean(IList data) } /// - /// Computes the arithmetic sample mean from the unsorted data array. + /// The number of accumulation chunks used by the parallel mean reduction. Fixed, not derived + /// from the processor count, so the summation order — and therefore the mean's last bits — + /// does not vary with the machine or the thread count. Matches the bootstrap's jackknife + /// reduction. + /// + private const int ParallelMeanChunks = 64; + + /// + /// The sample size below which the parallel mean falls through to the sequential mean: + /// scheduling a parallel loop costs more than summing this few values, and the sequential + /// result is then bitwise identical to . + /// + private const int ParallelMeanSequentialThreshold = 8192; + + /// + /// Computes the arithmetic sample mean from the unsorted data array using a parallel + /// reduction with a deterministic summation order. /// Returns NaN if data is empty or any entry is NaN. /// /// Sample of data, no sorting is assumed. /// - /// This method delegates to and is retained for API - /// compatibility. The former PLINQ implementation summed per-partition and combined the - /// partial sums, so the result depended on the partition count the runtime chose from the - /// processor count; because floating-point addition is not associative, the same data could - /// produce different last bits on different machines. A sequential sum is bit-reproducible - /// everywhere, and the arrays this method sees are far too small for parallel summation to - /// pay for its overhead. + /// Large samples are split into a fixed number of chunks with balanced ranges, each chunk is + /// summed sequentially into its own slot in parallel, and the chunk sums are combined + /// serially in chunk order — the same deterministic reduction the bootstrap's jackknife + /// accumulation uses. The summation tree therefore depends only on the sample length, so the + /// result is bit-identical on every machine, core count, and scheduler. The former PLINQ + /// implementation partitioned by the processor count, so the same data produced different + /// last bits on different machines; a shared accumulator such as Tools.ParallelAdd + /// would be race-free but commits its additions in thread-scheduler order, which is the same + /// non-reproducibility. Samples below the threshold fall through to the sequential + /// . /// public static double ParallelMean(IList data) { - return Mean(data); + if (data == null) throw new ArgumentNullException(nameof(data)); + int n = data.Count; + if (n == 0) return double.NaN; + if (n < ParallelMeanSequentialThreshold) return Mean(data); + + int chunks = Math.Min(ParallelMeanChunks, n); + var chunkSums = new double[chunks]; + Parallel.For(0, chunks, c => + { + int start = (int)((long)c * n / chunks); + int end = (int)((long)(c + 1) * n / chunks); + double sum = 0d; + for (int i = start; i < end; i++) + sum += data[i]; + chunkSums[c] = sum; + }); + + double total = 0d; + for (int c = 0; c < chunks; c++) + total += chunkSums[c]; + return total / n; } /// diff --git a/Test_Numerics/Data/Statistics/Test_Statistics.cs b/Test_Numerics/Data/Statistics/Test_Statistics.cs index f1c3538b..4ef382b7 100644 --- a/Test_Numerics/Data/Statistics/Test_Statistics.cs +++ b/Test_Numerics/Data/Statistics/Test_Statistics.cs @@ -84,13 +84,15 @@ public void Test_ParallelMean() } /// - /// Verify that ParallelMean is bitwise identical to the sequential Mean. + /// Verify that ParallelMean is bitwise identical to the sequential Mean below the parallel + /// threshold. /// /// /// The former PLINQ implementation combined per-partition sums in a machine-dependent /// order, so its last bits varied with the processor count (measured up to 29 ULP from the - /// sequential sum at n = 100,000). The method now delegates to the sequential mean, so the - /// two must agree exactly on a magnitude-spanning sample, not merely to a tolerance. + /// sequential sum at n = 100,000) — PLINQ partitions even at n = 16. Samples below the + /// fixed sequential threshold now fall through to the sequential mean, so the two must + /// agree exactly on a magnitude-spanning sample, not merely to a tolerance. /// [TestMethod] public void Test_ParallelMean_MatchesSequentialMeanExactly() @@ -108,6 +110,56 @@ public void Test_ParallelMean_MatchesSequentialMeanExactly() Assert.AreEqual(sequential, parallel, 0d); } + /// + /// Verify that the large-sample parallel reduction is bit-reproducible and matches the + /// fixed-chunk summation order exactly. + /// + /// + /// Above the sequential threshold the mean is computed over a fixed number of chunks — + /// never derived from the processor count — each summed sequentially and merged serially + /// in chunk order, the same deterministic reduction the bootstrap's jackknife accumulation + /// uses. This test recomputes that exact summation tree sequentially and requires bitwise + /// agreement, which proves the parallel result is independent of the scheduler: a + /// scheduler-ordered accumulator (PLINQ or a shared Tools.ParallelAdd) cannot reproduce a + /// fixed tree on a magnitude-spanning sample. Repeated calls must also agree exactly. + /// + [TestMethod] + public void Test_ParallelMean_LargeSample_MatchesFixedChunkOrderExactly() + { + var data = new double[100000]; + for (int i = 0; i < data.Length; i++) + { + // Deterministic values spanning several orders of magnitude so any + // reassociation of the summation order would change the last bits. + data[i] = Math.Pow(10d, (i % 9) - 4) * (1d + i / 99991d); + } + + // The fixed-chunk reference: 64 chunks with balanced ranges, summed sequentially and + // merged in chunk order — the summation tree ParallelMean must reproduce. + const int chunks = 64; + var chunkSums = new double[chunks]; + for (int c = 0; c < chunks; c++) + { + int start = (int)((long)c * data.Length / chunks); + int end = (int)((long)(c + 1) * data.Length / chunks); + double sum = 0d; + for (int i = start; i < end; i++) + sum += data[i]; + chunkSums[c] = sum; + } + double total = 0d; + for (int c = 0; c < chunks; c++) + total += chunkSums[c]; + double expected = total / data.Length; + + double first = Numerics.Data.Statistics.Statistics.ParallelMean(data); + double second = Numerics.Data.Statistics.Statistics.ParallelMean(data); + Assert.AreEqual(expected, first, 0d, "The parallel reduction must follow the fixed chunk order."); + Assert.AreEqual(first, second, 0d, "Repeated calls must be bit-identical."); + Assert.AreEqual(Numerics.Data.Statistics.Statistics.Mean(data), first, + Math.Abs(first) * 1E-12, "The chunked mean must agree with the sequential mean to rounding."); + } + /// /// Test the GeometricMean method against R's "geometric.mean()" method from the "psych" package. /// From cce9f529a60cd6911047ed0c57fee23abfc8c252 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 28 Aug 2026 12:28:52 -0600 Subject: [PATCH 155/222] Preserve the cumulative-sum interval and align the indexed overload guards --- Numerics/Data/Time Series/TimeSeries.cs | 16 ++-- .../Data/Time Series/Test_TimeSeries.cs | 77 +++++++++++++++++++ 2 files changed, 87 insertions(+), 6 deletions(-) diff --git a/Numerics/Data/Time Series/TimeSeries.cs b/Numerics/Data/Time Series/TimeSeries.cs index 75a6e87b..8230e529 100644 --- a/Numerics/Data/Time Series/TimeSeries.cs +++ b/Numerics/Data/Time Series/TimeSeries.cs @@ -407,12 +407,14 @@ public void LogTransform(double baseValue = 10) /// /// List of integer index values (0 based) for each ordinate in the time series to apply the calculation to. /// The log base value. + /// Out-of-range indexes are skipped. public void LogTransform(IList indexes, double baseValue = 10) { SuppressCollectionChanged = true; for (int i = 0; i < indexes.Count; i++) { - if (indexes[i] >= 0 && indexes[i] < Count && this[indexes[i]].Value > 0 && (!double.IsNaN(this[indexes[i]].Value))) { this[indexes[i]].Value = Math.Log(this[indexes[i]].Value, baseValue); } + if (indexes[i] < 0 || indexes[i] >= Count) { continue; } + if (this[indexes[i]].Value > 0 && (!double.IsNaN(this[indexes[i]].Value))) { this[indexes[i]].Value = Math.Log(this[indexes[i]].Value, baseValue); } else { this[indexes[i]].Value = double.NaN; } } SuppressCollectionChanged = false; @@ -456,15 +458,17 @@ public void Inverse() /// /// Specified values in the time-series are replaced by their inverse (1/x). Missing values are kept as missing. If the value is 0.0, the value is set to Double.NaN. - /// List of integer index values (0 based) for each ordinate in the time series to apply the inverse calculation to. /// + /// List of integer index values (0 based) for each ordinate in the time series to apply the inverse calculation to. + /// Out-of-range indexes are skipped. public void Inverse(IList indexes) { SuppressCollectionChanged = true; for (int i = 0; i < indexes.Count; i++) { - if (indexes[i] >= 0 && indexes[i] < Count && this[indexes[i]].Value != 0 && !double.IsNaN(this[indexes[i]].Value)) { this[indexes[i]].Value = 1d / this[indexes[i]].Value; } - else if (this[indexes[i]].Value == 0 || double.IsNaN(this[indexes[i]].Value)) { this[indexes[i]].Value = double.NaN; } + if (indexes[i] < 0 || indexes[i] >= Count) { continue; } + if (this[indexes[i]].Value != 0 && !double.IsNaN(this[indexes[i]].Value)) { this[indexes[i]].Value = 1d / this[indexes[i]].Value; } + else { this[indexes[i]].Value = double.NaN; } } SuppressCollectionChanged = false; RaiseCollectionChangedReset(); @@ -475,7 +479,7 @@ public void Inverse(IList indexes) /// public TimeSeries CumulativeSum() { - var timeSeries = new TimeSeries(); + var timeSeries = new TimeSeries(TimeInterval); double sum = 0d; for (int i = 0; i < Count; i++) { @@ -650,7 +654,7 @@ public void InterpolateMissingData(int maxNumberOfMissing, IList indexes) break; } // the extrapolation case - if (j == Count - 1) + if (j == Count - 1 && idx >= 2) { x1 = this[idx - 2].Index.ToOADate(); x2 = this[idx - 1].Index.ToOADate(); diff --git a/Test_Numerics/Data/Time Series/Test_TimeSeries.cs b/Test_Numerics/Data/Time Series/Test_TimeSeries.cs index ef2ecfad..9d882cae 100644 --- a/Test_Numerics/Data/Time Series/Test_TimeSeries.cs +++ b/Test_Numerics/Data/Time Series/Test_TimeSeries.cs @@ -227,6 +227,56 @@ public void Test_Math() Equal(ts, values); } + /// + /// Verifies that the indexed log transform skips out-of-range indexes like its sibling overloads. + /// + [TestMethod] + public void Test_LogTransform_Indexed_SkipsOutOfRangeIndexes() + { + var ts = new TimeSeries(TimeInterval.OneDay, new DateTime(2023, 01, 01), new double[] { 10, 100, 1000 }); + ts.LogTransform(new[] { 1, 7, -1 }); + Assert.AreEqual(10d, ts[0].Value, 1E-6); + Assert.AreEqual(2d, ts[1].Value, 1E-6); + Assert.AreEqual(1000d, ts[2].Value, 1E-6); + } + + /// + /// Verifies that the indexed log transform still marks in-range non-positive values as missing. + /// + [TestMethod] + public void Test_LogTransform_Indexed_MarksNonPositiveInRangeValuesMissing() + { + var ts = new TimeSeries(TimeInterval.OneDay, new DateTime(2023, 01, 01), new double[] { -5, 100, 1000 }); + ts.LogTransform(new[] { 0, 1 }); + Assert.IsTrue(double.IsNaN(ts[0].Value)); + Assert.AreEqual(2d, ts[1].Value, 1E-6); + } + + /// + /// Verifies that the indexed inverse skips out-of-range indexes like its sibling overloads. + /// + [TestMethod] + public void Test_Inverse_Indexed_SkipsOutOfRangeIndexes() + { + var ts = new TimeSeries(TimeInterval.OneDay, new DateTime(2023, 01, 01), new double[] { 2, 4, 8 }); + ts.Inverse(new[] { 0, 5, -2 }); + Assert.AreEqual(0.5, ts[0].Value, 1E-6); + Assert.AreEqual(4d, ts[1].Value, 1E-6); + Assert.AreEqual(8d, ts[2].Value, 1E-6); + } + + /// + /// Verifies that the indexed inverse still marks in-range zero and missing values as missing. + /// + [TestMethod] + public void Test_Inverse_Indexed_MarksZeroInRangeValuesMissing() + { + var ts = new TimeSeries(TimeInterval.OneDay, new DateTime(2023, 01, 01), new double[] { 0, 4, 8 }); + ts.Inverse(new[] { 0, 2 }); + Assert.IsTrue(double.IsNaN(ts[0].Value)); + Assert.AreEqual(0.125, ts[2].Value, 1E-6); + } + /// /// Test the CumulativeSum method /// @@ -243,6 +293,17 @@ public void Test_Cumulative() } + /// + /// Verifies that the cumulative sum preserves the source series' time interval. + /// + [TestMethod] + public void Test_Cumulative_PreservesTimeInterval() + { + var ts = new TimeSeries(TimeInterval.OneMonth, new DateTime(2023, 01, 01), new double[] { 22, 16, 33, 5, 12, 36, 48, 10, 18, 15, 22, 13 }); + var newTS = ts.CumulativeSum(); + Assert.AreEqual(ts.TimeInterval, newTS.TimeInterval); + } + /// /// Test the successive Difference method /// @@ -287,6 +348,22 @@ public void Test_Missing() Assert.AreEqual(8.9, ts[11].Value, 1E-6); } + /// + /// Verifies that the indexed interpolation matches its non-indexed twin at the series start instead of reading before the first ordinate. + /// + [TestMethod] + public void Test_InterpolateMissingData_Indexed_MatchesTwinAtSeriesStart() + { + var indexed = new TimeSeries(TimeInterval.OneDay, new DateTime(2023, 01, 01), new double[] { 1, double.NaN, double.NaN }); + var plain = new TimeSeries(TimeInterval.OneDay, new DateTime(2023, 01, 01), new double[] { 1, double.NaN, double.NaN }); + indexed.InterpolateMissingData(1, new[] { 1 }); + plain.InterpolateMissingData(1); + for (int i = 0; i < plain.Count; i++) + { + Assert.AreEqual(plain[i].Value, indexed[i].Value); + } + } + /// /// Test the method that adds different interval times /// From 79aedf690c94ce895db85e2ffc0905546f00393e Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 28 Aug 2026 12:28:52 -0600 Subject: [PATCH 156/222] Satisfy the MSTEST0037 analyzer in the remaining comparison asserts --- Test_Numerics/Distributions/Univariate/Test_LogNormal.cs | 4 ++-- .../Distributions/Univariate/Test_LogPearsonTypeIII.cs | 4 ++-- Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Test_Numerics/Distributions/Univariate/Test_LogNormal.cs b/Test_Numerics/Distributions/Univariate/Test_LogNormal.cs index 7cde7b49..5adff3ef 100644 --- a/Test_Numerics/Distributions/Univariate/Test_LogNormal.cs +++ b/Test_Numerics/Distributions/Univariate/Test_LogNormal.cs @@ -300,8 +300,8 @@ public void Test_LogNormal_ParameterConstraints_AllowNegativeLogMean() var initials = constraints.Item1; var lowers = constraints.Item2; var uppers = constraints.Item3; - Assert.IsTrue(initials[0] < 0d, "Fixture precondition: the log10 mean is negative."); - Assert.IsTrue(lowers[0] < uppers[0], "The mean bounds must not be inverted."); + Assert.IsLessThan(0d, initials[0], "Fixture precondition: the log10 mean is negative."); + Assert.IsLessThan(uppers[0], lowers[0], "The mean bounds must not be inverted."); Assert.IsTrue(initials[0] >= lowers[0] && initials[0] <= uppers[0], "The initial mean must sit inside its own bounds."); Assert.IsTrue(lowers[1] < uppers[1] && initials[1] >= lowers[1] && initials[1] <= uppers[1], diff --git a/Test_Numerics/Distributions/Univariate/Test_LogPearsonTypeIII.cs b/Test_Numerics/Distributions/Univariate/Test_LogPearsonTypeIII.cs index 7844d234..149ee74c 100644 --- a/Test_Numerics/Distributions/Univariate/Test_LogPearsonTypeIII.cs +++ b/Test_Numerics/Distributions/Univariate/Test_LogPearsonTypeIII.cs @@ -398,8 +398,8 @@ public void Test_LP3_ParameterConstraints_AllowNegativeLogMean() var initials = constraints.Item1; var lowers = constraints.Item2; var uppers = constraints.Item3; - Assert.IsTrue(initials[0] < 0d, "Fixture precondition: the log10 mean is negative."); - Assert.IsTrue(lowers[0] < uppers[0], "The mean bounds must not be inverted."); + Assert.IsLessThan(0d, initials[0], "Fixture precondition: the log10 mean is negative."); + Assert.IsLessThan(uppers[0], lowers[0], "The mean bounds must not be inverted."); Assert.IsTrue(initials[0] >= lowers[0] && initials[0] <= uppers[0], "The initial mean must sit inside its own bounds."); Assert.AreEqual(double.NegativeInfinity, new LogPearsonTypeIII().MinimumOfParameters[0]); diff --git a/Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs b/Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs index deeee97a..076b45c7 100644 --- a/Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs +++ b/Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs @@ -144,7 +144,7 @@ public void Test_GMM_MStep_PositiveDefiniteRepairIsApplied() double wgt = 0d; for (int i = 0; i < n; i++) wgt += gmm.LikelihoodMatrix[i, k]; - Assert.IsTrue(wgt > 0, "Fixture precondition: both components carry responsibility."); + Assert.IsGreaterThan(0d, wgt, "Fixture precondition: both components carry responsibility."); // Recompute the raw M-step covariance from the responsibilities and stored means. var expected = new double[dims, dims]; From 06f17d630f87618dc82e11a88c58d040cd4c22ff Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 28 Aug 2026 12:55:55 -0600 Subject: [PATCH 157/222] Align the guides with the rank-normalized diagnostics and index the functions pages --- README.md | 3 +- docs/distributions/copulas.md | 2 +- docs/distributions/univariate.md | 2 +- docs/functions/index.md | 13 ++-- docs/index.md | 3 + docs/mathematics/optimization.md | 18 +++--- docs/sampling/convergence-diagnostics.md | 75 ++++++++++++++++-------- docs/sampling/mcmc.md | 6 +- 8 files changed, 80 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index e0686dc2..e510da8d 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,9 @@ Or search for [RMC.Numerics](https://www.nuget.org/packages/RMC.Numerics/) in th | [Data](docs/data/interpolation.md) | Interpolation, linear regression, time series analysis | | [Statistics](docs/statistics/descriptive.md) | Descriptive and weighted statistics, goodness-of-fit metrics, hypothesis tests, global sensitivity analysis | | [Distributions](docs/distributions/univariate.md) | 43 univariate distributions, parameter estimation, uncertainty analysis, copulas, multivariate distributions | +| [Functions](docs/functions/index.md) | Uncertain univariate function forms, composition, posterior ensembles, link functions | | [Machine Learning](docs/machine-learning/machine-learning.md) | GLM, decision trees, random forests, KNN, naive Bayes, k-means, GMM | -| [Sampling](docs/sampling/mcmc.md) | MCMC (RWMH, ARWMH, DE-MCz, HMC, NUTS, Gibbs), random generation, scrambled quasi-random sequences, convergence diagnostics | +| [Sampling](docs/sampling/mcmc.md) | MCMC (RWMH, ARWMH, DE-MCz, DE-MCzs, HMC, NUTS, Gibbs), random generation, scrambled quasi-random sequences, convergence diagnostics | | [References](docs/references.md) | Consolidated bibliography | ## Prerequisites diff --git a/docs/distributions/copulas.md b/docs/distributions/copulas.md index 54a80b0f..15878be6 100644 --- a/docs/distributions/copulas.md +++ b/docs/distributions/copulas.md @@ -363,7 +363,7 @@ where $u$ and $v$ are non-exceedance probabilities. Every copula exposes it dire | Student-t | $T_{\nu+1}\!\left(\dfrac{x_2 - \rho x_1}{s}\right)$, with $x_i = T_\nu^{-1}(\cdot)$, $s = \sqrt{\frac{(1-\rho^2)(\nu + x_1^2)}{\nu+1}}$ | | Independence | $v$ | -The scalar `InverseConditionalCDF(u, t)` inverts the h-function in $v$ without allocating, and the array form `InverseCDF(u, t)` returns the pair `[u, v]` on top of it — the conditional-simulation surface. The base-class `ConditionalCDF` also provides a central-finite-difference fallback over `CDF` for external copula subclasses that predate the analytic surface. +The scalar `InverseConditionalCDF(u, t)` inverts the h-function in $v$ without allocating the array that `InverseCDF` returns (the Student-t copula is the exception: it allocates two Student's t distribution objects per call), and the array form `InverseCDF(u, t)` returns the pair `[u, v]` on top of it — the conditional-simulation surface. The base-class `ConditionalCDF` also provides a central-finite-difference fallback over `CDF`, so an external subclass that implements only `CDF` still gets a correct approximation. ```cs // Observed flow diff --git a/docs/distributions/univariate.md b/docs/distributions/univariate.md index b7726ea2..a58c7fe2 100644 --- a/docs/distributions/univariate.md +++ b/docs/distributions/univariate.md @@ -2,7 +2,7 @@ [← Previous: Hypothesis Tests](../statistics/hypothesis-tests.md) | [Back to Index](../index.md) | [Next: Parameter Estimation →](parameter-estimation.md) -The ***Numerics*** library provides over 40 univariate probability distributions for statistical analysis, risk assessment, and uncertainty quantification. All distributions implement a common interface with consistent methods for computing probability density functions (PDF), cumulative distribution functions (CDF), quantiles, and statistical moments. +The ***Numerics*** library provides 43 univariate probability distributions for statistical analysis, risk assessment, and uncertainty quantification. All distributions implement a common interface with consistent methods for computing probability density functions (PDF), cumulative distribution functions (CDF), quantiles, and statistical moments. ## Available Distributions diff --git a/docs/functions/index.md b/docs/functions/index.md index a06e099b..efe6fbc2 100644 --- a/docs/functions/index.md +++ b/docs/functions/index.md @@ -37,16 +37,19 @@ parameter vector is `[h₁, log₁₀α₁, β₁, h₂, log₁₀α₂, β₂, a fitted posterior `ParameterSet.Values` applies directly through `SetParameters`. Breakpoints must be strictly ordered, exponents positive (the monotonicity constraint behind the numeric Brent inverse), discharge is zero at and below the cease-to-flow stage `h₁`, and one -segment degenerates to the plain `PowerFunction`. +segment degenerates deterministically to the plain `PowerFunction` (the stochastic residual +spaces differ: log₁₀ here versus natural log in `PowerFunction`). ```cs using Numerics.Functions; // Two controls: main channel from stage 1, overbank activating at stage 3. var rating = new SegmentedPowerFunction(new[] { 1.0, 1.5, 2.0, 3.0, 1.2, 1.5, 0.1 }); -double q = rating.Function(5.0); // deterministic (mean) discharge -rating.IsDeterministic = false; -rating.ConfidenceLevel = 0.75; // multiplies by 10^(z·σ) +double q = rating.Function(5.0); // deterministic (median) curve: ConfidenceLevel + // defaults to -1, and any value outside [0, 1] + // selects the deterministic curve +rating.ConfidenceLevel = 0.75; // 75th-percentile curve via the log₁₀-space + // residual: multiplies by 10^(z·σ) double q75 = rating.Function(5.0); ``` @@ -57,7 +60,7 @@ double q75 = rating.Function(5.0); driving every child co-monotonically. The **mixture** mode composes with a single uniform: the draw selects a child by cumulative weight and re-scales the remainder as the child's own draw — deterministic composition sampling with no internal random source. Outside [0, 1] both modes -evaluate the weighted average of the child means. +evaluate the weighted average of each child evaluated at its own configured confidence level. ## Serialization and the factory diff --git a/docs/index.md b/docs/index.md index 8f6981fc..3357fe1a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -152,6 +152,8 @@ var results = sampler.Output; | [Uncertainty Analysis](distributions/uncertainty-analysis.md) | Bootstrap and confidence intervals | | [Copulas](distributions/copulas.md) | Dependency modeling with copulas | | [Multivariate Distributions](distributions/multivariate.md) | Multivariate Normal, Student-t, Dirichlet, Multinomial | +| **Functions** | | +| [Univariate Functions](functions/index.md) | Uncertain univariate function forms, composition, posterior ensembles, and link functions | | **Machine Learning** | | | [Machine Learning](machine-learning/machine-learning.md) | Supervised and unsupervised learning algorithms | | **Sampling** | | @@ -165,6 +167,7 @@ var results = sampler.Output; | Namespace | Description | |-----------|-------------| | `Numerics.Distributions` | Probability distributions and copulas | +| `Numerics.Functions` | Uncertain univariate functions, posterior ensembles, and link functions | | `Numerics.Data.Statistics` | Statistical functions and tests | | `Numerics.Data` | Interpolation methods, linear regression, time series data structures | | `Numerics.Mathematics` | Base namespace for mathematical operations (includes NumericalDerivative) | diff --git a/docs/mathematics/optimization.md b/docs/mathematics/optimization.md index d23c0e56..cb407156 100644 --- a/docs/mathematics/optimization.md +++ b/docs/mathematics/optimization.md @@ -62,7 +62,7 @@ All optimizers in ***Numerics*** inherit from the `Optimizer` base class and sha - `Iterations`: Number of iterations performed - `FunctionEvaluations`: Number of function evaluations - `Status`: Optimization status (Success, Failure, etc.) -- `ParameterSetTrace`: Full trace of parameter evaluations +- `ParameterSetTrace`: Read-only trace of the best-so-far parameter set and fitness at every function evaluation (entries recorded between improvements share one values array) - `Hessian`: Numerically differentiated Hessian matrix (if computed) ### Methods @@ -453,10 +453,11 @@ Console.WriteLine($"Constrained optimum: [{constrained.BestParameterSet.Values[0 ## Shortest Path (Dijkstra) -The `Dijkstra`, `Network`, `Edge`, and `BinaryHeap` types solve destination-rooted shortest -paths over directed networks [9] — the routing kernel for agent-based evacuation modeling, where -every agent needs its route to the nearest destination and edge costs (travel times) change as -conditions evolve. +The `Dijkstra`, `Network`, and `Edge` types solve destination-rooted shortest paths over +directed networks [9] — the routing kernel for agent-based evacuation modeling, where every +agent needs its route to the nearest destination and edge costs (travel times) change as +conditions evolve. The solvers run on an internal indexed binary min-heap sized at the node +count; the public `BinaryHeap` is a separate standalone utility. ### The result table @@ -524,12 +525,15 @@ for (int t = 0; t < timeSteps; t++) } // Detour routing around blocked segments, splicing onto the precomputed table when possible. -List? detour = network.GetPath(blockedEdgeIndices, agentNodeIndex, table); +List detour = network.GetPath(blockedEdgeIndices, agentNodeIndex, table); ``` `Network.GetPath` finds the cheapest route to the nearest destination that avoids every edge bearing an excluded edge index; when the precomputed table's recorded route is untouched by the -exclusions it is returned directly, with no solve. +exclusions it is returned directly, with no solve. The table-based overload never returns null: +it returns an empty list when the start node is unreachable, is already a destination, or no +detour exists. The two-argument overload (without a table), by contrast, returns null when +every destination is unreachable. ## Practical Example: Calibrating a Hydrological Model diff --git a/docs/sampling/convergence-diagnostics.md b/docs/sampling/convergence-diagnostics.md index f38fa337..bd103d40 100644 --- a/docs/sampling/convergence-diagnostics.md +++ b/docs/sampling/convergence-diagnostics.md @@ -33,7 +33,7 @@ where $\pi$ is the target (posterior) distribution. This guarantee is asymptotic ## Gelman-Rubin Statistic (R̂) -The Gelman-Rubin diagnostic compares within-chain and between-chain variance [[1]](#1). Values near 1.0 indicate convergence. +The Gelman-Rubin diagnostic compares within-chain and between-chain variance [[1]](#1). Values near 1.0 indicate convergence. The ***Numerics*** implementation computes the rank-normalized split-$\hat{R}$ with folding of Vehtari et al. (2021) [[4]](#4), which is robust to heavy tails and sensitive to both location and scale differences between chains. ### Computing R̂ @@ -60,9 +60,9 @@ for (int i = 0; i < rHat.Length; i++) { Console.WriteLine($" Parameter {i}: R̂ = {rHat[i]:F4}"); - if (rHat[i] < 1.1) + if (rHat[i] < 1.01) Console.WriteLine(" ✓ Converged"); - else if (rHat[i] < 1.2) + else if (rHat[i] < 1.05) Console.WriteLine(" ⚠ Marginal - run longer"); else Console.WriteLine(" ✗ Not converged - investigate"); @@ -73,20 +73,22 @@ for (int i = 0; i < rHat.Length; i++) | R̂ Value | Interpretation | Action | |---------|---------------|--------| -| R̂ < 1.01 | Excellent convergence | Proceed | -| R̂ < 1.1 | Good convergence | Safe to use | -| 1.1 ≤ R̂ < 1.2 | Marginal | Run longer | -| R̂ ≥ 1.2 | Poor convergence | Investigate | +| R̂ < 1.01 | Converged (recommended threshold for the rank-normalized statistic [[4]](#4)) | Proceed | +| 1.01 ≤ R̂ < 1.05 | Marginal | Run longer | +| R̂ ≥ 1.05 | Poor convergence | Investigate | **Formula:** +Each retained chain is split in half, and the pooled draws are replaced by normal scores of their ranks. The classic statistic is then computed on the transformed split chains: + ```math \hat{R} = \sqrt{\frac{\hat{V}}{W}} ``` -Where $W$ is the mean within-chain variance, $B$ is the between-chain variance, and: +Where $W$ is the mean within-chain variance and $B$ is the between-chain variance, both computed from the rank-normalized split chains, and: ```math \hat{V} = \frac{n-1}{n}W + \frac{1}{n}B ``` +The same statistic is also computed on draws folded around the pooled median, and the reported value is the maximum of the two (see the derivation below). ### Mathematical Derivation @@ -132,7 +134,19 @@ This is a weighted average that has a key property: $\hat{V}$ **overestimates** Since $\hat{V} \geq W$ in general, we have $\hat{R} \geq 1$. At perfect convergence $\hat{R} = 1$; values substantially above 1 indicate that the chains have not mixed and further sampling is needed. -**Split-$\hat{R}$.** Modern practice [[4]](#4) recommends splitting each chain in half before computing $\hat{R}$, which doubles the number of chains from $m$ to $2m$. This helps detect non-stationarity *within* individual chains -- for example, a chain that drifted during the first half but settled during the second half. The ***Numerics*** implementation does not perform split-$\hat{R}$ automatically; to use this approach, split each chain manually before passing them to `GelmanRubin()`. +**Split-$\hat{R}$.** Following Vehtari et al. (2021) [[4]](#4), each retained chain is split in half automatically, doubling the number of chains from $m$ to $2m$. This helps detect non-stationarity *within* individual chains -- for example, a chain that drifted during the first half but settled during the second half. Pass whole chains to `GelmanRubin()`; chains that are split beforehand end up split a second time. + +**Rank normalization.** Before the variance comparison, the pooled draws are replaced by their normal scores: each draw's average rank $r$ is mapped through the standard normal inverse CDF using Blom's offset, + +```math +z = \Phi^{-1}\!\left(\frac{r - 3/8}{S + 1/4}\right) +``` + +where $S$ is the pooled draw count. Rank normalization makes the diagnostic robust to heavy tails and well-defined even for parameters without finite mean or variance [[4]](#4). + +**Folding.** The computation is repeated on draws folded around the pooled median, $\zeta_{jt} = |\theta_{jt} - \text{median}(\theta)|$, which is sensitive to scale differences between chains that the location-based statistic misses. The reported diagnostic is the maximum of the rank-normalized split-$\hat{R}$ and the folded rank-normalized split-$\hat{R}$. + +**Edge cases.** `GelmanRubin()` returns `NaN` for a parameter when independent-chain comparison is unavailable or degenerate: fewer than two chains, fewer than four retained draws per chain, or constant or non-finite draws. ### Common Causes of High R̂ @@ -144,7 +158,7 @@ Since $\hat{V} \geq W$ in general, we have $\hat{R} \geq 1$. At perfect converge ## Effective Sample Size (ESS) -ESS quantifies number of independent samples, accounting for autocorrelation [[2]](#2). +ESS quantifies number of independent samples, accounting for autocorrelation [[2]](#2). The ***Numerics*** implementation computes the rank-normalized ESS of Vehtari et al. (2021) [[4]](#4): the value reported for each parameter is the minimum of the bulk ESS and the tail ESS values at the 5th and 95th percentiles, so it is conservative for both central and tail summaries. ### Computing ESS @@ -165,6 +179,8 @@ if (ess < 100) Console.WriteLine("⚠ Warning: Low ESS - run longer or thin more"); ``` +The single-series overload treats the input as one chain and splits it into two half-chains, so disagreement between the first and second half lowers the estimate. Constant or non-finite draws, or fewer than 6 draws per chain, return `NaN`. + ### ESS Across All Parameters ```cs @@ -235,23 +251,34 @@ Solving for ESS: The quantity $\tau = 1 + 2\sum_{k=1}^{\infty}\rho_k$ is called the **integrated autocorrelation time**. It represents how many MCMC iterations correspond to one independent draw: $\text{ESS} = N/\tau$. -**Truncation strategy.** In practice, the infinite sum must be truncated. The ***Numerics*** implementation uses a simple truncation rule: the sum is cut off at the first lag $k$ where $\rho_k < 0$. This works because for a well-behaved MCMC chain, the autocorrelation function decays monotonically toward zero and oscillations below zero represent noise rather than genuine correlation. +**Truncation strategy.** In practice, the infinite sum must be truncated. The ***Numerics*** implementation uses Geyer's (1992) [[3]](#3) **initial positive sequence estimator**, which sums consecutive *pairs* of autocorrelations $(\rho_{2k} + \rho_{2k+1})$ and truncates at the first pair whose sum is not positive. For a reversible Markov chain these pair sums are theoretically positive, so a non-positive pair sum marks the point where the estimates are dominated by noise. The estimate is then regularized with Geyer's **initial monotone sequence** rule: any pair sum that exceeds the preceding pair sum is replaced by that preceding value, enforcing the theoretical monotone decay. Together these rules produce a stable estimate of the integrated autocorrelation time. + +**Multi-chain ESS.** When $M$ chains of length $N$ are available, each chain is first split in half, so $2M$ split chains enter the computation while the total draw count $S = N \cdot M$ is unchanged. Lag autocovariances $\hat{\gamma}_k$ are estimated for each split chain with an FFT and averaged across the split chains, and the multi-chain variance estimate combines within-chain and between-chain variability: -Geyer (1992) [[3]](#3) proposed a more robust alternative called the **initial positive sequence estimator**, which sums consecutive *pairs* of autocorrelations $(\rho_{2k} + \rho_{2k+1})$ and stops when a pair sum becomes negative. This approach is theoretically guaranteed to produce a non-negative variance estimate. The ***Numerics*** implementation uses the simpler first-negative truncation, which is adequate for chains with good mixing behavior. +```math +\widehat{\text{var}}^{+} = \frac{n-1}{n}\,W + \frac{1}{n}\,B +``` -**Multi-chain ESS.** When $M$ chains of length $N$ are available, the implementation computes the autocorrelation sum $\rho_m$ for each chain $m$ separately, then averages across chains: +where $n$ is the split-chain length and $W$ and $B$ are the within-chain and between-chain variances defined as in the Gelman-Rubin derivation. The combined autocorrelation at lag $k$, ```math -\bar{\rho} = \frac{1}{M}\sum_{m=1}^{M}\rho_m \qquad \text{where} \quad \rho_m = \sum_{k=1}^{K_m}\hat{\rho}_k^{(m)} +\hat{\rho}_k = 1 - \frac{W - \bar{\gamma}_k}{\widehat{\text{var}}^{+}} ``` -Here $K_m$ is the truncation point for chain $m$ (the first lag at which the autocorrelation is negative). The total effective sample size is then: +where $\bar{\gamma}_k$ is the averaged lag-$k$ autocovariance, is truncated with Geyer's rules above to obtain the integrated autocorrelation time $\hat{\tau}$, and: ```math -\text{ESS} = \frac{N \cdot M}{1 + 2\bar{\rho}} +\text{ESS} = \frac{S}{\hat{\tau}} ``` -This is capped at $N \cdot M$ (the total number of samples) since the effective sample size cannot exceed the actual number of draws. +The integrated autocorrelation time is bounded below by $1/\log_{10}(S)$ to keep the estimate stable, but the ESS is **not** capped at $S$: when chains are negatively autocorrelated (antithetic behavior), $\hat{\tau} < 1$ and the rank-normalized ESS can exceed the number of draws. + +**Bulk and tail ESS.** The scalar ESS reported for each parameter is a conservative summary of three components [[4]](#4): + +- **Bulk ESS** measures sampling efficiency in the body of the distribution. It is computed from the split chains after the same rank-normal transformation used for $\hat{R}$. +- **Tail ESS** measures efficiency in the tails. It is the ESS of the indicator variables $I(\theta_t \leq \hat{q}_{0.05})$ and $I(\theta_t \leq \hat{q}_{0.95})$, where $\hat{q}_{0.05}$ and $\hat{q}_{0.95}$ are the pooled 5th and 95th percentiles. + +The reported value is the minimum of the three, so slow tail exploration is not hidden by a healthy bulk estimate. ### ESS Requirements @@ -426,9 +453,9 @@ double[] rhat = MCMCDiagnostics.GelmanRubin(chains, sampler.WarmupIterations); bool converged = true; for (int i = 0; i < nParams; i++) { - string status = rhat[i] < 1.1 ? "✓" : "✗"; + string status = rhat[i] < 1.01 ? "✓" : "✗"; Console.WriteLine($" {status} θ{i}: R̂ = {rhat[i]:F4}"); - if (rhat[i] >= 1.1) converged = false; + if (rhat[i] >= 1.01) converged = false; } // Step 4: Check ESS @@ -506,11 +533,11 @@ for (int param = 0; param < nParams; param++) ## Troubleshooting Convergence Issues -### Problem: High R̂ (> 1.1) +### Problem: High R̂ (> 1.01) **Diagnosis:** ```cs -if (rhat.Max() > 1.1) +if (rhat.Max() > 1.01) { Console.WriteLine("Convergence issue detected"); Console.WriteLine("Possible causes:"); @@ -627,7 +654,7 @@ sampler.WarmupIterations = Math.Max(2000, sampler.Iterations / 2); ```cs // Always check before using samples -bool ready = (rhat.Max() < 1.1) && (ess.Min() > 100); +bool ready = (rhat.Max() < 1.01) && (ess.Min() > 100); if (!ready) { @@ -660,7 +687,7 @@ Console.WriteLine($" ESS range: [{ess.Min():F0}, {ess.Max():F0}]"); ```cs int iteration = 1; int totalIterations = sampler.Iterations; -while (rhat.Max() > 1.05 || ess.Min() < 200) +while (rhat.Max() > 1.01 || ess.Min() < 200) { totalIterations += 5000; Console.WriteLine($"\nIteration {iteration}: Restarting with {totalIterations} iterations..."); @@ -689,7 +716,7 @@ while (rhat.Max() > 1.05 || ess.Min() < 200) | Diagnostic | Target | Action if Not Met | |------------|--------|------------------| -| **R̂** | < 1.1 | Increase warmup, run longer | +| **R̂** | < 1.01 | Increase warmup, run longer | | **ESS** | > 100 (per param) | Increase iterations, improve mixing | | **Visual traces** | Stationary | Check initialization, try different sampler | | **ACF** | Drops quickly | Increase thinning, better sampler | diff --git a/docs/sampling/mcmc.md b/docs/sampling/mcmc.md index 76663798..eaedb887 100644 --- a/docs/sampling/mcmc.md +++ b/docs/sampling/mcmc.md @@ -320,7 +320,7 @@ where: - $d$ is the number of parameters (`NumberOfParameters`) - $\beta = 0.05$ by default (the `Beta` property) -- $\hat{\Sigma}_t$ is the empirical covariance matrix computed as a running covariance of accepted samples (and current states after warmup) +- $\hat{\Sigma}_t$ is the empirical covariance matrix computed as a running covariance of every realized chain state (accepted proposals and repeated retained states alike) - $I_d$ is the $d$-dimensional identity matrix - The scale factor $s = 2.38^2/d$ is the `Scale` property @@ -905,7 +905,7 @@ if (results.MarkovChains != null) | `StandardDeviation` | `double` | Posterior standard deviation | | `LowerCI` | `double` | Lower confidence interval (default 5th percentile) | | `UpperCI` | `double` | Upper confidence interval (default 95th percentile) | -| `Rhat` | `double` | Gelman-Rubin convergence diagnostic | +| `Rhat` | `double` | Gelman-Rubin convergence diagnostic (rank-normalized split-R̂; target < 1.01) | | `ESS` | `double` | Effective sample size | | `N` | `int` | Total sample count | @@ -1006,7 +1006,7 @@ Do you have gradient information (or a smooth, differentiable log-posterior)? ```cs // Visual inspection of traces -// Check R-hat < 1.1 for all parameters +// Check R-hat < 1.01 for all parameters // Effective sample size > 100 per parameter ``` From 8e0dde960697cd870a5ae1debfeeb6921c717f12 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 28 Aug 2026 12:55:55 -0600 Subject: [PATCH 158/222] Correct the XML remarks and restate comment rationales as invariants --- .../Interpolation/Support/Interpolater.cs | 4 +- .../Data/Paired Data/LineSimplification.cs | 8 +-- .../Data/Paired Data/OrderedPairedData.cs | 16 ++--- .../Data/Paired Data/UncertainOrdinate.cs | 4 +- Numerics/Data/Statistics/Statistics.cs | 10 +-- .../Time Series/Support/TimeSeriesDownload.cs | 7 +- .../Bivariate Copulas/FrankCopula.cs | 2 +- .../Bivariate Copulas/JoeCopula.cs | 2 +- .../Bivariate Copulas/StudentTCopula.cs | 4 +- .../Multivariate/MultivariateNormal.cs | 66 ++++--------------- .../Multivariate/MultivariateStudentT.cs | 4 +- .../Distributions/Univariate/LogNormal.cs | 2 +- .../Univariate/LogPearsonTypeIII.cs | 2 +- .../Uncertainty Analysis/BootstrapAnalysis.cs | 27 ++++++-- .../UncertaintyAnalysisResults.cs | 6 +- Numerics/Functions/CompositeFunction.cs | 3 +- Numerics/Functions/SegmentedPowerFunction.cs | 8 ++- .../Supervised/DecisionTree.cs | 10 +-- .../Linear Algebra/CholeskyDecomposition.cs | 5 +- .../Constrained/AugmentedLagrange.cs | 6 +- .../Optimization/Dynamic/CompactAdjacency.cs | 6 +- .../Optimization/Dynamic/Dijkstra.cs | 10 +-- .../Optimization/Dynamic/Network.cs | 15 +++-- Numerics/Sampling/Bootstrap/Bootstrap.cs | 4 +- Numerics/Sampling/MCMC/Base/MCMCSampler.cs | 6 ++ Numerics/Sampling/MCMC/NUTS.cs | 14 +++- Numerics/Sampling/MCMC/SNIS.cs | 4 +- .../Sampling/MCMC/Support/MCMCDiagnostics.cs | 4 +- .../Bivariate Copulas/Test_GumbelCopula.cs | 4 +- .../Sampling/MCMC/Test_HMC_GradientReuse.cs | 14 ++-- 30 files changed, 139 insertions(+), 138 deletions(-) diff --git a/Numerics/Data/Interpolation/Support/Interpolater.cs b/Numerics/Data/Interpolation/Support/Interpolater.cs index 16837271..7f12ca37 100644 --- a/Numerics/Data/Interpolation/Support/Interpolater.cs +++ b/Numerics/Data/Interpolation/Support/Interpolater.cs @@ -44,8 +44,8 @@ public Interpolater(IList xValues, IList yValues, SortOrder sort this.XValues = xValues; this.YValues = yValues; // Scale the correlated-search window with the table size (Numerical Recipes' N^0.25 hunt - // heuristic). Math.Max keeps the window at least 1; the former Math.Min pinned it to - // exactly 1 for every table, which starved the hunt path. See the remarks on deltaStart. + // heuristic). Math.Max keeps the window at least 1 and lets it grow with the table; a + // window pinned to a constant starves the hunt path. See the remarks on deltaStart. deltaStart = Math.Max(1, (int)Math.Pow((double)Count, 0.25)); SortOrder = sortOrder; diff --git a/Numerics/Data/Paired Data/LineSimplification.cs b/Numerics/Data/Paired Data/LineSimplification.cs index a9c6c9c3..b8dc917b 100644 --- a/Numerics/Data/Paired Data/LineSimplification.cs +++ b/Numerics/Data/Paired Data/LineSimplification.cs @@ -35,10 +35,10 @@ public static void RamerDouglasPeucker(List ordinates, double epsilon, if (ordinates.Count < 2) throw new ArgumentOutOfRangeException("Not enough points to simplify"); - // The output parameter's contract must not depend on which branch runs: the recursion - // branch appends while the endpoint branch replaced, so a pre-populated list was - // replaced or appended-to depending on the curve. Clearing up front makes the result - // the simplified curve alone on every path. + // The output parameter's contract must not depend on the caller's list state: both the + // recursion branch and the endpoint branch append, so a pre-populated list would keep + // its stale contents ahead of the result. Clearing up front makes the result the + // simplified curve alone on every path. output.Clear(); // Find the point with the maximum distance from line between the start and end diff --git a/Numerics/Data/Paired Data/OrderedPairedData.cs b/Numerics/Data/Paired Data/OrderedPairedData.cs index 6090f06e..14e62f53 100644 --- a/Numerics/Data/Paired Data/OrderedPairedData.cs +++ b/Numerics/Data/Paired Data/OrderedPairedData.cs @@ -50,10 +50,9 @@ public class OrderedPairedData : IList, INotifyCollectionChanged /// /// /// Computed from the collection size on every read as the Numerical Recipes N^0.25 hunt - /// heuristic (never below 1), matching the Interpolater's window, so the correlated test in - /// can actually select the hunt path; the former constant 0 - /// reported correlated only when a search landed on exactly the same index as the previous - /// one. The value affects only which search path runs — hunt and bisection return the same + /// heuristic, matching the Interpolater's window: the window scales as Count^0.25 with a + /// floor of 1, so consecutive nearby lookups take the hunt path in + /// . The value affects only which search path runs — hunt and bisection return the same /// bracket for the same input — so it is a performance characteristic rather than a /// correctness one. and are public and /// settable, so a consumer can steer the search directly. @@ -65,9 +64,10 @@ public class OrderedPairedData : IList, INotifyCollectionChanged /// treated as correlated, selecting the hunt search over bisection. /// /// - /// Computed from the collection size on every read, exactly like ; - /// the former constant 0 reported correlated only on an exact index repeat. The effect is - /// confined to which search path runs, never to the bracket returned. + /// Computed from the collection size on every read, exactly like : + /// the window scales as Count^0.25 with a floor of 1, so consecutive nearby lookups take + /// the hunt path. The effect is confined to which search path runs, never to the bracket + /// returned. /// private int YdeltaStart => Math.Max(1, (int)Math.Pow(Count, 0.25)); @@ -1623,7 +1623,7 @@ public OrderedPairedData LangSimplify(double tolerance, int lookAhead) { // The clamp must fire at the exact tail boundary too (i + lookAhead == count): // an unclamped look-ahead there falls through RecursiveTolerance's own range guard - // unreduced and overshoots the final ordinate, which silently dropped the curve's + // unreduced and overshoots the final ordinate, silently dropping the curve's // last point. if (i + lookAhead >= count) lookAhead = count - i - 1; diff --git a/Numerics/Data/Paired Data/UncertainOrdinate.cs b/Numerics/Data/Paired Data/UncertainOrdinate.cs index c8cddd83..c36e0c91 100644 --- a/Numerics/Data/Paired Data/UncertainOrdinate.cs +++ b/Numerics/Data/Paired Data/UncertainOrdinate.cs @@ -284,8 +284,8 @@ public List OrdinateErrors() { // Match Ordinate's equality convention for the shared X coordinate: allow a machine-epsilon // slack, and (as Ordinate documents for its own operator) a NaN coordinate compares equal - // because the rejection test below is false for NaN. The former exact inequality made the - // two classes disagree on the same conceptual coordinate. + // because the rejection test below is false for NaN. The tolerance matches Ordinate's + // convention so the two classes agree on the same conceptual coordinate. if (Math.Abs(left.X - right.X) > Tools.DoubleMachineEpsilon) return false; if (left.Y is null && right.Y is null) diff --git a/Numerics/Data/Statistics/Statistics.cs b/Numerics/Data/Statistics/Statistics.cs index 5aa7cee6..08c7a69c 100644 --- a/Numerics/Data/Statistics/Statistics.cs +++ b/Numerics/Data/Statistics/Statistics.cs @@ -161,11 +161,11 @@ public static double Mean(IList data) /// summed sequentially into its own slot in parallel, and the chunk sums are combined /// serially in chunk order — the same deterministic reduction the bootstrap's jackknife /// accumulation uses. The summation tree therefore depends only on the sample length, so the - /// result is bit-identical on every machine, core count, and scheduler. The former PLINQ - /// implementation partitioned by the processor count, so the same data produced different - /// last bits on different machines; a shared accumulator such as Tools.ParallelAdd - /// would be race-free but commits its additions in thread-scheduler order, which is the same - /// non-reproducibility. Samples below the threshold fall through to the sequential + /// result is bit-identical on every machine, core count, and scheduler. A processor-count + /// partition would produce different last bits on different machines, and a shared + /// accumulator such as Tools.ParallelAdd would be race-free but commit its additions + /// in thread-scheduler order — the same non-reproducibility — so both are excluded from this + /// reduction. Samples below the threshold fall through to the sequential /// . /// public static double ParallelMean(IList data) diff --git a/Numerics/Data/Time Series/Support/TimeSeriesDownload.cs b/Numerics/Data/Time Series/Support/TimeSeriesDownload.cs index f5f4f503..c904859e 100644 --- a/Numerics/Data/Time Series/Support/TimeSeriesDownload.cs +++ b/Numerics/Data/Time Series/Support/TimeSeriesDownload.cs @@ -83,9 +83,8 @@ public class TimeSeriesDownload /// Provider root endpoints used for the public Internet connectivity check. /// /// - /// The downloader no longer uses these URLs as a precondition for data requests. They are - /// retained only for callers that explicitly ask whether at least one supported provider - /// endpoint is reachable. + /// These URLs are not a precondition for data requests; they are used only by callers that + /// explicitly ask whether at least one supported provider endpoint is reachable. /// private static readonly string[] InternetProbeUrls = { @@ -430,7 +429,7 @@ private static Encoding GetResponseEncoding(HttpResponseMessage response) /// An exception with a user-facing timeout message. /// /// The message names the endpoint so application callers can distinguish a blocked provider - /// request from the old generic Internet connectivity failure. + /// request from a general Internet connectivity failure. /// private static TimeoutException CreateDownloadTimeoutException(string url, TimeSpan requestTimeout, Exception innerException) { diff --git a/Numerics/Distributions/Bivariate Copulas/FrankCopula.cs b/Numerics/Distributions/Bivariate Copulas/FrankCopula.cs index 0ec28c0d..1717f333 100644 --- a/Numerics/Distributions/Bivariate Copulas/FrankCopula.cs +++ b/Numerics/Distributions/Bivariate Copulas/FrankCopula.cs @@ -254,7 +254,7 @@ internal static double KendallsTauFromTheta(double theta) /// Thrown when Kendall's τ for the sample data lies outside the range the fitting bracket can reach. /// /// - /// Kendall's τ is estimated from the sample data and is + /// Kendall's τ is estimated from the sample data and the closed-form Kendall's τ(θ) relation is /// inverted with Brent's method over the bracket returned by /// , which is [0.001, 100] for a /// positive τ and [-100, -0.001] for a non-positive one. That bracket reaches |τ| in diff --git a/Numerics/Distributions/Bivariate Copulas/JoeCopula.cs b/Numerics/Distributions/Bivariate Copulas/JoeCopula.cs index 2857df05..b5508f4a 100644 --- a/Numerics/Distributions/Bivariate Copulas/JoeCopula.cs +++ b/Numerics/Distributions/Bivariate Copulas/JoeCopula.cs @@ -291,7 +291,7 @@ private static double TauSeriesTail(double theta) /// Thrown when Kendall's τ for the sample data lies outside the range the fitting bracket can reach. /// /// - /// Kendall's τ is estimated from the sample data and is + /// Kendall's τ is estimated from the sample data and the closed-form Kendall's τ(θ) relation is /// inverted with Brent's method over the bracket returned by /// , θ in [1, 100]. That bracket reaches /// τ in [0, 0.98025359]. The Joe copula models positive dependence only, so a negative τ is not attainable diff --git a/Numerics/Distributions/Bivariate Copulas/StudentTCopula.cs b/Numerics/Distributions/Bivariate Copulas/StudentTCopula.cs index 34b85393..999a086f 100644 --- a/Numerics/Distributions/Bivariate Copulas/StudentTCopula.cs +++ b/Numerics/Distributions/Bivariate Copulas/StudentTCopula.cs @@ -103,8 +103,8 @@ public StudentTCopula(double rho, double degreesOfFreedom, IUnivariateDistributi /// /// /// ν is represented as a so that gradient-free MCMC samplers can - /// explore the parameter space smoothly; previously the value was rounded to an integer - /// on every call, which produced a step-function + /// explore the parameter space smoothly. Rounding ν to an integer on every + /// call would produce a step-function /// likelihood surface and unnecessary plateaus in the posterior. Both /// and accept non-integer /// degrees of freedom. diff --git a/Numerics/Distributions/Multivariate/MultivariateNormal.cs b/Numerics/Distributions/Multivariate/MultivariateNormal.cs index a36aa6ac..382bc0d6 100644 --- a/Numerics/Distributions/Multivariate/MultivariateNormal.cs +++ b/Numerics/Distributions/Multivariate/MultivariateNormal.cs @@ -155,9 +155,9 @@ public MultivariateNormal(double[] mean, double[,] covariance, DecompositionMeth /// /// The literal must be 2⁻⁵² bit-exactly, so it is written out rather than derived as /// 2d * Tools.DoubleMachineEpsilon: that constant is the decimal literal - /// 1.11022302462516E-16, which is 2⁻⁵³ rounded to fifteen significant figures, so doubling it - /// gives 1.9999999999999938 times 2⁻⁵³ rather than 2⁻⁵², and the thresholds built on it would - /// drift off scipy's by a few ulps. + /// 1.11022302462516E-16, which slightly exceeds 2⁻⁵³ — it parses to 1.0000000000000031 + /// times 2⁻⁵³ — so doubling it gives 2.0000000000000062 times 2⁻⁵³, overshooting 2⁻⁵², and + /// the thresholds built on it would drift off scipy's by a few ulps. /// /// private const double RelativeMachineEpsilon = 2.220446049250313E-16; @@ -869,9 +869,9 @@ private static bool IsSymmetricPositiveSemiDefinite(SingularValueDecomposition s /// public bool TrySetParameters(double[] mean, double[,] covariance) { - // The Cholesky constructor itself throws on strongly indefinite matrices - // (negative pivots) while merely flagging weakly non-positive-definite - // ones, so the non-throwing contract absorbs both failure modes. + // The Cholesky constructor throws on any rejected pivot — it never returns + // with IsPositiveDefinite false — so the non-throwing contract absorbs the + // decomposition failure here. try { // Validate through the overload that hands back the decomposition and apply it directly, @@ -1342,9 +1342,6 @@ public static MultivariateNormal Bivariate(double mu1, double mu2, double sigma1 #region Cumulative Distribution Support - - //****************************************************************************80 - /// /// Computes the bivariate normal CDF. /// @@ -1352,52 +1349,13 @@ public static MultivariateNormal Bivariate(double mu1, double mu2, double sigma1 /// Upper limit for variable Y. /// The correlation coefficient. /// The bivariate normal CDF value. + /// + /// Original FORTRAN77 version by Thomas Donnelly (ACM Algorithm 462); adapted from the + /// MIT-licensed C++ version by John Burkardt. + /// Reference: Donnelly, T. (1973). "Algorithm 462: Bivariate Normal Distribution." + /// Communications of the ACM, 16(10), 638. + /// public static double bivnor(double ah, double ak, double r) - - //****************************************************************************80 - // - // Purpose: - // - // BIVNOR computes the bivariate normal CDF. - // - // Discussion: - // - // BIVNOR computes the probability for two normal variates X and Y - // whose correlation is R, that AH <= X and AK <= Y. - // - // Licensing: - // - // This code is distributed under the MIT license. - // - // Modified: - // - // 13 April 2012 - // - // Author: - // - // Original FORTRAN77 version by Thomas Donnelly. - // C++ version by John Burkardt. - // - // Reference: - // - // Thomas Donnelly, - // Algorithm 462: Bivariate Normal Distribution, - // Communications of the ACM, - // October 1973, Volume 16, Number 10, page 638. - // - // Parameters: - // - // Input, double AH, AK, the lower limits of integration. - // - // Input, double R, the correlation between X and Y. - // - // Output, double BIVNOR, the bivariate normal CDF. - // - // Local Parameters: - // - // Local, int IDIG, the number of significant digits - // to the right of the decimal point desired in the answer. - // { double a2; double ap; diff --git a/Numerics/Distributions/Multivariate/MultivariateStudentT.cs b/Numerics/Distributions/Multivariate/MultivariateStudentT.cs index c9692d02..740e7476 100644 --- a/Numerics/Distributions/Multivariate/MultivariateStudentT.cs +++ b/Numerics/Distributions/Multivariate/MultivariateStudentT.cs @@ -543,7 +543,9 @@ public override double CDF(double[] x) // P(X ≤ x) = E_W[ Φ_MVN((x−μ)·√(W/ν); 0, Σ) ] where W ~ χ²(ν) // // We evaluate this by computing the MVN CDF at K equally-spaced quantiles - // of χ²(ν) and averaging. This is deterministic and works for any ν. + // of χ²(ν) and averaging. The stratification over χ²(ν) is deterministic and + // works for any ν; the inner MVN CDF is a randomized lattice rule above two + // dimensions (see the method remarks). const int K = 200; var gamma = new GammaDistribution(2.0, _degreesOfFreedom / 2.0); diff --git a/Numerics/Distributions/Univariate/LogNormal.cs b/Numerics/Distributions/Univariate/LogNormal.cs index e5c943ca..5ea0f9b1 100644 --- a/Numerics/Distributions/Univariate/LogNormal.cs +++ b/Numerics/Distributions/Univariate/LogNormal.cs @@ -459,7 +459,7 @@ public Tuple GetParameterConstraints(IList // Get bounds of mean. The mean is a location parameter on the log scale and is // legitimately negative whenever the data are mostly below 1, so the bounds are // symmetric about zero from the magnitude of the initial value, matching Normal's - // location bounds; the former machine-epsilon floor rejected any sub-unity sample + // location bounds. A machine-epsilon floor here would reject any sub-unity sample // before a fit could start. if (initialVals[0] == 0d) initialVals[0] = Tools.DoubleMachineEpsilon; lowerVals[0] = -Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); diff --git a/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs b/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs index 5497bb6a..37a20630 100644 --- a/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs +++ b/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs @@ -695,7 +695,7 @@ public Tuple GetParameterConstraints(IList // Get bounds of mean. The mean is a location parameter on the log scale and is // legitimately negative whenever the data are mostly below 1, so the bounds are // symmetric about zero from the magnitude of the initial value, matching Normal's - // location bounds; the former machine-epsilon floor rejected any sub-unity sample + // location bounds. A machine-epsilon floor here would reject any sub-unity sample // before a fit could start. if (initialVals[0] == 0d) initialVals[0] = Tools.DoubleMachineEpsilon; lowerVals[0] = -Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); diff --git a/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs b/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs index c7dac474..60869a29 100644 --- a/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs +++ b/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs @@ -406,6 +406,12 @@ public double[] ExpectedProbabilities(IList quantiles, IList pro /// The quantiles to evaluate. /// The bootstrap distributions; null entries represent failed fits. /// The mean CDF values. + /// + /// The replications are split into chunks (capped at the + /// replication count), each summed sequentially and merged in chunk order, so the result + /// does not depend on the thread count. Failed fits are excluded from both the sum and + /// the divisor. + /// private static double[] MeanCDFs(double[] quantiles, IUnivariateDistribution[] distributions) { int replications = distributions.Length; @@ -471,7 +477,8 @@ public double[] ExpectedProbabilities(IList quantiles, IUnivariateDistri /// Optional. Pass in an array of bootstrapped distributions. Default = null. public double[] ComputeMinMaxQuantiles(double minProbability, double maxProbability, IUnivariateDistribution[] distributions) { - // Thread-local extremes merged once per partition rather than a lock per distribution. + // The extremes merge once per partition; min and max are order-independent, so the + // result does not depend on the partitioning. var output = new double[] { double.MaxValue, double.MinValue }; object lockObject = new object(); int count = distributions.Length; @@ -536,8 +543,10 @@ public double[] ComputeMinMaxQuantiles(double minProbability, double maxProbabil /// The bias-correction proportion is count(θ*ᵢ ≤ θ̂) / (B + 1) over the B successful bootstrap /// replicates — the plotting-position form of Efron's estimator, which keeps the proportion /// below one when every replicate falls at or below the estimate. When every replicate exceeds - /// the estimate the proportion is zero, the bias correction is −∞, and the adjusted limits - /// collapse to the smallest replicate. + /// the estimate the proportion is zero and the bias correction saturates at the finite floor of + /// — the z-score of , about + /// −38.5; the adjusted probability underflows to zero, so the limits collapse to the smallest + /// replicate. /// public double[,] BiasCorrectedQuantileCI(IList probabilities, double alpha = 0.1, IUnivariateDistribution[]? distributions = null) { @@ -624,8 +633,10 @@ public double[] ComputeMinMaxQuantiles(double minProbability, double maxProbabil /// The bias-correction proportion is count(θ*ᵢ ≤ θ̂) / (B + 1) over the B successful bootstrap /// replicates — the plotting-position form of Efron's estimator, which keeps the proportion /// below one when every replicate falls at or below the estimate. When every replicate exceeds - /// the estimate the proportion is zero and the bias correction is −∞; the adjusted limits then - /// collapse to the smallest replicate, or are undefined when the acceleration is nonzero. + /// the estimate the proportion is zero and the bias correction saturates at the finite floor of + /// — the z-score of , about + /// −38.5; the BCa expression stays defined, the adjusted probability underflows to zero, and + /// the limits collapse to the smallest replicate. /// public double[,] BCaQuantileCI(IList sampleData, IList probabilities, double alpha = 0.1) { @@ -676,6 +687,10 @@ public double[] ComputeMinMaxQuantiles(double minProbability, double maxProbabil /// The fitted population quantiles. /// One acceleration constant per probability. /// Thrown when every leave-one-out fit fails. + /// + /// The jackknife is chunked () so the moment sums merge in a + /// fixed order independent of the thread count. + /// private double[] AccelerationConstants(IList sampleData, IList probabilities, IList thetaHats) { int sampleCount = sampleData.Count; @@ -707,6 +722,7 @@ private double[] AccelerationConstants(IList sampleData, IList p for (int k = 0; k < index; k++) jackknifeSample[k] = sampleData[k]; for (int k = index + 1; k < sampleCount; k++) jackknifeSample[k - 1] = sampleData[k]; + // Cloned per point: a failed Estimate can leave the instance partially set. var distribution = ((UnivariateDistributionBase)Distribution).Clone(); try { @@ -890,6 +906,7 @@ private double[] BootstrapStandardError(UnivariateDistributionBase parentDist, I /// The cube-root-transformed fitted population quantiles. /// One jackknife standard error per probability. /// Thrown when every leave-one-out fit fails. + /// Chunked as . private double[] StandardError(IList sampleData, IList probabilities, IList thetaHats) { int sampleCount = sampleData.Count; diff --git a/Numerics/Distributions/Univariate/Uncertainty Analysis/UncertaintyAnalysisResults.cs b/Numerics/Distributions/Univariate/Uncertainty Analysis/UncertaintyAnalysisResults.cs index 586a71c3..8ab35afd 100644 --- a/Numerics/Distributions/Univariate/Uncertainty Analysis/UncertaintyAnalysisResults.cs +++ b/Numerics/Distributions/Univariate/Uncertainty Analysis/UncertaintyAnalysisResults.cs @@ -422,9 +422,9 @@ public void ProcessMeanCurve(UnivariateDistributionBase[] sampledDistributions, int B = sampledDistributions.Length; - // Compute min and max X values across all distributions. The extremes merge once per - // worker rather than once per distribution; min and max are order-independent, so the - // result is the same however the loop partitions. + // Compute min and max X values across all distributions. The extremes merge per + // partition; min and max are order-independent, so the result is the same however + // the loop partitions. double minX = double.MaxValue; double maxX = double.MinValue; object lockObject = new object(); diff --git a/Numerics/Functions/CompositeFunction.cs b/Numerics/Functions/CompositeFunction.cs index a7a2f1ac..a1147f31 100644 --- a/Numerics/Functions/CompositeFunction.cs +++ b/Numerics/Functions/CompositeFunction.cs @@ -23,7 +23,8 @@ namespace Numerics.Functions /// selected and evaluated at the re-scaled remainder (u − Σ w₍<i₎)/wᵢ — deterministic /// composition sampling with no internal random source, so the same u always reproduces the /// same curve. Outside [0, 1] (the mean convention shared by the other function types), - /// both modes evaluate the weighted average of the children's own mean evaluations. + /// both modes evaluate the weighted average of each child evaluated at its own configured + /// confidence level. /// /// /// The numeric assumes the composed function is diff --git a/Numerics/Functions/SegmentedPowerFunction.cs b/Numerics/Functions/SegmentedPowerFunction.cs index 4fe12e3f..fade4bfd 100644 --- a/Numerics/Functions/SegmentedPowerFunction.cs +++ b/Numerics/Functions/SegmentedPowerFunction.cs @@ -23,8 +23,9 @@ namespace Numerics.Functions /// addition mode the BaRatin continuity derivation collapses each control's offset to its /// activation stage (b_k = κ_k), the breakpoints must be strictly ordered /// (h₁ < h₂ < …), and discharge is zero at and below the main-channel cease-to-flow - /// stage h₁. One segment degenerates to the plain power law - /// with α = 10^(log₁₀α₁), β = β₁, ξ = h₁. + /// stage h₁. One segment degenerates, for the deterministic curve only, to the plain power + /// law with α = 10^(log₁₀α₁), β = β₁, ξ = h₁; the residual + /// spaces differ (log₁₀ here, natural log in ). /// /// /// The residual is Gaussian in log₁₀ space: with a @@ -294,11 +295,12 @@ public double InverseFunction(double y) if (_parametersValid == false) ValidateParameters(_parameters, true); - // Fold the residual out first: the stochastic curve is the deterministic curve + // Reject NaN and negative infinity; positive infinity clamps to the support cap. if (double.IsNaN(y) || double.IsNegativeInfinity(y)) throw new ArgumentOutOfRangeException(nameof(y), "The inverse value must not be NaN or negative infinity."); if (double.IsPositiveInfinity(y)) return Maximum; + // Fold the residual out first: the stochastic curve is the deterministic curve // scaled by 10^z, so the inverse divides before the monotone root find. if (IsDeterministic == false && ConfidenceLevel >= 0 && ConfidenceLevel <= 1) y /= Math.Pow(10d, _normal.InverseCDF(ConfidenceLevel)); diff --git a/Numerics/Machine Learning/Supervised/DecisionTree.cs b/Numerics/Machine Learning/Supervised/DecisionTree.cs index 94ba3c06..3a174f1e 100644 --- a/Numerics/Machine Learning/Supervised/DecisionTree.cs +++ b/Numerics/Machine Learning/Supervised/DecisionTree.cs @@ -228,11 +228,11 @@ public void Train() private DecisionNode GrowTree(int[] indices, int lo, int hi, int depth) { int numberOfSamples = hi - lo; - // Count distinct responses for BOTH modes so the pure-node guard below can fire. Regression - // used to substitute the sample count, which made the guard redundant with the minimum split - // size, and a zero-gain split of a pure node still beat the double.MinValue seed — a default - // regression tree therefore recursed to one observation per leaf. Counting distinct values - // applies scikit-learn's rule: a node is never split once it is pure. + // Count distinct responses for BOTH modes so the pure-node guard below can fire — + // scikit-learn's rule: a node is never split once it is pure. Substituting the sample + // count makes the guard redundant with the minimum split size, and a zero-gain split of + // a pure node beats the double.MinValue seed, so a default regression tree recurses to + // one observation per leaf. int numberOfLabels = CountDistinctLabels(indices, lo, hi); // The feature subset is drawn for every node, split or leaf, so the generator consumes diff --git a/Numerics/Mathematics/Linear Algebra/CholeskyDecomposition.cs b/Numerics/Mathematics/Linear Algebra/CholeskyDecomposition.cs index 3a3452aa..f73aa5b0 100644 --- a/Numerics/Mathematics/Linear Algebra/CholeskyDecomposition.cs +++ b/Numerics/Mathematics/Linear Algebra/CholeskyDecomposition.cs @@ -184,8 +184,9 @@ public CholeskyDecomposition(Matrix A, double relativeTolerance) /// /// The value is approximately n * 2^-52 ≈ n * 2.22E-16, computed as 2 * n * /// because that constant is the unit roundoff 2^-53 - /// rather than the double-precision spacing 2^-52. The library constant is a decimal-truncated - /// 2^-53, so the computed tolerance exceeds n * 2^-52 by about 3.1E-15 relative. + /// rather than the double-precision spacing 2^-52. The library constant is 2^-53 + /// rounded to fifteen significant figures, which sits just above the exact value, so the computed + /// tolerance exceeds n * 2^-52 by about 3.1E-15 relative. /// /// /// This tracks the standard backward-error bound for Cholesky factorization, in which the computed pivot diff --git a/Numerics/Mathematics/Optimization/Constrained/AugmentedLagrange.cs b/Numerics/Mathematics/Optimization/Constrained/AugmentedLagrange.cs index 26d6b1bf..8b366bc7 100644 --- a/Numerics/Mathematics/Optimization/Constrained/AugmentedLagrange.cs +++ b/Numerics/Mathematics/Optimization/Constrained/AugmentedLagrange.cs @@ -100,9 +100,9 @@ public AugmentedLagrange(Func objectiveFunction, Optimizer opt /// The primary objective enters on the optimizer's scaled convention, exactly as /// applies it, so a maximization negates it here while the /// constraint penalties stay direction-neutral and are always added. The inner search then - /// always minimizes this function. Without the scale the inner search minimized the raw - /// objective regardless of the requested direction, so a maximization reported the - /// constrained minimum. + /// always minimizes this function. Dropping the scale here would leave the inner search + /// minimizing the raw objective regardless of the requested direction, so a maximization + /// would report the constrained minimum. /// private double augmentedLagrangianFunction(double[] x) { diff --git a/Numerics/Mathematics/Optimization/Dynamic/CompactAdjacency.cs b/Numerics/Mathematics/Optimization/Dynamic/CompactAdjacency.cs index 5319a58e..110c1008 100644 --- a/Numerics/Mathematics/Optimization/Dynamic/CompactAdjacency.cs +++ b/Numerics/Mathematics/Optimization/Dynamic/CompactAdjacency.cs @@ -128,8 +128,10 @@ internal static CompactAdjacency FromEdges(IList edges, int nodeCount, boo /// /// Builds the view from caller-supplied per-node incoming-edge lists, preserving each /// list's order. The lists are authoritative when supplied (the documented solver - /// precedence), so their contents are read as-is; node indices carried by the listed - /// edges are still range-checked so a malformed list fails loudly. + /// precedence), so their contents are read as-is; only out-of-range node indices carried + /// by the listed edges throw. An edge placed in the wrong per-node bucket is not + /// detected and silently corrupts routing — bucket consistency is the caller's + /// responsibility. /// /// The incoming edges for each node; a null entry means the node has none. /// The number of nodes; the array length must equal it. diff --git a/Numerics/Mathematics/Optimization/Dynamic/Dijkstra.cs b/Numerics/Mathematics/Optimization/Dynamic/Dijkstra.cs index 5c988543..67d54357 100644 --- a/Numerics/Mathematics/Optimization/Dynamic/Dijkstra.cs +++ b/Numerics/Mathematics/Optimization/Dynamic/Dijkstra.cs @@ -159,11 +159,11 @@ public static bool TryGetPath(float[,] resultTable, int startNodeIndex, out List /// Lookup table of shortest paths from any given node to its nearest destination. /// /// Costs match the multi-destination - /// overload exactly; the routed next node and edge can differ from it only where two - /// destinations are exactly equidistant, where this method resolves the tie by - /// deterministic heap order rather than destination array order. One pass over the - /// network replaces one pass per destination. Duplicate destination indices are - /// tolerated. + /// overload exactly; the routed next node and edge can differ from it wherever two + /// routes have exactly equal cost — whether to the same or to different destinations — + /// where this method resolves the tie by deterministic heap order rather than + /// destination array order. One pass over the network replaces one pass per + /// destination. Duplicate destination indices are tolerated. /// /// Thrown when the edges or destination indices are null. /// Thrown when the destination array is empty, the node count cannot be derived, or an edge references a node outside the network. diff --git a/Numerics/Mathematics/Optimization/Dynamic/Network.cs b/Numerics/Mathematics/Optimization/Dynamic/Network.cs index a1074d65..c9c02452 100644 --- a/Numerics/Mathematics/Optimization/Dynamic/Network.cs +++ b/Numerics/Mathematics/Optimization/Dynamic/Network.cs @@ -16,9 +16,9 @@ namespace Numerics.Mathematics.Optimization /// The network compiles its topology once at construction — the node count, the incoming and /// outgoing adjacency, and the destination set are fixed for the instance's lifetime — so /// repeated solves pay only the solve itself, and custom-weight solves overlay a positional - /// weight vector with no rebuild. The parameterless solve methods allocate their results - /// per call and are safe for concurrent use; the overloads that write into a caller-supplied - /// table reuse instance scratch buffers and are not thread safe. Weights follow the + /// weight vector with no rebuild. The solve and path methods that return their results + /// allocate them per call and are safe for concurrent use; the overloads that write into a + /// caller-supplied table reuse instance scratch buffers and are not thread safe. Weights follow the /// conventions: non-negative weights are the correctness /// precondition, positive infinity is impassable, and NaN severs its edge. /// @@ -167,7 +167,8 @@ public Network(Edge[] edges, int[] destinationIndices) /// /// Each destination is solved independently and the tables merge per node by strictly /// smaller cost in destination order, so on an exact cost tie the earlier destination - /// wins — the same semantics as the static multi-destination solver. + /// wins — the same semantics as the static multi-destination solver. An empty + /// destination array returns an all-unreachable table. /// /// Thrown when the destination indices are null. /// Thrown when a destination index is outside the network. @@ -214,8 +215,10 @@ public Network(Edge[] edges, int[] destinationIndices) /// A result table with the next node, edge index, and cumulative weight for each node. /// /// Costs match over the network's destinations exactly; the - /// routed next node and edge can differ only where two destinations are exactly - /// equidistant. One pass replaces one pass per destination. + /// routed next node and edge can differ from it wherever two routes have exactly equal + /// cost — whether to the same or to different destinations — where this method resolves + /// the tie by deterministic heap order rather than destination array order. One pass + /// replaces one pass per destination. /// public float[,] SolveNearest() { diff --git a/Numerics/Sampling/Bootstrap/Bootstrap.cs b/Numerics/Sampling/Bootstrap/Bootstrap.cs index e18cc373..86325aa9 100644 --- a/Numerics/Sampling/Bootstrap/Bootstrap.cs +++ b/Numerics/Sampling/Bootstrap/Bootstrap.cs @@ -1204,7 +1204,9 @@ private BootstrapStatisticResult ComputeBootstrapTCI(int statisticIndex, double /// The acceleration constant for each statistic. /// /// Thrown when reports a non-positive sample size, or when every - /// leave-one-out replicate produced by fails. + /// leave-one-out replicate fails. Each replicate applies , then + /// , then , and any of the three can be + /// the cause. /// /// /// The jackknife second and third moments are accumulated over a fixed number of chunks and merged diff --git a/Numerics/Sampling/MCMC/Base/MCMCSampler.cs b/Numerics/Sampling/MCMC/Base/MCMCSampler.cs index 275ecdab..b67054d6 100644 --- a/Numerics/Sampling/MCMC/Base/MCMCSampler.cs +++ b/Numerics/Sampling/MCMC/Base/MCMCSampler.cs @@ -401,6 +401,12 @@ public enum InitializationType /// /// The acceptance rate per chain. /// + /// + /// NUTS accepts every transition, so this property is identically 1 for + /// ; use (or + /// , which substitutes it) for the statistic + /// that dual averaging targets. + /// public double[] AcceptanceRates { get diff --git a/Numerics/Sampling/MCMC/NUTS.cs b/Numerics/Sampling/MCMC/NUTS.cs index ea71729b..dd026259 100644 --- a/Numerics/Sampling/MCMC/NUTS.cs +++ b/Numerics/Sampling/MCMC/NUTS.cs @@ -185,6 +185,12 @@ public NUTS(List priorDistributions, LogLikelihood logL /// /// The mass vector for the momentum distribution. /// + /// + /// When is (the default), the supplied + /// mass seeds the metric and is replaced at the end of each adaptation window; set + /// to to sample with the fixed + /// supplied metric. + /// public Vector Mass { get; } /// @@ -352,7 +358,7 @@ private double[] ComputeDiagnosticMeans(double[] sums) /// trajectory has to span the widest, so on an ill-conditioned posterior NUTS saturates /// on nearly every transition; adaptation removes that failure mode. /// On small, well-conditioned fits the metric has little to correct and the adaptation can cost - /// up to about 40% more leapfrog steps per transition. + /// up to about 38% more leapfrog steps per transition. /// /// /// Set this to to sample with the fixed metric supplied through @@ -956,7 +962,8 @@ private void AccumulateWelfordStatistics(int chainIndex, double[] sample) /// /// The smallest per-coordinate variance retained in an adapted metric, as a fraction of the /// largest measured variance in the same window; fallback values never set that scale. - /// This caps the diagonal metric's condition number at 1e12. + /// This bounds the ratio of the largest measured window variance to any retained variance at + /// 1e12; fallback values are outside that ratio. /// private const double RELATIVE_VARIANCE_FLOOR = 1e-12; @@ -997,7 +1004,8 @@ private void AccumulateWelfordStatistics(int chainIndex, double[] sample) /// Retained variances are then floored at times the largest /// measured variance in the same window; fallback values never set that scale, because /// letting them do so would put the prior range back into the floor for every other coordinate. - /// The floor bounds the diagonal metric's condition number at 1e12 and prevents a coordinate that + /// The floor bounds the ratio of the largest measured window variance to any retained + /// variance at 1e12 — fallback values are outside that ratio — and prevents a coordinate that /// is numerically degenerate over the window from producing an unbounded mass. It does not /// correct a coordinate that merely under-explored: a variance that comes back at 1e-4 to 1e-6 of /// the truth is far above the floor and passes through, yielding a mass that is too large and a diff --git a/Numerics/Sampling/MCMC/SNIS.cs b/Numerics/Sampling/MCMC/SNIS.cs index ab05bdc3..f3e2952d 100644 --- a/Numerics/Sampling/MCMC/SNIS.cs +++ b/Numerics/Sampling/MCMC/SNIS.cs @@ -189,8 +189,8 @@ public override void Sample() // The list is sorted ascending on Fitness while the CDF below accumulates Weight; the two // keys coincide only when no importance distribution is supplied. See the remarks on Sample(). // OrderBy is a stable sort, so tied fitness values (commonly many -Infinity draws under wide - // priors) keep their original draw order and the resampled output is reproducible across - // runs and platforms; List.Sort is an unstable introsort whose tie order is not. + // priors) keep their draw order; List.Sort's tie order is implementation-defined and + // differs between target frameworks, so a seeded run would resample differently per runtime. MarkovChains[0] = MarkovChains[0].OrderBy(x => x.Fitness).ToList(); var cdf = new double[Iterations]; cdf[0] = Math.Max(0.0, MarkovChains[0][0].Weight); diff --git a/Numerics/Sampling/MCMC/Support/MCMCDiagnostics.cs b/Numerics/Sampling/MCMC/Support/MCMCDiagnostics.cs index dd97a102..aec3e8ca 100644 --- a/Numerics/Sampling/MCMC/Support/MCMCDiagnostics.cs +++ b/Numerics/Sampling/MCMC/Support/MCMCDiagnostics.cs @@ -57,8 +57,8 @@ public static double EffectiveSampleSize(IList series) /// Computes a conservative rank-normalized effective sample size for each model parameter. /// /// - /// The Markov chains to evaluate. When lengths differ, only their common leading - /// length is used, matching the historical pooled-output behavior. + /// The Markov chains to evaluate. When lengths differ, only the common leading + /// length of every chain is used. /// /// Output. A jagged array of averaged autocorrelation functions, one for each parameter. /// diff --git a/Test_Numerics/Distributions/Bivariate Copulas/Test_GumbelCopula.cs b/Test_Numerics/Distributions/Bivariate Copulas/Test_GumbelCopula.cs index b28e6d92..f60d7b14 100644 --- a/Test_Numerics/Distributions/Bivariate Copulas/Test_GumbelCopula.cs +++ b/Test_Numerics/Distributions/Bivariate Copulas/Test_GumbelCopula.cs @@ -303,8 +303,8 @@ public void Test_InverseConditionalCDF() /// rounds below 1 by roughly |ln u| ulps, so a requested level of t = 1 − 1E-16 can exceed the /// attainable maximum of the objective for u values with |ln u| ≳ 1; the solver must still /// complete. The asserted contract is the inverse relationship, not a - /// particular value: every call returns a probability in [0, 1] whose conditional CDF - /// reproduces the requested level. That phrasing is deliberate — in the far tail under + /// particular value: every call returns a probability in [0, 1] whose value is monotone + /// in the conditional level. That phrasing is deliberate — in the far tail under /// strong dependence the conditional CDF is numerically saturated across a band of v, /// so several values satisfy h(v|u) = 1 − 1E-16 to full double precision and the /// solver may legitimately return any of them. The asserted contract is therefore diff --git a/Test_Numerics/Sampling/MCMC/Test_HMC_GradientReuse.cs b/Test_Numerics/Sampling/MCMC/Test_HMC_GradientReuse.cs index 0f300078..da21b35b 100644 --- a/Test_Numerics/Sampling/MCMC/Test_HMC_GradientReuse.cs +++ b/Test_Numerics/Sampling/MCMC/Test_HMC_GradientReuse.cs @@ -273,13 +273,13 @@ public void Test_HMC_GradientReuse_TreatsNegativeZeroAsADistinctPosition() /// The memo must store a copy of the queried position, not a reference to the caller's array. /// /// - /// The requirement is real and not hypothetical: the trajectory's own working array is what the - /// sampler queries with, and the leapfrog loop rewrites that array's successor one statement - /// later. A memo holding the queried array by reference would find its key rewritten under it, so - /// the entry would stop matching the point it was computed at and start matching a point it was - /// not. This probe reproduces that directly: evaluate at a position, mutate the caller's array, - /// and check that the entry still answers the original position and does not answer the mutated - /// one. + /// A gradient delegate that writes through its argument, or a caller that mutates the array it + /// passed, must not be able to corrupt the memo key: a memo holding the queried array by + /// reference would find its key rewritten under it, so the entry would stop matching the point + /// it was computed at and start matching a point it was not. Storing a copy makes the key + /// immutable. This probe reproduces the hazard directly: evaluate at a position, mutate the + /// caller's array, and check that the entry still answers the original position and does not + /// answer the mutated one. /// [TestMethod] public void Test_HMC_GradientReuse_StoresACopyOfTheQueriedPosition() From 69f3ebd8fa2e8e2baec9de4d2f14f886e5db3ac4 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 28 Aug 2026 13:26:47 -0600 Subject: [PATCH 159/222] Pin the review-identified contracts and restate test documentation as invariants --- .../Data/Statistics/Test_Statistics.cs | 30 +++++----- .../Data/Time Series/Test_TimeSeries.cs | 35 ++++++++++++ .../Univariate/Test_BootstrapAnalysis.cs | 56 +++++++++++++++++++ .../Univariate/Test_KappaFour.cs | 5 +- .../Supervised/Test_DecisionTree.cs | 11 ++-- .../Machine Learning/Supervised/Test_kNN.cs | 7 ++- .../Machine Learning/Unsupervised/Test_GMM.cs | 12 ++-- .../Unsupervised/Test_JenksNaturalBreaks.cs | 7 +-- .../Unsupervised/Test_KMeans.cs | 7 +-- .../Mathematics/Integration/Test_Vegas.cs | 4 +- .../Constrained/Test_AugmentedLagrange.cs | 19 ++++--- .../Optimization/Global/Test_MLSL.cs | 17 +++--- .../Optimization/Global/Test_MultiStart.cs | 8 +-- .../MCMC/Test_MCMCSamplerDiagnostics.cs | 14 +++-- .../Sampling/MCMC/Test_MCMCTransitionCount.cs | 15 +++++ Test_Numerics/Sampling/MCMC/Test_RWMH.cs | 18 +++--- Test_Numerics/Sampling/MCMC/Test_SNIS.cs | 53 ++++++++++++++++++ Test_Numerics/Utilities/Test_Tools.cs | 2 +- 18 files changed, 242 insertions(+), 78 deletions(-) diff --git a/Test_Numerics/Data/Statistics/Test_Statistics.cs b/Test_Numerics/Data/Statistics/Test_Statistics.cs index 4ef382b7..929917d2 100644 --- a/Test_Numerics/Data/Statistics/Test_Statistics.cs +++ b/Test_Numerics/Data/Statistics/Test_Statistics.cs @@ -68,14 +68,16 @@ public void Test_Mean() } /// - /// Test the ParallelMean method with the direct equation. Should also be the same as the arithmetic mean in this case. + /// Test the ParallelMean method against the sequential arithmetic mean, accumulated explicitly in index order. /// [TestMethod] public void Test_ParallelMean() { - // basic equation for parallel mean - var parallel = _sample1.AsParallel(); - var valid = parallel.Sum() / parallel.Count(); + // Sequential arithmetic mean: sum in index order, then divide by the count. + double sum = 0d; + for (int i = 0; i < _sample1.Length; i++) + sum += _sample1[i]; + var valid = sum / _sample1.Length; double test = Numerics.Data.Statistics.Statistics.ParallelMean(_sample1); double regMean = Numerics.Data.Statistics.Statistics.Mean(_sample1); @@ -88,11 +90,10 @@ public void Test_ParallelMean() /// threshold. /// /// - /// The former PLINQ implementation combined per-partition sums in a machine-dependent - /// order, so its last bits varied with the processor count (measured up to 29 ULP from the - /// sequential sum at n = 100,000) — PLINQ partitions even at n = 16. Samples below the - /// fixed sequential threshold now fall through to the sequential mean, so the two must - /// agree exactly on a magnitude-spanning sample, not merely to a tolerance. + /// The summation tree depends only on the sample length, never on the processor count. + /// Samples below the fixed sequential threshold fall through to the sequential mean, so + /// the two must agree exactly on a magnitude-spanning sample, not merely to a tolerance — + /// a partition-ordered parallel reduction would differ in the last bits. /// [TestMethod] public void Test_ParallelMean_MatchesSequentialMeanExactly() @@ -742,12 +743,11 @@ public void Test_RanksInPlace_Ties() /// Verify that a tie run reaching the final sorted element records its length in the ties array. /// /// - /// The tie-length write used to happen only when a run closed at a later, distinct value inside - /// the loop, so a run containing the largest values never closed and its length was silently - /// dropped: for this fixture the buggy code returned ties[6] = 0 instead of 2, while the rank - /// averaging itself was already correct. Hand-computed oracle: sorted data are - /// {1, 2, 3, 3, 5, 5, 5}; the {3, 3} run closes at sorted position 3 with length - 1 = 1, and - /// the trailing {5, 5, 5} run ends at sorted position 6 with length - 1 = 2. + /// A trailing tie run closes at the end of the sorted array rather than at a later, distinct + /// value, and its length is recorded like any interior run's: for this fixture ties[6] == 2. + /// Hand-computed oracle: sorted data are {1, 2, 3, 3, 5, 5, 5}; the {3, 3} run closes at + /// sorted position 3 with length - 1 = 1, and the trailing {5, 5, 5} run ends at sorted + /// position 6 with length - 1 = 2. /// [TestMethod] public void Test_RanksInPlace_Ties_TrailingRunIsRecorded() diff --git a/Test_Numerics/Data/Time Series/Test_TimeSeries.cs b/Test_Numerics/Data/Time Series/Test_TimeSeries.cs index 9d882cae..2dfbb03e 100644 --- a/Test_Numerics/Data/Time Series/Test_TimeSeries.cs +++ b/Test_Numerics/Data/Time Series/Test_TimeSeries.cs @@ -1042,6 +1042,41 @@ public void Test_MonthlyStats() } } + /// + /// Test that the mean column of MonthlySummaryStatistics() is the sequential arithmetic + /// mean of each month's values, reproduced bit-for-bit by Statistics.Mean. + /// + /// + /// The summary sorts each month's values ascending before reducing them, so the oracle + /// applies the same order. Sequential accumulation makes the reduction deterministic on + /// every host regardless of processor count, and the delta of zero detects any + /// reassociation of the summation order. + /// + [TestMethod] + public void Test_MonthlySummaryStats_MeanColumnIsSequentialMean() + { + // Three years of monthly values spanning several orders of magnitude so a change in + // summation order would alter the last bits of the mean. + var values = new double[36]; + for (int i = 0; i < values.Length; i++) + { + values[i] = Math.Pow(10d, (i % 5) - 2) * (1d + i / 35d); + } + var ts = new TimeSeries(TimeInterval.OneMonth, new DateTime(2021, 01, 01), values); + + var summary = ts.MonthlySummaryStatistics(); + for (int month = 1; month <= 12; month++) + { + var monthlyData = new List(); + for (int j = 0; j < ts.Count; j++) + { + if (ts[j].Index.Month == month) { monthlyData.Add(ts[j].Value); } + } + monthlyData.Sort(); + Assert.AreEqual(Numerics.Data.Statistics.Statistics.Mean(monthlyData), summary[month - 1, 7], 0d); + } + } + /// /// Test the monthly frequency method /// diff --git a/Test_Numerics/Distributions/Univariate/Test_BootstrapAnalysis.cs b/Test_Numerics/Distributions/Univariate/Test_BootstrapAnalysis.cs index b6a44fa6..b33c5ed6 100644 --- a/Test_Numerics/Distributions/Univariate/Test_BootstrapAnalysis.cs +++ b/Test_Numerics/Distributions/Univariate/Test_BootstrapAnalysis.cs @@ -401,5 +401,61 @@ public void Test_ExpectedProbabilities_InterpolationKeepsPairsSorted() CollectionAssert.AreEqual(expected, actual); } + /// + /// Test that Quantiles dimensions its output by the supplied distributions array — not by + /// the replication count — with orientation [replication, ordinate], writes a NaN row for + /// each null entry, and fills every non-null entry's row with that distribution's own + /// InverseCDF at the requested probabilities. + /// + [TestMethod] + public void Test_Quantiles_SuppliedDistributions_DimensionsNaNRowsAndValues() + { + var probabilities = new double[] { 0.1d, 0.5d, 0.9d }; + var parent = new Normal(0d, 1d); + var boot = new BootstrapAnalysis(parent, ParameterEstimationMethod.MethodOfMoments, 10, 200); + IUnivariateDistribution[] fits = { new Normal(0d, 1d), null!, new Normal(1d, 2d) }; + + double[,] result = boot.Quantiles(probabilities, fits); + + Assert.AreEqual(3, result.GetLength(0)); + Assert.AreEqual(probabilities.Length, result.GetLength(1)); + for (int j = 0; j < probabilities.Length; j++) + { + Assert.IsTrue(double.IsNaN(result[1, j]), $"The null entry's row must be NaN at column {j}."); + Assert.IsTrue(Tools.IsFinite(result[0, j])); + Assert.IsTrue(Tools.IsFinite(result[2, j])); + Assert.AreEqual(fits[0].InverseCDF(probabilities[j]), result[0, j], 0d); + Assert.AreEqual(fits[2].InverseCDF(probabilities[j]), result[2, j], 0d); + } + } + + /// + /// Test that Probabilities dimensions its output by the supplied distributions array — not + /// by the replication count — with orientation [replication, ordinate], writes a NaN row + /// for each null entry, and fills every non-null entry's row with that distribution's own + /// CDF at the requested quantiles. + /// + [TestMethod] + public void Test_Probabilities_SuppliedDistributions_DimensionsNaNRowsAndValues() + { + var quantiles = new double[] { -1d, 0.5d, 2d }; + var parent = new Normal(0d, 1d); + var boot = new BootstrapAnalysis(parent, ParameterEstimationMethod.MethodOfMoments, 10, 200); + IUnivariateDistribution[] fits = { new Normal(0d, 1d), null!, new Normal(1d, 2d) }; + + double[,] result = boot.Probabilities(quantiles, fits); + + Assert.AreEqual(3, result.GetLength(0)); + Assert.AreEqual(quantiles.Length, result.GetLength(1)); + for (int j = 0; j < quantiles.Length; j++) + { + Assert.IsTrue(double.IsNaN(result[1, j]), $"The null entry's row must be NaN at column {j}."); + Assert.IsTrue(Tools.IsFinite(result[0, j])); + Assert.IsTrue(Tools.IsFinite(result[2, j])); + Assert.AreEqual(fits[0].CDF(quantiles[j]), result[0, j], 0d); + Assert.AreEqual(fits[2].CDF(quantiles[j]), result[2, j], 0d); + } + } + } } diff --git a/Test_Numerics/Distributions/Univariate/Test_KappaFour.cs b/Test_Numerics/Distributions/Univariate/Test_KappaFour.cs index cdd39df0..30e36e53 100644 --- a/Test_Numerics/Distributions/Univariate/Test_KappaFour.cs +++ b/Test_Numerics/Distributions/Univariate/Test_KappaFour.cs @@ -107,7 +107,10 @@ public void Test_K4_Dist() } /// - /// Verifies the zero-kappa density against the analytical CDF derivative. + /// Pins the implemented zero-kappa closed form: the density must equal + /// exp(-(x - xi) / alpha) / alpha * F(x)^(1 - h), evaluated with the distribution's own + /// CDF. The independent evidence for the zero-kappa branch lives in the companion + /// normalization and continuity tests. /// [TestMethod] public void Test_K4_ZeroKappa_PDFMatchesAnalyticalDerivative() diff --git a/Test_Numerics/Machine Learning/Supervised/Test_DecisionTree.cs b/Test_Numerics/Machine Learning/Supervised/Test_DecisionTree.cs index 664ce5e4..5dad188a 100644 --- a/Test_Numerics/Machine Learning/Supervised/Test_DecisionTree.cs +++ b/Test_Numerics/Machine Learning/Supervised/Test_DecisionTree.cs @@ -114,12 +114,11 @@ public void Test_DecisionTree_Regression() /// splitting further. /// /// - /// Regression growth used to set the label count to the SAMPLE count, so the pure-node guard - /// only fired on single-row nodes, and a zero-gain split of a pure node still beat the - /// double.MinValue seed: this twelve-point, two-value fixture grew a 23-node right-leaning - /// chain with one observation per leaf. Counting distinct responses for both modes applies - /// scikit-learn's rule — a node is never split once it is pure — so the same fixture now - /// stops at the root split with two pure leaves. + /// Regression growth counts DISTINCT responses, not sample rows, when testing node purity, + /// applying scikit-learn's rule that a node is never split once it is pure. On this + /// twelve-point, two-value fixture the tree therefore stops at the root split with two pure + /// leaves — three nodes in total — rather than chaining zero-gain splits of pure nodes down + /// to one observation per leaf. /// [TestMethod] public void Test_DecisionTree_Regression_PureNodeBecomesLeaf() diff --git a/Test_Numerics/Machine Learning/Supervised/Test_kNN.cs b/Test_Numerics/Machine Learning/Supervised/Test_kNN.cs index 8e43df4b..429b9933 100644 --- a/Test_Numerics/Machine Learning/Supervised/Test_kNN.cs +++ b/Test_Numerics/Machine Learning/Supervised/Test_kNN.cs @@ -170,9 +170,10 @@ public void Test_GetNeighbors_MultiRow() /// /// A query whose column count differs from the training matrix must be rejected with a null - /// result, matching Predict. GetNeighbors' old guard compared the training matrix against - /// itself, so a narrower query silently computed partial-dimension distances and a wider - /// query threw an IndexOutOfRangeException from inside the distance helper. + /// result, matching Predict. GetNeighbors validates the QUERY matrix against the training + /// matrix; without that guard a narrower query would silently compute partial-dimension + /// distances and a wider query would throw an IndexOutOfRangeException from inside the + /// distance helper. /// [TestMethod] public void Test_GetNeighbors_QueryShapeMismatch_ReturnsNull() diff --git a/Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs b/Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs index 076b45c7..054685e8 100644 --- a/Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs +++ b/Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs @@ -104,12 +104,12 @@ public void Test_GMM_DegenerateFixture_StaysFinite() /// /// /// MatrixRegularization.MakeSymmetricPositiveDefinite is pure — it returns a symmetrized copy - /// with a trace-scaled ridge — and the M-step used to discard that return value, so the repair - /// its own comment promised was a silent no-op and only the diagonal floor protected the next - /// E-step's Cholesky factorization. This test recomputes the M-step covariance externally from - /// the public responsibilities after a single EM iteration and asserts the stored matrices carry - /// the repair's base ridge (1E-10 of the mean diagonal) exactly; the pre-fix code reproduced the - /// external value WITHOUT the ridge and failed these assertions by exactly that amount. + /// with a trace-scaled ridge — so the M-step must store that return value for the repair to + /// reach the covariance the next E-step's Cholesky factorization consumes. This test recomputes + /// the M-step covariance externally from the public responsibilities after a single EM iteration + /// and asserts the stored matrices carry the repair's base ridge (1E-10 of the mean diagonal) + /// exactly; a covariance stored WITHOUT the ridge would miss these assertions by exactly that + /// amount. /// [TestMethod] public void Test_GMM_MStep_PositiveDefiniteRepairIsApplied() diff --git a/Test_Numerics/Machine Learning/Unsupervised/Test_JenksNaturalBreaks.cs b/Test_Numerics/Machine Learning/Unsupervised/Test_JenksNaturalBreaks.cs index 00278f84..4246052d 100644 --- a/Test_Numerics/Machine Learning/Unsupervised/Test_JenksNaturalBreaks.cs +++ b/Test_Numerics/Machine Learning/Unsupervised/Test_JenksNaturalBreaks.cs @@ -63,10 +63,9 @@ public void Test_Jenks_9Classes() /// /// Data with a single distinct value cannot form more than one cluster: every candidate - /// split has zero variance, and the dynamic program's walkback previously produced a - /// negative class limit that surfaced as an IndexOutOfRangeException from inside the fitted - /// algorithm. The degenerate input must be rejected up front, while a single-cluster fit of - /// the same data remains valid. + /// split has zero variance, leaving the dynamic program's walkback no valid class limit to + /// choose. The degenerate input must be rejected up front with an ArgumentException, while + /// a single-cluster fit of the same data remains valid. /// [TestMethod] public void Test_Jenks_AllIdenticalValues() diff --git a/Test_Numerics/Machine Learning/Unsupervised/Test_KMeans.cs b/Test_Numerics/Machine Learning/Unsupervised/Test_KMeans.cs index 97429be4..80e16a01 100644 --- a/Test_Numerics/Machine Learning/Unsupervised/Test_KMeans.cs +++ b/Test_Numerics/Machine Learning/Unsupervised/Test_KMeans.cs @@ -90,10 +90,9 @@ public void Test_KMeans_ClusterCountValidation() } /// - /// A single-cluster fit converges on its first E-step, which previously exited before any - /// centroid had been computed, so the reported mean was whatever data point the k-means++ - /// initializer happened to seed (this fixture reported 10). The M-step must run once so the - /// reported mean is the mean of the assigned points. + /// A single-cluster fit converges on its first E-step, so the M-step must run before that + /// convergence exit: the reported mean is the mean of the assigned points, not whichever + /// data point the k-means++ initializer happened to seed. /// [TestMethod] public void Test_KMeans_SingleCluster_ReportsTheSampleMean() diff --git a/Test_Numerics/Mathematics/Integration/Test_Vegas.cs b/Test_Numerics/Mathematics/Integration/Test_Vegas.cs index a1d33b50..e81edc6f 100644 --- a/Test_Numerics/Mathematics/Integration/Test_Vegas.cs +++ b/Test_Numerics/Mathematics/Integration/Test_Vegas.cs @@ -319,7 +319,6 @@ public void Test_HighDimension() UseSobolSequence = false, Random = new Numerics.Sampling.MersenneTwister(12345), FunctionCalls = 20000, - MaxIterations = 10, }; vegas.Integrate(); @@ -358,6 +357,5 @@ static double Run(int? seed, bool assign) Assert.AreEqual(2d / 3d, untouched, 0.01d); Assert.AreEqual(2d / 3d, seeded, 0.01d); } -} - + } } diff --git a/Test_Numerics/Mathematics/Optimization/Constrained/Test_AugmentedLagrange.cs b/Test_Numerics/Mathematics/Optimization/Constrained/Test_AugmentedLagrange.cs index 917b9206..02a5933f 100644 --- a/Test_Numerics/Mathematics/Optimization/Constrained/Test_AugmentedLagrange.cs +++ b/Test_Numerics/Mathematics/Optimization/Constrained/Test_AugmentedLagrange.cs @@ -154,8 +154,8 @@ public void Test_RosenbrockDisk() Assert.AreEqual(0d, solver.Mu[0]); } /// - /// Tests AugmentedLagrange with mixed constraint types (equality + lesser-than + greater-than). - /// This previously caused IndexOutOfRangeException due to incorrect multiplier array indexing. + /// Tests AugmentedLagrange with mixed constraint types (equality + lesser-than + greater-than), + /// which exercises the per-type multiplier array indexing. /// /// /// Minimize x² + y² subject to: @@ -247,10 +247,9 @@ public void Test_MixedConstraints_Binding() } /// - /// Maximization drives the inner search in the requested direction. The augmented objective - /// previously entered the inner minimization unscaled, so a maximization reported the - /// constrained minimum with a Success status: this construct returned x = -10 at the lower - /// bound instead of the constrained maximum at x = 1. + /// Maximization drives the inner search in the requested direction: this construct's + /// constrained maximum is x = 1 with objective -4, and an unscaled inner minimization + /// would report the constrained minimum at the lower bound x = -10 instead. /// [TestMethod] public void Test_Maximize_InequalityConstraint() @@ -263,11 +262,13 @@ public void Test_Maximize_InequalityConstraint() Assert.AreEqual(1.0, solver.BestParameterSet.Values[0], 1E-3); Assert.AreEqual(-4.0, func(solver.BestParameterSet.Values), 1E-3); + Assert.AreEqual(-func(solver.BestParameterSet.Values), solver.BestParameterSet.Fitness, 1E-3, + "While maximizing, the stored fitness is the negated objective at the stored point."); } /// /// A maximization whose constraint is inactive at the optimum reaches the unconstrained - /// maximum; previously it reported a box corner instead. + /// maximum at (1, 3) rather than a box corner. /// [TestMethod] public void Test_Maximize_InactiveConstraint() @@ -281,6 +282,8 @@ public void Test_Maximize_InactiveConstraint() Assert.AreEqual(1.0, solver.BestParameterSet.Values[0], 1E-3); Assert.AreEqual(3.0, solver.BestParameterSet.Values[1], 1E-3); Assert.AreEqual(0.0, func(solver.BestParameterSet.Values), 1E-4); + Assert.AreEqual(-func(solver.BestParameterSet.Values), solver.BestParameterSet.Fitness, 1E-4, + "While maximizing, the stored fitness is the negated objective at the stored point."); } /// @@ -301,6 +304,8 @@ public void Test_Maximize_EqualityConstraint() Assert.AreEqual(2.0, solver.BestParameterSet.Values[0], 1E-3); Assert.AreEqual(2.0, solver.BestParameterSet.Values[1], 1E-3); Assert.AreEqual(-8.0, func(solver.BestParameterSet.Values), 1E-3); + Assert.AreEqual(-func(solver.BestParameterSet.Values), solver.BestParameterSet.Fitness, 1E-3, + "While maximizing, the stored fitness is the negated objective at the stored point."); } } } diff --git a/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs b/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs index ed078a62..5ac077bb 100644 --- a/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs +++ b/Test_Numerics/Mathematics/Optimization/Global/Test_MLSL.cs @@ -518,10 +518,10 @@ public void Test_SolutionStaysWithinBounds() /// the constructor after a run, and that the caller's own array is left untouched. /// /// - /// On the first iteration, itself was stored by reference into a - /// and handed straight to the local solver's in-place bounds repair, so - /// either path could silently corrupt the public property's own array. This pins both the public - /// array and the caller's own array against that regression. + /// Every a run records owns its own values array: no sampled + /// point may alias itself, and the local-search entry + /// point must not modify its argument. This pins both the public + /// array and the caller's own array. /// [TestMethod] public void Test_InitialValuesAreNotMutatedByARun() @@ -537,11 +537,10 @@ public void Test_InitialValuesAreNotMutatedByARun() CollectionAssert.AreEqual(callerSnapshot, solver.InitialValues, "InitialValues must still equal what was passed to the constructor after a run."); // Reference identity is the assertion that discriminates here. The value comparisons above - // are blind to the aliasing: the constructor rejects out-of-bounds initial values, so the - // bounds repair inside the local solver is a no-op for any legally constructed MLSL, and the - // first sampled point is added with Minimized = true, which the local-search loop skips. - // Both aliasing paths are therefore inert for legal use, and only the aliasing itself is - // observable. + // are blind to aliasing: the constructor rejects out-of-bounds initial values, the local + // solver repairs bounds into a private copy rather than its argument, and the first sampled + // point is added with Minimized = true, which the local-search loop skips — so an aliased + // array would still hold the right values, and only the aliasing itself is observable. // // The whole collection is searched rather than element zero, because the run sorts // SampledPoints by fitness and rebuilds the list, so the initial point does not stay diff --git a/Test_Numerics/Mathematics/Optimization/Global/Test_MultiStart.cs b/Test_Numerics/Mathematics/Optimization/Global/Test_MultiStart.cs index 2adb0a45..33617fd5 100644 --- a/Test_Numerics/Mathematics/Optimization/Global/Test_MultiStart.cs +++ b/Test_Numerics/Mathematics/Optimization/Global/Test_MultiStart.cs @@ -401,10 +401,10 @@ public void Test_SolutionStaysWithinBounds() /// passed to the constructor after a run, and that the caller's own array is left untouched. /// /// - /// On the first iteration the solver's working array was re-pointed at - /// itself rather than copied into, so every later uniform draw and the local solver's bounds repair - /// wrote straight into the public property's own array. This pins both the public - /// array and the caller's own array against that regression. + /// The solver's working array is a private copy of : + /// uniform draws overwrite only that copy, no sampled point may alias the public array, and + /// the local-search entry point must not modify its argument. This pins both the public + /// array and the caller's own array. /// [TestMethod] public void Test_InitialValuesAreNotMutatedByARun() diff --git a/Test_Numerics/Sampling/MCMC/Test_MCMCSamplerDiagnostics.cs b/Test_Numerics/Sampling/MCMC/Test_MCMCSamplerDiagnostics.cs index 24b57743..5b70f8d6 100644 --- a/Test_Numerics/Sampling/MCMC/Test_MCMCSamplerDiagnostics.cs +++ b/Test_Numerics/Sampling/MCMC/Test_MCMCSamplerDiagnostics.cs @@ -16,11 +16,12 @@ public class Test_MCMCSamplerDiagnostics { /// /// Confirms that rejected transitions before the warmup boundary advance the - /// running covariance sample count in the corrected ARWMH implementation. + /// ARWMH running covariance sample count. /// /// - /// Adaptive Metropolis covariance is defined from the realized chain history; - /// a rejected proposal therefore contributes the repeated retained state. + /// Adaptive Metropolis covariance is defined from the realized chain history, so every + /// realized transition, accepted or rejected, contributes one observation to the running + /// covariance; a rejected proposal contributes the repeated retained state. /// [TestMethod] public void ARWMH_RejectedWarmupTransitionsEnterCovariance() @@ -46,8 +47,9 @@ public void ARWMH_RejectedWarmupTransitionsEnterCovariance() /// /// /// Continued Adaptive Metropolis updating is consistent with the original - /// Haario-Saksman-Tamminen construction. This test separates that intended - /// behavior from the omitted repeated states before the boundary. + /// Haario-Saksman-Tamminen construction. Together with the pre-warmup test above, this + /// pins the schedule on both sides of the boundary: every realized transition enters the + /// running covariance before the boundary and after it alike. /// [TestMethod] public void ARWMH_RejectedPostWarmupTransitionsContinueEnteringCovariance() @@ -262,7 +264,7 @@ double SpikeTarget(double[] values) => private static int GetArwmhCovarianceSampleCount(ARWMH sampler) { FieldInfo field = typeof(ARWMH).GetField("sigma", BindingFlags.Instance | BindingFlags.NonPublic); - Assert.IsNotNull(field, "The ARWMH running covariance field must be available for characterization."); + Assert.IsNotNull(field, "The ARWMH running covariance field must be accessible."); var covariance = field.GetValue(sampler) as RunningCovarianceMatrix[]; Assert.IsNotNull(covariance); return covariance[0].N; diff --git a/Test_Numerics/Sampling/MCMC/Test_MCMCTransitionCount.cs b/Test_Numerics/Sampling/MCMC/Test_MCMCTransitionCount.cs index 6f25ec6b..68c76d5c 100644 --- a/Test_Numerics/Sampling/MCMC/Test_MCMCTransitionCount.cs +++ b/Test_Numerics/Sampling/MCMC/Test_MCMCTransitionCount.cs @@ -129,6 +129,21 @@ public void TransitionCount_DoesNotOverflowForLargeSettings() Assert.IsGreaterThan(int.MaxValue, sampler.TransitionCount); } + /// + /// Zero chains is not a samplable configuration, so both counts report zero rather than + /// dividing by zero. The properties are readable at any time; the configuration itself is + /// only rejected by ValidateSettings when Sample() runs. + /// + [TestMethod] + public void TransitionCount_AtZeroChains_ReportsZero() + { + var sampler = CreateSampler(); + sampler.NumberOfChains = 0; + + Assert.AreEqual(0L, sampler.TransitionCount); + Assert.AreEqual(0L, sampler.TotalTransitionCount); + } + /// /// Ties to the transitions Sample() actually /// performs, so the property cannot drift away from the loop it describes. diff --git a/Test_Numerics/Sampling/MCMC/Test_RWMH.cs b/Test_Numerics/Sampling/MCMC/Test_RWMH.cs index e7f467bf..59f9b4af 100644 --- a/Test_Numerics/Sampling/MCMC/Test_RWMH.cs +++ b/Test_Numerics/Sampling/MCMC/Test_RWMH.cs @@ -78,17 +78,17 @@ double logLH(double[] x) } /// - /// Pins seeded RWMH draws bitwise so the proposal distribution's mean-only update path can be - /// proven equivalent to the former full re-parameterization. + /// Pins seeded RWMH draws bitwise: a fixed seed and configuration must reproduce every drawn + /// value, fitness, and accept count exactly, on every platform. /// /// - /// The proposal covariance is fixed for a whole RWMH run, yet every chain iteration used to call - /// MultivariateNormal.SetParameters with it, re-running an O(D^3) Cholesky factorization - /// of an unchanged matrix on every transition. The refactor factorizes each chain's proposal once - /// and translates only the mean per iteration, which must not change a single drawn value. The - /// expected literals below were captured from the pre-refactor implementation (seed 12345, - /// 2 chains, 200 iterations, 100 warmup, Randomize initialization) and every one must reproduce - /// exactly — no tolerance. + /// The proposal covariance is fixed for a whole RWMH run, so each chain's proposal is + /// factorized once at initialization and only its mean moves with the chain state — a + /// translation changes nothing the factorization derives from the covariance, so the drawn + /// values are identical to re-parameterizing the proposal at every step. The expected + /// literals below are a golden master captured at seed 12345, 2 chains, 200 iterations, + /// 100 warmup, Randomize initialization, and every one must reproduce exactly — no + /// tolerance. /// [TestMethod] public void Test_RWMH_MeanOnlyProposalUpdate_ReproducesReferenceDrawsExactly() diff --git a/Test_Numerics/Sampling/MCMC/Test_SNIS.cs b/Test_Numerics/Sampling/MCMC/Test_SNIS.cs index a0b37331..6bedc43d 100644 --- a/Test_Numerics/Sampling/MCMC/Test_SNIS.cs +++ b/Test_Numerics/Sampling/MCMC/Test_SNIS.cs @@ -1,6 +1,9 @@ using System.Collections.Generic; +using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics; using Numerics.Distributions; +using Numerics.Sampling; using Numerics.Sampling.MCMC; namespace Sampling.MCMC @@ -129,5 +132,55 @@ public void Test_SNIS_MixedFiniteAndInvalidWeights_NormalizesFiniteWeights() Assert.AreEqual(1d, sum, 1e-12); } + /// + /// This test verifies that the resampling sort is stable — draws tied at the same fitness + /// keep their original draw order — and that an identically-seeded run reproduces the + /// output exactly, element for element. + /// + /// + /// The fixture yields two tied-fitness runs: half the draws sit at a log-likelihood of + /// exactly negative infinity and the rest at exactly zero, so the sorted chain is fully + /// determined by the tie-breaking rule. The oracle reproduces the sampler's parameter + /// draws from the same master PRNG seed and requires the sorted chain to be the + /// negative-infinity run followed by the finite run, each in original draw order. An + /// unstable sort would reorder the tied draws and change which samples the resampling + /// plotting positions select. + /// + [TestMethod] + public void Test_SNIS_TiedFitnessDraws_KeepDrawOrderAndReproduceExactly() + { + var priors = new List { new Uniform(0d, 1d) }; + double logLH(double[] x) => x[0] < 0.5d ? 0d : double.NegativeInfinity; + + var first = new SNIS(priors, logLH) { Iterations = 100, OutputLength = 100, PRNGSeed = 12345 }; + var second = new SNIS(priors, logLH) { Iterations = 100, OutputLength = 100, PRNGSeed = 12345 }; + first.Sample(); + second.Sample(); + + Assert.HasCount(first.Output[0].Count, second.Output[0]); + for (int i = 0; i < first.Output[0].Count; i++) + { + Assert.AreEqual(first.Output[0][i].Values[0], second.Output[0][i].Values[0], 0d); + } + + // Reproduce the sampler's parameter draws: the master PRNG is seeded with PRNGSeed and + // each draw is the prior inverse CDF of its uniform matrix, in draw-index order. + var prior = new Uniform(0d, 1d); + var rnds = new MersenneTwister(12345).NextDoubles(100, 1); + var draws = new double[100]; + for (int i = 0; i < draws.Length; i++) + draws[i] = prior.InverseCDF(rnds[i, 0]); + + // The stable ascending sort on Fitness places the negative-infinity run first and the + // zero-fitness run second, each preserving its original draw order. + var expected = draws.Where(d => !(d < 0.5d)).Concat(draws.Where(d => d < 0.5d)).ToArray(); + Assert.IsTrue(draws.Any(d => !(d < 0.5d))); + Assert.IsTrue(draws.Any(d => d < 0.5d)); + for (int i = 0; i < expected.Length; i++) + { + Assert.AreEqual(expected[i], first.MarkovChains[0][i].Values[0], 0d); + } + } + } } diff --git a/Test_Numerics/Utilities/Test_Tools.cs b/Test_Numerics/Utilities/Test_Tools.cs index 81a7d629..071e7c81 100644 --- a/Test_Numerics/Utilities/Test_Tools.cs +++ b/Test_Numerics/Utilities/Test_Tools.cs @@ -619,7 +619,7 @@ public void Test_Expm1() // Series references: 1e-8 + 0.5e-16 + 1.667e-25 and its negative-argument mirror. Assert.AreEqual(1.0000000050000000167E-8, Tools.Expm1(1E-8), 1E-24); Assert.AreEqual(-9.9999999500000002E-9, Tools.Expm1(-1E-8), 1E-24); - // Deep negatives: a subnormal exponential still resolves, and full underflow is exact. + // Deep negatives saturate at -1: within 1E-15 at -746, and full underflow returns exactly -1. Assert.AreEqual(-1d, Tools.Expm1(-746d), 1E-15); Assert.AreEqual(-1d, Tools.Expm1(-800d), 0d); Assert.AreEqual(-1d, Tools.Expm1(double.NegativeInfinity), 0d); From 8d084f41d53a3d295bc7e55454da31f9d219e3df Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 28 Aug 2026 13:26:47 -0600 Subject: [PATCH 160/222] Refresh the v2.2.0 release metadata and notes --- CITATION.cff | 2 +- Numerics/Numerics.csproj | 2 +- codemeta.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index 229dd878..1b707be1 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -3,7 +3,7 @@ message: "If you use this software, please cite our article in the Journal of Op type: software title: "Numerics: A .NET Library for Numerical Computing, Statistical Analysis, and Risk Assessment" version: "2.2.0" -date-released: "2026-08-27" +date-released: "2026-08-28" license: 0BSD repository-code: "https://github.com/USACE-RMC/Numerics" url: "https://github.com/USACE-RMC/Numerics" diff --git a/Numerics/Numerics.csproj b/Numerics/Numerics.csproj index fd205df0..2d642fcf 100644 --- a/Numerics/Numerics.csproj +++ b/Numerics/Numerics.csproj @@ -29,7 +29,7 @@ 2.2.0 - Version 2.2.0 adds weighted statistics, given-data global sensitivity estimators, a two-dimensional adaptive Gauss-Kronrod integrator, seeded Sobol scrambling, conditional copula functions with serialization and factories, composable univariate functions, sided transform-aware paired-data extrapolation, and adaptive NUTS with gradient reuse in NUTS and HMC, corrects validation, tie-correction, optimization, and machine learning edge cases, and pins seeded streams and deterministic reductions with expanded regression coverage. + Version 2.2.0 adds weighted statistics, given-data global sensitivity estimators, a two-dimensional adaptive Gauss-Kronrod integrator, seeded Sobol scrambling with scrambled driving points on the Vegas integrator, conditional copula functions with an Independence copula, serialization, and factories, composable univariate functions, empirical-distribution convolution, sided transform-aware paired-data extrapolation, singular-covariance multivariate normal support, expanded shortest-path routing with custom weights and detours, lazy exclusive-probability enumeration, distribution XML serialization for the empirical, kernel-density, and competing-risks families, and adaptive NUTS with per-chain diagnostics and gradient reuse in NUTS and HMC. Reported results can differ from 2.1.4: R-hat and effective sample size follow the rank-normalized split definitions, NUTS adapts its mass matrix by default, regression trees stop at pure nodes, LogNormal and Log-Pearson Type III accept a negative log-space mean, Gamma.Incomplete is corrected at large shape, bounded optimizers difference inside the feasible region, and seeded streams and reductions are deterministic across machines. The release also corrects validation, tie-correction, optimization, machine learning, and time-series edge cases and expands regression coverage. 2.2.0.0 diff --git a/codemeta.json b/codemeta.json index 55687461..a435eb60 100644 --- a/codemeta.json +++ b/codemeta.json @@ -6,7 +6,7 @@ "description": "A free and open-source .NET library providing numerical methods, probability distributions, statistical analysis, and Bayesian inference tools for quantitative risk assessment in water resources engineering.", "version": "2.2.0", "dateCreated": "2023-09-28", - "dateModified": "2026-08-27", + "dateModified": "2026-08-28", "license": "https://spdx.org/licenses/0BSD", "codeRepository": "https://github.com/USACE-RMC/Numerics", "issueTracker": "https://github.com/USACE-RMC/Numerics/issues", From 3cafca4f272e81a0e18fd7605c0bdd1df288805d Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 28 Aug 2026 13:50:01 -0600 Subject: [PATCH 161/222] Correct the bivnor and BCa documentation, wire the functions guide into the chain, and finish the narration sweep --- .../Multivariate/MultivariateNormal.cs | 9 ++++--- .../Uncertainty Analysis/BootstrapAnalysis.cs | 6 +++-- Numerics/Numerics.csproj | 2 +- Numerics/Sampling/MCMC/NUTS.cs | 5 ++-- .../Data/Time Series/Test_TimeSeries.cs | 25 ++++++++++--------- .../Univariate/Test_KappaFour.cs | 2 +- .../Univariate/Test_LogNormal.cs | 12 ++++----- .../Univariate/Test_LogPearsonTypeIII.cs | 4 +-- docs/distributions/multivariate.md | 6 ++--- docs/functions/index.md | 6 ++++- docs/machine-learning/machine-learning.md | 6 ++--- docs/mathematics/optimization.md | 2 +- docs/sampling/convergence-diagnostics.md | 2 +- 13 files changed, 48 insertions(+), 39 deletions(-) diff --git a/Numerics/Distributions/Multivariate/MultivariateNormal.cs b/Numerics/Distributions/Multivariate/MultivariateNormal.cs index 382bc0d6..4b8ddb41 100644 --- a/Numerics/Distributions/Multivariate/MultivariateNormal.cs +++ b/Numerics/Distributions/Multivariate/MultivariateNormal.cs @@ -1343,12 +1343,13 @@ public static MultivariateNormal Bivariate(double mu1, double mu2, double sigma1 #region Cumulative Distribution Support /// - /// Computes the bivariate normal CDF. + /// Computes the bivariate normal upper-orthant probability P(X ≥ ah, Y ≥ ak) for standard + /// normal variates X and Y with correlation r. /// - /// Upper limit for variable X. - /// Upper limit for variable Y. + /// The lower limit of integration for variable X. + /// The lower limit of integration for variable Y. /// The correlation coefficient. - /// The bivariate normal CDF value. + /// The probability that X ≥ ah and Y ≥ ak. /// /// Original FORTRAN77 version by Thomas Donnelly (ACM Algorithm 462); adapted from the /// MIT-licensed C++ version by John Burkardt. diff --git a/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs b/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs index 60869a29..058320d1 100644 --- a/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs +++ b/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs @@ -635,8 +635,10 @@ public double[] ComputeMinMaxQuantiles(double minProbability, double maxProbabil /// below one when every replicate falls at or below the estimate. When every replicate exceeds /// the estimate the proportion is zero and the bias correction saturates at the finite floor of /// — the z-score of , about - /// −38.5; the BCa expression stays defined, the adjusted probability underflows to zero, and - /// the limits collapse to the smallest replicate. + /// −38.5. The BCa expression stays defined; for small accelerations the adjusted probability + /// underflows to zero and the limits collapse to the smallest replicate, while an acceleration + /// negative enough to flip the denominator's sign (below about −0.026 at that floor) drives the + /// adjusted probability toward one instead. /// public double[,] BCaQuantileCI(IList sampleData, IList probabilities, double alpha = 0.1) { diff --git a/Numerics/Numerics.csproj b/Numerics/Numerics.csproj index 2d642fcf..c4e9454a 100644 --- a/Numerics/Numerics.csproj +++ b/Numerics/Numerics.csproj @@ -29,7 +29,7 @@ 2.2.0 - Version 2.2.0 adds weighted statistics, given-data global sensitivity estimators, a two-dimensional adaptive Gauss-Kronrod integrator, seeded Sobol scrambling with scrambled driving points on the Vegas integrator, conditional copula functions with an Independence copula, serialization, and factories, composable univariate functions, empirical-distribution convolution, sided transform-aware paired-data extrapolation, singular-covariance multivariate normal support, expanded shortest-path routing with custom weights and detours, lazy exclusive-probability enumeration, distribution XML serialization for the empirical, kernel-density, and competing-risks families, and adaptive NUTS with per-chain diagnostics and gradient reuse in NUTS and HMC. Reported results can differ from 2.1.4: R-hat and effective sample size follow the rank-normalized split definitions, NUTS adapts its mass matrix by default, regression trees stop at pure nodes, LogNormal and Log-Pearson Type III accept a negative log-space mean, Gamma.Incomplete is corrected at large shape, bounded optimizers difference inside the feasible region, and seeded streams and reductions are deterministic across machines. The release also corrects validation, tie-correction, optimization, machine learning, and time-series edge cases and expands regression coverage. + Version 2.2.0 adds weighted statistics, given-data global sensitivity estimators, a two-dimensional adaptive Gauss-Kronrod integrator, seeded Sobol scrambling with scrambled driving points on the Vegas integrator, conditional copula functions with an Independence copula, serialization, and factories, composable univariate functions, empirical-distribution convolution, sided transform-aware paired-data extrapolation, singular-covariance multivariate normal support, expanded shortest-path routing with custom weights and detours, lazy exclusive-probability enumeration, distribution XML serialization for the empirical, kernel-density, and competing-risks families, and adaptive NUTS with per-chain diagnostics and gradient reuse in NUTS and HMC. Reported results can differ from 2.1.4: R-hat and effective sample size follow the rank-normalized split definitions, NUTS adapts its mass matrix by default, regression trees stop at pure nodes, LogNormal and Log-Pearson Type III accept a negative log-space mean, Gamma.Incomplete runs its continued fraction to convergence (results change across its upper branch, most at large shape), bounded optimizers difference inside the feasible region, and seeded streams and reductions are deterministic across machines. The release also corrects validation, tie-correction, optimization, machine learning, and time-series edge cases and expands regression coverage. 2.2.0.0 diff --git a/Numerics/Sampling/MCMC/NUTS.cs b/Numerics/Sampling/MCMC/NUTS.cs index dd026259..a28ed1b9 100644 --- a/Numerics/Sampling/MCMC/NUTS.cs +++ b/Numerics/Sampling/MCMC/NUTS.cs @@ -1042,8 +1042,9 @@ private void UpdateMassMatrix(int chainIndex, ParameterSet currentState) largestWindowVariance = variance; } - // Bound the metric's condition number against the window's own scale. When no coordinate - // produced a usable variance there is no such scale, and the floor is inert. + // Floor every retained variance at 1e-12 of the largest measured window variance, so the + // ratio of measured variances is bounded; fallback values sit outside that ratio. When no + // coordinate produced a usable variance there is no such scale, and the floor is inert. double varianceFloor = largestWindowVariance * RELATIVE_VARIANCE_FLOOR; for (int j = 0; j < NumberOfParameters; j++) { diff --git a/Test_Numerics/Data/Time Series/Test_TimeSeries.cs b/Test_Numerics/Data/Time Series/Test_TimeSeries.cs index 2dfbb03e..c8fb2ba2 100644 --- a/Test_Numerics/Data/Time Series/Test_TimeSeries.cs +++ b/Test_Numerics/Data/Time Series/Test_TimeSeries.cs @@ -672,9 +672,9 @@ public void Test_MonthlySeries_NaN() public void Test_PeaksOverThreshold_MovingSum_NaN() { // One legitimate 2-day exceedance (5 + 6 = 11). The trailing 8.0 sits at the very end - // of the series with a NaN before it, so the only window touching it is [NaN, 8.0]. - // Pre-fix: [NaN, 8.0] silently became 8.0 -> spurious extra event above threshold 7. - // Post-fix: that window is NaN -> excluded. Only [5, 6] = 11 remains. + // of the series with a NaN before it, so the only window touching it is [NaN, 8.0] — + // a NaN window carries no event, so nothing above threshold 7 may come from it and + // only [5, 6] = 11 remains. var values = new double[] { 0.1, 0.2, 5.0, 6.0, 0.1, 0.0, 0.1, 0.2, 0.0, double.NaN, 8.0 }; var ts = new TimeSeries(TimeInterval.OneDay, new DateTime(2023, 01, 01), values); @@ -1487,8 +1487,9 @@ public void Test_ResampleWithKNN_RecoversAR1Autocorrelation() } double avgLag1 = sumLag1 / trials; - // Post-fix: avgLag1 should be > 0.4 (close to phiTrue=0.7, allow shrinkage). - // Pre-fix: avgLag1 is dominated by KNN-neighbor random walk near a fixed point, NOT phi. + // The resampler must recover the AR(1) persistence: avgLag1 above 0.4 (near + // phiTrue = 0.7 with shrinkage), not the near-zero autocorrelation of a + // KNN-neighbor random walk about a fixed point. Assert.IsTrue(avgLag1 > 0.4 && avgLag1 < 0.95, $"KNN should recover lag-1 autocorrelation in the AR(1) regime; expected ~{phiTrue}, got {avgLag1:F3}."); } @@ -1612,9 +1613,9 @@ public void Test_ResampleWithBlockBootstrap_PreservesMarginalVariance() /// Clear should empty the series and raise a single reset event. /// /// - /// Clear used to remove elements one at a time, costing two full equality scans per - /// element (O(n²) on large downloads). This locks in the single-reset contract of the - /// rewritten implementation. + /// Clearing must raise a single Reset rather than per-element notifications; + /// element-by-element removal costs two full equality scans per element + /// (O(n²) on large downloads). /// [TestMethod] public void Test_Clear_EmptiesSeriesAndRaisesSingleReset() @@ -1634,8 +1635,8 @@ public void Test_Clear_EmptiesSeriesAndRaisesSingleReset() /// Clearing a large series should complete quickly. /// /// - /// Guards against reintroducing the element-by-element removal that made clearing a - /// century of daily data take seconds to minutes. + /// Clearing a century of daily data must complete immediately; element-by-element + /// removal takes seconds to minutes at this size. /// [TestMethod] [Timeout(5000, CooperativeCancellation = true)] @@ -1653,8 +1654,8 @@ public void Test_Clear_LargeSeries_CompletesQuickly() /// ordinate appears earlier in the series. /// /// - /// RemoveAt used to delegate to Remove(item), which removed the first equal element, so - /// removing a duplicate by index silently deleted the wrong ordinate. + /// RemoveAt must remove by position: delegating to Remove(item) removes the first equal + /// element, silently deleting the wrong ordinate when duplicates exist. /// [TestMethod] public void Test_RemoveAt_WithDuplicateOrdinates_RemovesRequestedIndex() diff --git a/Test_Numerics/Distributions/Univariate/Test_KappaFour.cs b/Test_Numerics/Distributions/Univariate/Test_KappaFour.cs index 30e36e53..fcb55ccb 100644 --- a/Test_Numerics/Distributions/Univariate/Test_KappaFour.cs +++ b/Test_Numerics/Distributions/Univariate/Test_KappaFour.cs @@ -113,7 +113,7 @@ public void Test_K4_Dist() /// normalization and continuity tests. /// [TestMethod] - public void Test_K4_ZeroKappa_PDFMatchesAnalyticalDerivative() + public void Test_K4_ZeroKappa_PDFMatchesClosedForm() { const double xi = 2.5d; const double alpha = 1.75d; diff --git a/Test_Numerics/Distributions/Univariate/Test_LogNormal.cs b/Test_Numerics/Distributions/Univariate/Test_LogNormal.cs index 5adff3ef..462e0302 100644 --- a/Test_Numerics/Distributions/Univariate/Test_LogNormal.cs +++ b/Test_Numerics/Distributions/Univariate/Test_LogNormal.cs @@ -281,12 +281,12 @@ public void Test_InverseCDF() /// /// /// The location parameter is the mean of the log10-transformed data, which is legitimately - /// negative whenever the data are mostly below 1. The former constraints floored the lower - /// bound at machine epsilon and collapsed the upper bound toward ceil(mu + 1), so a sub-unity - /// sample produced an initial value below its own lower bound (and, for mu at or below -1, - /// an inverted lower/upper pair) and every downstream consumer reported the inputs invalid. - /// The bounds are now symmetric about zero from the magnitude of the initial value, matching - /// the Normal distribution's location bounds. + /// negative whenever the data are mostly below 1. Flooring the lower bound at machine + /// epsilon with an upper bound near ceil(mu + 1) would put a sub-unity sample's initial + /// value below its own lower bound (and invert the pair for mu at or below -1), so every + /// downstream consumer would report the inputs invalid. The bounds are symmetric about zero + /// from the magnitude of the initial value, matching the Normal distribution's location + /// bounds. /// [TestMethod] public void Test_LogNormal_ParameterConstraints_AllowNegativeLogMean() diff --git a/Test_Numerics/Distributions/Univariate/Test_LogPearsonTypeIII.cs b/Test_Numerics/Distributions/Univariate/Test_LogPearsonTypeIII.cs index 149ee74c..34f72fdf 100644 --- a/Test_Numerics/Distributions/Univariate/Test_LogPearsonTypeIII.cs +++ b/Test_Numerics/Distributions/Univariate/Test_LogPearsonTypeIII.cs @@ -387,8 +387,8 @@ public void Test_LinearMoments_SignedSmallAndZeroSkew() /// /// /// See the matching LogNormal test: the location parameter is the mean of the - /// log10-transformed data, negative whenever the data are mostly below 1, and the former - /// machine-epsilon lower bound rejected any such sample before a fit could start. + /// log10-transformed data, negative whenever the data are mostly below 1; a machine-epsilon + /// lower bound would reject any such sample before a fit could start. /// [TestMethod] public void Test_LP3_ParameterConstraints_AllowNegativeLogMean() diff --git a/docs/distributions/multivariate.md b/docs/distributions/multivariate.md index 0125ec55..484c3308 100644 --- a/docs/distributions/multivariate.md +++ b/docs/distributions/multivariate.md @@ -1,6 +1,6 @@ -# Multivariate Distributions +# Multivariate Distributions -[← Previous: Copulas](copulas.md) | [Back to Index](../index.md) | [Next: Machine Learning →](../machine-learning/machine-learning.md) +[← Previous: Copulas](copulas.md) | [Back to Index](../index.md) | [Next: Univariate Functions →](../functions/index.md) The ***Numerics*** library provides several multivariate distributions for modeling correlated random variables: the **Multivariate Normal**, **Multivariate Student-t**, **Dirichlet**, and **Multinomial** distributions. These are fundamental in multivariate statistics, risk assessment, and uncertainty quantification. @@ -947,4 +947,4 @@ Console.WriteLine($"P(Stage > 16 AND/OR Duration > 48hr) = {jointExceedance:F4}" --- -[← Previous: Copulas](copulas.md) | [Back to Index](../index.md) | [Next: Machine Learning →](../machine-learning/machine-learning.md) +[← Previous: Copulas](copulas.md) | [Back to Index](../index.md) | [Next: Univariate Functions →](../functions/index.md) diff --git a/docs/functions/index.md b/docs/functions/index.md index efe6fbc2..0ca57b67 100644 --- a/docs/functions/index.md +++ b/docs/functions/index.md @@ -1,6 +1,6 @@ # Univariate Functions -[Back to Index](../index.md) +[← Previous: Multivariate Distributions](../distributions/multivariate.md) | [Back to Index](../index.md) | [Next: Machine Learning →](../machine-learning/machine-learning.md) The `Numerics.Functions` namespace provides the uncertain-function toolkit: univariate functional forms with optional stochastic residuals, sampled by confidence level, plus @@ -97,3 +97,7 @@ IUnivariateFunction pDraw = ensemble.Sample(0.37); // min(⌊u·N⌋, N The sibling `Link Functions` family (`ILinkFunction`: identity, log, logit, probit, complementary log-log, Fisher-z, Yeo-Johnson) serves regression and machine-learning transformations and has its own factory, `LinkFunctionFactory`. + +--- + +[← Previous: Multivariate Distributions](../distributions/multivariate.md) | [Back to Index](../index.md) | [Next: Machine Learning →](../machine-learning/machine-learning.md) diff --git a/docs/machine-learning/machine-learning.md b/docs/machine-learning/machine-learning.md index bbd5fe02..03391765 100644 --- a/docs/machine-learning/machine-learning.md +++ b/docs/machine-learning/machine-learning.md @@ -1,6 +1,6 @@ -# Machine Learning +# Machine Learning -[← Previous: Multivariate Distributions](../distributions/multivariate.md) | [Back to Index](../index.md) | [Next: Random Generation →](../sampling/random-generation.md) +[← Previous: Univariate Functions](../functions/index.md) | [Back to Index](../index.md) | [Next: Random Generation →](../sampling/random-generation.md) The ***Numerics*** library provides machine learning algorithms for both supervised and unsupervised learning tasks. These implementations are designed for engineering and scientific applications including classification, regression, and clustering. @@ -1029,4 +1029,4 @@ Console.WriteLine($" KNN (k=3): {(double)knnCorrect / y_test.Length:P1}"); --- -[← Previous: Multivariate Distributions](../distributions/multivariate.md) | [Back to Index](../index.md) | [Next: Random Generation →](../sampling/random-generation.md) +[← Previous: Univariate Functions](../functions/index.md) | [Back to Index](../index.md) | [Next: Random Generation →](../sampling/random-generation.md) diff --git a/docs/mathematics/optimization.md b/docs/mathematics/optimization.md index cb407156..c43867e0 100644 --- a/docs/mathematics/optimization.md +++ b/docs/mathematics/optimization.md @@ -525,7 +525,7 @@ for (int t = 0; t < timeSteps; t++) } // Detour routing around blocked segments, splicing onto the precomputed table when possible. -List detour = network.GetPath(blockedEdgeIndices, agentNodeIndex, table); +var detour = network.GetPath(blockedEdgeIndices, agentNodeIndex, table); ``` `Network.GetPath` finds the cheapest route to the nearest destination that avoids every edge diff --git a/docs/sampling/convergence-diagnostics.md b/docs/sampling/convergence-diagnostics.md index bd103d40..d16f1c9e 100644 --- a/docs/sampling/convergence-diagnostics.md +++ b/docs/sampling/convergence-diagnostics.md @@ -253,7 +253,7 @@ The quantity $\tau = 1 + 2\sum_{k=1}^{\infty}\rho_k$ is called the **integrated **Truncation strategy.** In practice, the infinite sum must be truncated. The ***Numerics*** implementation uses Geyer's (1992) [[3]](#3) **initial positive sequence estimator**, which sums consecutive *pairs* of autocorrelations $(\rho_{2k} + \rho_{2k+1})$ and truncates at the first pair whose sum is not positive. For a reversible Markov chain these pair sums are theoretically positive, so a non-positive pair sum marks the point where the estimates are dominated by noise. The estimate is then regularized with Geyer's **initial monotone sequence** rule: any pair sum that exceeds the preceding pair sum is replaced by that preceding value, enforcing the theoretical monotone decay. Together these rules produce a stable estimate of the integrated autocorrelation time. -**Multi-chain ESS.** When $M$ chains of length $N$ are available, each chain is first split in half, so $2M$ split chains enter the computation while the total draw count $S = N \cdot M$ is unchanged. Lag autocovariances $\hat{\gamma}_k$ are estimated for each split chain with an FFT and averaged across the split chains, and the multi-chain variance estimate combines within-chain and between-chain variability: +**Multi-chain ESS.** When $M$ chains of length $N$ are available, each chain is first split in half, so $2M$ split chains enter the computation; for even $N$ the total draw count $S = N \cdot M$ is unchanged, while for odd $N$ the middle draw of each chain is discarded, giving $S = (N - 1) \cdot M$. Lag autocovariances $\hat{\gamma}_k$ are estimated for each split chain with an FFT and averaged across the split chains, and the multi-chain variance estimate combines within-chain and between-chain variability: ```math \widehat{\text{var}}^{+} = \frac{n-1}{n}\,W + \frac{1}{n}\,B From 5b56a019fb82af1411b3d53a557ae1f0024ff4ac Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 28 Aug 2026 16:08:32 -0600 Subject: [PATCH 162/222] Report BFGS line search failures distinctly --- .../Mathematics/Optimization/Local/BFGS.cs | 26 +++++++++------ .../Support/OptimizationStatus.cs | 5 +++ .../Optimization/Support/Optimizer.cs | 7 ++-- .../Optimization/Local/Test_BFGS.cs | 33 +++++++++++++++++++ 4 files changed, 58 insertions(+), 13 deletions(-) diff --git a/Numerics/Mathematics/Optimization/Local/BFGS.cs b/Numerics/Mathematics/Optimization/Local/BFGS.cs index 0f991498..e8cc2f5a 100644 --- a/Numerics/Mathematics/Optimization/Local/BFGS.cs +++ b/Numerics/Mathematics/Optimization/Local/BFGS.cs @@ -104,12 +104,11 @@ protected override void Optimize() { int D = NumberOfParameters; double EPS = Tools.DoubleMachineEpsilon; - // TOLX is Numerical Recipes' dfpmin outer parameter-change tolerance: the loop below exits - // when the largest relative parameter step falls below it, so a line search that returns - // the starting point (a stagnated warm start) terminates immediately instead of repeating - // the identical non-progressing iteration until MaxIterations. (The TOLX local in - // LineSearchArmijo is dfpmin's unrelated inner step-size floor, and that routine is not - // called by Optimize, which uses the strong Wolfe LineSearch.) + // TOLX is Numerical Recipes' dfpmin outer parameter-change tolerance: after an accepted + // line-search step, the loop below exits when the largest relative parameter step falls + // below it. A line search that cannot satisfy the strong Wolfe conditions reports + // LineSearchFailed separately. (The TOLX local in LineSearchArmijo is dfpmin's unrelated + // inner step-size floor, and that routine is not called by Optimize.) double TOLX = 4 * EPS, STPMX = 100.0; bool cancel = false, check = false; @@ -142,6 +141,11 @@ protected override void Optimize() // Perform line search LineSearch(p, fp, g, xi, pnew, ref fret, stpmax, ref check, ref cancel); if (cancel) return; + if (check) + { + UpdateStatus(OptimizationStatus.LineSearchFailed); + return; + } // Check convergence. if (CheckConvergence(fp, fret)) @@ -151,7 +155,6 @@ protected override void Optimize() } // The new function evaluation occurs in line search; save the function value in fp for the next line search. - // It is usually safe to ignore the value of check. fp = fret; for (int i = 0; i < D; i++) { @@ -333,6 +336,7 @@ private void LineSearchArmijo(double[] xold, double fold, double[] g, ref double private void LineSearch(double[] x0, double f0, double[] g0, double[] p, double[] x, ref double f, double stpmax, ref bool check, ref bool cancel) { const double c1 = 1e-4, c2 = 0.9; + check = false; double alpha = 1.0, alphaPrev = 0.0; double fPrev = f0; double slope0 = Tools.SumProduct(g0, p); @@ -360,7 +364,7 @@ private void LineSearch(double[] x0, double f0, double[] g0, double[] p, double[ if (f > f0 + c1 * alpha * slope0 || (iter > 0 && f >= fPrev)) { - Zoom(x0, f0, slope0, p, alphaPrev, alpha, ref f, x, ref cancel); + Zoom(x0, f0, slope0, p, alphaPrev, alpha, ref f, x, ref check, ref cancel); return; } @@ -379,7 +383,7 @@ private void LineSearch(double[] x0, double f0, double[] g0, double[] p, double[ if (slope >= 0) { - Zoom(x0, f0, slope0, p, alpha, alphaPrev, ref f, x, ref cancel); + Zoom(x0, f0, slope0, p, alpha, alphaPrev, ref f, x, ref check, ref cancel); return; } @@ -403,9 +407,10 @@ private void LineSearch(double[] x0, double f0, double[] g0, double[] p, double[ /// The upper bound of the step size interval. /// The objective function value at the final accepted point. /// The parameter vector at the final accepted step size. + /// Returns true if the zoom search exhausts its attempts without finding an acceptable step. /// Set to true if cancellation is requested or a cancel condition occurs during evaluation. - private void Zoom(double[] x0, double f0, double slope0, double[] p, double alphaLow, double alphaHigh, ref double f, double[] x, ref bool cancel) + private void Zoom(double[] x0, double f0, double slope0, double[] p, double alphaLow, double alphaHigh, ref double f, double[] x, ref bool check, ref bool cancel) { const double c1 = 1e-4, c2 = 0.9; double[] g = new double[p.Length]; @@ -448,6 +453,7 @@ private void Zoom(double[] x0, double f0, double slope0, double[] p, double alph } Array.Copy(x0, x, x.Length); + check = true; } } diff --git a/Numerics/Mathematics/Optimization/Support/OptimizationStatus.cs b/Numerics/Mathematics/Optimization/Support/OptimizationStatus.cs index 3014c148..5fba178b 100644 --- a/Numerics/Mathematics/Optimization/Support/OptimizationStatus.cs +++ b/Numerics/Mathematics/Optimization/Support/OptimizationStatus.cs @@ -38,5 +38,10 @@ public enum OptimizationStatus /// The optimization method was stopped due to internal failure. /// Failure, + + /// + /// The optimization method stopped because its line search could not find an acceptable step. + /// + LineSearchFailed, } } diff --git a/Numerics/Mathematics/Optimization/Support/Optimizer.cs b/Numerics/Mathematics/Optimization/Support/Optimizer.cs index fd6b1888..70ba6d32 100644 --- a/Numerics/Mathematics/Optimization/Support/Optimizer.cs +++ b/Numerics/Mathematics/Optimization/Support/Optimizer.cs @@ -63,7 +63,8 @@ protected Optimizer(Func objectiveFunction, int numberOfParame public bool RecordTraces { get; set; } = true; /// - /// Determines whether to compute a numerically differentiated Hessian matrix when the optimization was successful. + /// Determines whether to compute a numerically differentiated Hessian matrix when the optimization was successful + /// or a line search failed after producing a usable best parameter set. /// public bool ComputeHessian { get; set; } = true; @@ -198,7 +199,7 @@ public virtual void Minimize() try { Optimize(); - if (Status == OptimizationStatus.Success && ComputeHessian) + if ((Status == OptimizationStatus.Success || Status == OptimizationStatus.LineSearchFailed) && ComputeHessian) { Hessian = new Matrix(NumericalDerivative.Hessian((x) => { return ObjectiveFunction(x); }, BestParameterSet.Values, ParameterLowerBounds!, ParameterUpperBounds!)); } @@ -226,7 +227,7 @@ public virtual void Maximize() try { Optimize(); - if (Status == OptimizationStatus.Success && ComputeHessian) + if ((Status == OptimizationStatus.Success || Status == OptimizationStatus.LineSearchFailed) && ComputeHessian) { Hessian = new Matrix(NumericalDerivative.Hessian((x) => { return ObjectiveFunction(x); }, BestParameterSet.Values, ParameterLowerBounds!, ParameterUpperBounds!)); } diff --git a/Test_Numerics/Mathematics/Optimization/Local/Test_BFGS.cs b/Test_Numerics/Mathematics/Optimization/Local/Test_BFGS.cs index 20afb27f..36576594 100644 --- a/Test_Numerics/Mathematics/Optimization/Local/Test_BFGS.cs +++ b/Test_Numerics/Mathematics/Optimization/Local/Test_BFGS.cs @@ -225,5 +225,38 @@ public void Test_GradientDoesNotProbeOutsideBounds() Assert.AreEqual(0d, solver.BestParameterSet.Values[1], 1E-6); } + /// + /// An exhausted strong-Wolfe search reports a distinct failure instead of successful convergence. + /// + /// + /// The linear objective is minimized at the upper bound. Projection keeps every zoom trial at that + /// bound, where the unprojected slope cannot satisfy the Wolfe curvature condition. The optimizer + /// must retain the best evaluated bound point without treating the returned start coordinates as a + /// converged parameter step. The requested Hessian remains available for compatibility with callers + /// whose objective wrappers observe those evaluations. + /// + [TestMethod] + public void Test_WolfeSearchExhaustionReportsLineSearchFailed() + { + var solver = new BFGS( + x => -x[0], + 1, + new[] { 0d }, + new[] { 0d }, + new[] { 1d }, + _ => new[] { -1d }) + { + ReportFailure = false, + RecordTraces = false, + ComputeHessian = true + }; + + solver.Minimize(); + + Assert.AreEqual(OptimizationStatus.LineSearchFailed, solver.Status); + Assert.AreEqual(1d, solver.BestParameterSet.Values[0]); + Assert.IsNotNull(solver.Hessian); + } + } } From 46134b9268d08c9a253d9b31f7ae07837ec0cbca Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 28 Aug 2026 16:23:44 -0600 Subject: [PATCH 163/222] Pin impossible mixture EM row failure --- .../Distributions/Univariate/Test_Mixture.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Test_Numerics/Distributions/Univariate/Test_Mixture.cs b/Test_Numerics/Distributions/Univariate/Test_Mixture.cs index 7edc04ab..a365e3a5 100644 --- a/Test_Numerics/Distributions/Univariate/Test_Mixture.cs +++ b/Test_Numerics/Distributions/Univariate/Test_Mixture.cs @@ -564,6 +564,23 @@ public void Test_Mixture_EM_ImpossibleRowThrowsWithRowContext() StringAssert.Contains(exception.Message, "zero or nonfinite"); } + /// + /// Verifies ordinary mixture EM reports an observation outside every component support. + /// + [TestMethod] + public void Test_Mixture_EM_ObservationOutsideAllComponentSupportsThrowsWithRowContext() + { + var mixture = new Mixture( + new[] { 1.0 }, + new UnivariateDistributionBase[] { new Weibull(1.0, 2.0) }); + + InvalidOperationException exception = + Assert.Throws(() => mixture.MLE(new[] { -1.0, 1.0, 2.0 })); + StringAssert.Contains(exception.Message, "row 0"); + StringAssert.Contains(exception.Message, "value -1"); + StringAssert.Contains(exception.Message, "zero or nonfinite"); + } + /// /// The empirical machinery under the hood is unchanged by the extrapolation property: /// the internal empirical CDF keeps its default far-tail endpoint hold, no extrapolation From 747cb87579244f1a8a08d41d5216ca842ca34013 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 28 Aug 2026 17:23:37 -0600 Subject: [PATCH 164/222] Report Cholesky failures at workflow boundaries --- .../Multivariate/MultivariateNormal.cs | 9 ++++-- .../Unsupervised/GaussianMixtureModel.cs | 13 ++++++++- Numerics/Sampling/Bootstrap/Bootstrap.cs | 18 ++++++++++-- .../Multivariate/Test_MultivariateNormal.cs | 29 +++++++++++++++++-- .../Machine Learning/Unsupervised/Test_GMM.cs | 20 +++++++++++++ .../Sampling/Test_PivotalBootstrap.cs | 19 ++++++++++++ 6 files changed, 98 insertions(+), 10 deletions(-) diff --git a/Numerics/Distributions/Multivariate/MultivariateNormal.cs b/Numerics/Distributions/Multivariate/MultivariateNormal.cs index 4b8ddb41..4c7b8757 100644 --- a/Numerics/Distributions/Multivariate/MultivariateNormal.cs +++ b/Numerics/Distributions/Multivariate/MultivariateNormal.cs @@ -747,10 +747,13 @@ private void CreateCorrelationMatrix() if (_decomposition == DecompositionMethod.Cholesky) { - var chol = new CholeskyDecomposition(m); - if (!chol.IsPositiveDefinite) + try + { + _ = new CholeskyDecomposition(m); + } + catch (Exception exception) { - var ex = new ArgumentOutOfRangeException(nameof(Covariance), "Covariance matrix is not positive-definite."); + var ex = new ArgumentOutOfRangeException("Covariance matrix is not positive-definite.", exception); if (throwException) throw ex; else return ex; } } diff --git a/Numerics/Machine Learning/Unsupervised/GaussianMixtureModel.cs b/Numerics/Machine Learning/Unsupervised/GaussianMixtureModel.cs index ccbcc5e2..193c0e06 100644 --- a/Numerics/Machine Learning/Unsupervised/GaussianMixtureModel.cs +++ b/Numerics/Machine Learning/Unsupervised/GaussianMixtureModel.cs @@ -154,6 +154,7 @@ public GaussianMixtureModel(Matrix X, int k) /// /// Optional. The prng seed. If negative or zero, then the computer clock is used as a seed. /// Determines whether to use random initialization or to use the k-Means++ method. Default is to use k-Means++. + /// Thrown when a component covariance cannot be factorized. public void Train(int seed = -1, bool kMeansPlusPlus = true) { // 1. Initialize clusters from k-Means @@ -254,7 +255,17 @@ private double EStep() for (int k = 0; k < K; k++) { // Decompose the covariance in the outer loop - var cholesky = new CholeskyDecomposition(Sigmas[k]); + CholeskyDecomposition cholesky; + try + { + cholesky = new CholeskyDecomposition(Sigmas[k]); + } + catch (Exception exception) + { + throw new InvalidOperationException( + $"Gaussian mixture component {k + 1} covariance could not be factorized.", + exception); + } logDet[k] = cholesky.LogDeterminant(); for (int i = 0; i < X.NumberOfRows; i++) { diff --git a/Numerics/Sampling/Bootstrap/Bootstrap.cs b/Numerics/Sampling/Bootstrap/Bootstrap.cs index 86325aa9..e318c26d 100644 --- a/Numerics/Sampling/Bootstrap/Bootstrap.cs +++ b/Numerics/Sampling/Bootstrap/Bootstrap.cs @@ -656,7 +656,8 @@ public void RunPivotalBootstrap() /// /// Raw bootstrap fits and covariances to transform. /// Thrown when is null. - /// Thrown when no valid raw fits are accepted. + /// Thrown when no valid raw fits are accepted or when + /// the parent link-space covariance cannot be factorized. public void TransformPivotalBootstrap(IEnumerable rawFits) { if (rawFits == null) @@ -673,7 +674,8 @@ public void TransformPivotalBootstrap(IEnumerable rawFits) /// The number of raw replicates requested or supplied. /// The number of raw replicates that failed before transformation. /// The elapsed raw resampling and fitting time. - /// Thrown when no valid raw fits are accepted. + /// Thrown when no valid raw fits are accepted or when + /// the parent link-space covariance cannot be factorized. private void TransformPivotalBootstrap(BootstrapFit[] rawFits, int requestedReplicates, int failedRawReplicates, TimeSpan resamplingTime) { BootstrapFit parentFit = CreateOriginalFit(); @@ -693,7 +695,17 @@ private void TransformPivotalBootstrap(BootstrapFit[] rawFits, int requestedRepl ValidateTransformedValues(parentEta, "The parent link transformation produced a non-finite value."); Matrix parentLinkCovariance = LinkCovariance(parentFit, linkController); - var parentCholesky = new CholeskyDecomposition(parentLinkCovariance); + CholeskyDecomposition parentCholesky; + try + { + parentCholesky = new CholeskyDecomposition(parentLinkCovariance); + } + catch (Exception exception) + { + throw new InvalidOperationException( + "The parent link-space covariance could not be factorized for the pivotal transformation.", + exception); + } var pivotalParameterSets = new List(acceptedRawFits.Length); var jitterRng = new MersenneTwister(PRNGSeed); int invalid = 0; diff --git a/Test_Numerics/Distributions/Multivariate/Test_MultivariateNormal.cs b/Test_Numerics/Distributions/Multivariate/Test_MultivariateNormal.cs index 6c82fc4e..d6d28d9e 100644 --- a/Test_Numerics/Distributions/Multivariate/Test_MultivariateNormal.cs +++ b/Test_Numerics/Distributions/Multivariate/Test_MultivariateNormal.cs @@ -26,17 +26,19 @@ public class Test_MultivariateNormal /// Asserts the action throws an ArgumentOutOfRangeException (net481-compatible). /// /// The action expected to throw. - private static void AssertThrowsOutOfRange(Action action) + /// The captured exception. + private static ArgumentOutOfRangeException AssertThrowsOutOfRange(Action action) { try { action(); } - catch (ArgumentOutOfRangeException) + catch (ArgumentOutOfRangeException exception) { - return; + return exception; } Assert.Fail("Expected an ArgumentOutOfRangeException."); + throw new InvalidOperationException("Unreachable after Assert.Fail."); } @@ -70,6 +72,27 @@ public void Test_TrySetCovariance_NonThrowingInvalidState() Assert.AreEqual(-Math.Log(2d * Math.PI), mvn.LogPDF(new[] { 0d, 0d }), 1E-12); } + /// + /// Verifies that Cholesky rejection follows the validation throw flag and preserves + /// the decomposition failure as diagnostic context. + /// + [TestMethod] + public void Test_ValidateParameters_CholeskyRejectionHonorsThrowFlag() + { + var mean = new[] { 0d, 0d }; + var singularCovariance = new[,] { { 1d, 1d }, { 1d, 1d } }; + var mvn = new MultivariateNormal(mean, new[,] { { 1d, 0d }, { 0d, 1d } }); + + ArgumentOutOfRangeException returned = mvn.ValidateParameters(mean, singularCovariance, false); + Assert.IsNotNull(returned); + Assert.IsNotNull(returned.InnerException); + StringAssert.Contains(returned.Message, "positive-definite"); + + var thrown = AssertThrowsOutOfRange(() => mvn.ValidateParameters(mean, singularCovariance, true)); + Assert.IsNotNull(thrown.InnerException); + StringAssert.Contains(thrown.Message, "positive-definite"); + } + /// /// Verifies the marginal utility: the sub-mean and sub-covariance at the kept /// indices, including a reordered subset, and the index validation matrix. diff --git a/Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs b/Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs index 054685e8..7ea33eb3 100644 --- a/Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs +++ b/Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs @@ -98,6 +98,26 @@ public void Test_GMM_DegenerateFixture_StaysFinite() Assert.IsFalse(double.IsNaN(gmm.Means[k, d]), $"mean [{k},{d}] is NaN"); } + /// + /// A singular initial component covariance remains a loud training failure, but the + /// public workflow identifies the component and preserves the factorization context. + /// + [TestMethod] + public void Test_GMM_SingularInitialComponentReportsContextualFailure() + { + var data = new double[,] + { + { 0d, 0d }, { 1d, 1d }, { 2d, 2d }, + { 10d, 10d }, { 11d, 11d }, { 12d, 12d } + }; + var gmm = new GaussianMixtureModel(data, 2); + + var exception = Assert.Throws(() => gmm.Train(12345)); + + StringAssert.Contains(exception.Message, "component"); + Assert.IsNotNull(exception.InnerException); + } + /// /// Verify that the M-step's symmetric positive-definite repair is actually applied to the /// stored covariance matrices. diff --git a/Test_Numerics/Sampling/Test_PivotalBootstrap.cs b/Test_Numerics/Sampling/Test_PivotalBootstrap.cs index 2bdac82a..533211ca 100644 --- a/Test_Numerics/Sampling/Test_PivotalBootstrap.cs +++ b/Test_Numerics/Sampling/Test_PivotalBootstrap.cs @@ -36,6 +36,25 @@ public void Transform_IdentityLink_UsesBothCovariances() Assert.AreEqual(29d, boot.BootstrapParameterSets[0].Values[1], 1e-12); } + /// + /// Verifies that an explicitly unregularized singular parent covariance remains a loud + /// pivotal-transform failure with workflow context and the decomposition cause retained. + /// + [TestMethod] + public void Transform_UnregularizedSingularParentReportsContextualFailure() + { + var parent = Fit(new[] { 10d, 20d }, new double[,] { { 1d, 1d }, { 1d, 1d } }); + var raw = Fit(new[] { 8d, 17d }, new double[,] { { 1d, 0d }, { 0d, 1d } }); + var boot = CreatePivotalBootstrap(parent); + boot.RegularizePivotalCovariances = false; + + var exception = Assert.Throws( + () => boot.TransformPivotalBootstrap(new[] { raw })); + + StringAssert.Contains(exception.Message, "parent link-space covariance"); + Assert.IsNotNull(exception.InnerException); + } + /// /// Verifies a full covariance matrix is used in the two-covariance transform. /// From bd4fa39a0ebcabb0e7aae7cd4d15f253eaccbde7 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 28 Aug 2026 17:52:11 -0600 Subject: [PATCH 165/222] Saturate Expm1 at the deep-negative rounding boundary --- Numerics/Utilities/Tools.cs | 2 +- Test_Numerics/Utilities/Test_Tools.cs | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Numerics/Utilities/Tools.cs b/Numerics/Utilities/Tools.cs index a3c68cef..692e561e 100644 --- a/Numerics/Utilities/Tools.cs +++ b/Numerics/Utilities/Tools.cs @@ -249,7 +249,7 @@ public static double Expm1(double x) double u = Math.Exp(x); if (u == 1.0) return x; if (double.IsPositiveInfinity(u)) return u; - if (u == 0.0) return -1.0; + if (u <= DoubleMachineEpsilon / 2.0) return -1.0; double numerator = (u - 1.0) * x; // The product overflows only for x large enough that 1 is far below one unit in the last // place of u, where exp(x) - 1 carries no cancellation to compensate for. diff --git a/Test_Numerics/Utilities/Test_Tools.cs b/Test_Numerics/Utilities/Test_Tools.cs index 071e7c81..cdf32751 100644 --- a/Test_Numerics/Utilities/Test_Tools.cs +++ b/Test_Numerics/Utilities/Test_Tools.cs @@ -640,6 +640,17 @@ public void Test_Expm1() } } + /// + /// Expm1 saturates exactly at -1 when the exponential is below the binary64 + /// rounding boundary, without saturating a nearby representable result. + /// + [TestMethod] + public void Test_Expm1_DeepNegativeRoundingBoundary() + { + Assert.AreEqual(-1d, Tools.Expm1(-745d), 0d); + Assert.AreEqual(-0.9999999999999998d, Tools.Expm1(-36d), 0d); + } + /// /// Log1p pins for the companion helper: tiny arguments return themselves exactly, small /// arguments match the series log(1 + x) = x - x^2/2 + x^3/3 to full precision, and the From f9e3edac8f2f91931bdfe6589b40e9b30c7aa030 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 28 Aug 2026 18:27:42 -0600 Subject: [PATCH 166/222] Reject zero HMC step sizes --- Numerics/Sampling/MCMC/HMC.cs | 2 +- Test_Numerics/Sampling/MCMC/Test_HMC.cs | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Numerics/Sampling/MCMC/HMC.cs b/Numerics/Sampling/MCMC/HMC.cs index f19b5528..85ab5027 100644 --- a/Numerics/Sampling/MCMC/HMC.cs +++ b/Numerics/Sampling/MCMC/HMC.cs @@ -183,7 +183,7 @@ public int Steps protected override void ValidateCustomSettings() { if (Mass.Length != NumberOfParameters) throw new ArgumentException("The mass vector must be the same length as the number of parameters.", nameof(Mass)); - if (StepSize < 0) throw new ArgumentException("The leapfrog step size must be positive.", nameof(StepSize)); + if (StepSize <= 0) throw new ArgumentException("The leapfrog step size must be positive.", nameof(StepSize)); if (Steps < 1) throw new ArgumentException("The number of leapfrog steps must be at least one.", nameof(Steps)); } diff --git a/Test_Numerics/Sampling/MCMC/Test_HMC.cs b/Test_Numerics/Sampling/MCMC/Test_HMC.cs index e5a6c8e1..4784c9f8 100644 --- a/Test_Numerics/Sampling/MCMC/Test_HMC.cs +++ b/Test_Numerics/Sampling/MCMC/Test_HMC.cs @@ -112,5 +112,27 @@ double logLH(double[] x) Assert.IsNotEmpty(sampler.MarkovChains, "Expected at least one Markov chain"); } + /// + /// A zero leapfrog step cannot move a chain and is rejected before sampling begins. + /// + [TestMethod] + public void Test_HMC_ZeroStepSize_IsRejectedBeforeSampling() + { + var priors = new List { new Uniform(-1d, 1d) }; + var sampler = new HMC(priors, x => -0.5d * x[0] * x[0], stepSize: 0d, steps: 1) + { + NumberOfChains = 1, + ParallelizeChains = false, + InitialIterations = 1, + WarmupIterations = 1, + Iterations = 100, + OutputLength = 100 + }; + + var exception = Assert.Throws(() => sampler.Sample()); + + Assert.AreEqual(nameof(HMC.StepSize), exception.ParamName); + } + } } From 7f6c78a1afef111b6298fa806af53ef8333bfa4d Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 28 Aug 2026 19:32:10 -0600 Subject: [PATCH 167/222] Close concurrency and reproducibility review findings --- Numerics/Data/Statistics/Probability.cs | 14 ++- Numerics/Data/Statistics/Statistics.cs | 7 +- .../Multivariate/MultivariateNormal.cs | 10 ++- .../Supervised/DecisionTree.cs | 22 ++++- Numerics/Utilities/SafeProgressReporter.cs | 6 +- .../Data/Statistics/Test_Probability.cs | 46 ++++++++++ .../Supervised/Test_TreeGoldens.cs | 86 +++++++++++++------ .../Utilities/Test_SafeProgressReporter.cs | 42 +++++++++ 8 files changed, 198 insertions(+), 35 deletions(-) diff --git a/Numerics/Data/Statistics/Probability.cs b/Numerics/Data/Statistics/Probability.cs index 7ac32bb7..3e1fb79f 100644 --- a/Numerics/Data/Statistics/Probability.cs +++ b/Numerics/Data/Statistics/Probability.cs @@ -591,6 +591,12 @@ public static double JointProbabilityMVN(IList probabilities, int[] indi /// An array of probabilities for each event. /// An 2D array of indicators, 0 means the event did not occur, 1 means the event did occur. /// The multivariate normal distribution for computing the joint probability. + /// + /// Rows are evaluated serially in indicator order because dimensions above two advance the + /// randomized-lattice generator assigned to . This + /// makes a fresh seeded batch reproducible and avoids concurrent access to a shared random + /// generator. + /// public static double[] JointProbabilitiesMVN(IList probabilities, int[,] indicators, MultivariateNormal multivariateNormal) { // Validate input parameters @@ -603,7 +609,7 @@ public static double[] JointProbabilitiesMVN(IList probabilities, int[,] var result = new double[indicators.GetLength(0)]; - Parallel.For(0, indicators.GetLength(0), idx => + for (int idx = 0; idx < indicators.GetLength(0); idx++) { if (idx < probabilities.Count) { @@ -611,9 +617,9 @@ public static double[] JointProbabilitiesMVN(IList probabilities, int[,] } else { - result[idx] = JointProbabilityMVN(probabilities, indicators.GetRow(idx), (MultivariateNormal)multivariateNormal.Clone()); - } - }); + result[idx] = JointProbabilityMVN(probabilities, indicators.GetRow(idx), multivariateNormal); + } + } return result; } diff --git a/Numerics/Data/Statistics/Statistics.cs b/Numerics/Data/Statistics/Statistics.cs index 08c7a69c..cf77b4e6 100644 --- a/Numerics/Data/Statistics/Statistics.cs +++ b/Numerics/Data/Statistics/Statistics.cs @@ -167,6 +167,11 @@ public static double Mean(IList data) /// in thread-scheduler order — the same non-reproducibility — so both are excluded from this /// reduction. Samples below the threshold fall through to the sequential /// . + /// For samples at or above the threshold, indexed elements are read concurrently. The + /// caller must not mutate during the call, and custom + /// implementations must support concurrent indexed reads. This + /// method owns its internal scheduling; + /// parallel options from an outer operation are not inherited. /// public static double ParallelMean(IList data) { @@ -1241,4 +1246,4 @@ double Position(int i) #endregion } -} \ No newline at end of file +} diff --git a/Numerics/Distributions/Multivariate/MultivariateNormal.cs b/Numerics/Distributions/Multivariate/MultivariateNormal.cs index 4c7b8757..058e08cb 100644 --- a/Numerics/Distributions/Multivariate/MultivariateNormal.cs +++ b/Numerics/Distributions/Multivariate/MultivariateNormal.cs @@ -195,8 +195,9 @@ public MultivariateNormal(double[] mean, double[,] covariance, DecompositionMeth /// dimensions carries a small stochastic error and only reproduces when the generator is /// seeded. The default is the fixed ; assign a seeded /// generator to tie results to a caller's own seed and to decorrelate the quadrature error - /// across instances. Not thread-safe — MVNDST advances the generator, so an instance shared - /// across threads must be cloned per thread. + /// across instances. Not thread-safe — MVNDST advances the generator, so every concurrently + /// evaluated instance must own a distinct generator. preserves this + /// generator by reference and therefore does not provide that isolation. /// /// Thrown when the assigned generator is null. public Random MVNUNI @@ -2850,6 +2851,11 @@ public static double BVU(double SH, double SK, double R) /// /// Creates a copy of the distribution. /// + /// + /// Distribution parameters and numerical work arrays are copied, but + /// is shared by reference. This preserves a caller-supplied random stream across clones; + /// callers evaluating clones concurrently must assign a distinct generator to each clone. + /// public override MultivariateDistribution Clone() { var mvn = new MultivariateNormal() diff --git a/Numerics/Machine Learning/Supervised/DecisionTree.cs b/Numerics/Machine Learning/Supervised/DecisionTree.cs index 3a174f1e..718c1db5 100644 --- a/Numerics/Machine Learning/Supervised/DecisionTree.cs +++ b/Numerics/Machine Learning/Supervised/DecisionTree.cs @@ -348,6 +348,11 @@ private DecisionNode CreateLeaf(int[] indices, int lo, int hi) /// Classification splits maximize the information gain of the per-sample entropy, in which /// each label's term is weighted by its own empirical probability. /// + /// + /// Rows with equal predictor values retain their order in the current node. Candidate + /// thresholds are evaluated in ascending order, so the smallest threshold wins an exact + /// within-feature gain tie; the first sampled feature wins an exact cross-feature tie. + /// /// References: /// /// @@ -363,6 +368,8 @@ private void BestSplit(int[] indices, int lo, int hi, int[] featureIdxs, out int var keys = _keyScratch; var vals = _valScratch; + var order = _partitionScratch; + var sortedVals = _rightAccScratch; for (int f = 0; f < featureIdxs.Length; f++) { @@ -381,13 +388,26 @@ private void BestSplit(int[] indices, int lo, int hi, int[] featureIdxs, out int { keys[valid] = key; vals[valid] = Y[indices[i]]; + order[valid] = valid; valid++; } } if (valid == 0) continue; - Array.Sort(keys, vals, 0, valid); + Array.Sort(keys, order, 0, valid); + int runStart = 0; + while (runStart < valid) + { + int runEnd = runStart + 1; + while (runEnd < valid && keys[runEnd] == keys[runStart]) + runEnd++; + Array.Sort(order, runStart, runEnd - runStart); + runStart = runEnd; + } + for (int i = 0; i < valid; i++) + sortedVals[i] = vals[order[i]]; + Array.Copy(sortedVals, vals, valid); double performance; double threshold; diff --git a/Numerics/Utilities/SafeProgressReporter.cs b/Numerics/Utilities/SafeProgressReporter.cs index 5041bf05..9839fb6f 100644 --- a/Numerics/Utilities/SafeProgressReporter.cs +++ b/Numerics/Utilities/SafeProgressReporter.cs @@ -349,10 +349,10 @@ public void RequestCancel() /// /// public void ResetCancel() - { - _cancellationTokenSource = new CancellationTokenSource(); + { lock (_subProgReporterLock) { + _cancellationTokenSource = new CancellationTokenSource(); foreach (var subProg in _subProgReporterCollection) { subProg._cancellationTokenSource = _cancellationTokenSource; @@ -379,9 +379,9 @@ public SafeProgressReporter CreateProgressModifier(float fractionOfTotal, string child._previousProgress = 0d; child.ProgressReported += (reporter, prog, progDelta) => ReportProgress(_previousProgress + progDelta * fractionOfTotal); child.MessageReported += msg => ReportMessage(msg); - child._cancellationTokenSource = _cancellationTokenSource; lock (_subProgReporterLock) { + child._cancellationTokenSource = _cancellationTokenSource; _subProgReporterCollection.Add(child); } var invokeChildCreatedHandlers = new SendOrPostCallback(state => ChildReporterCreated?.Invoke(child)); diff --git a/Test_Numerics/Data/Statistics/Test_Probability.cs b/Test_Numerics/Data/Statistics/Test_Probability.cs index 37624a18..0f427c02 100644 --- a/Test_Numerics/Data/Statistics/Test_Probability.cs +++ b/Test_Numerics/Data/Statistics/Test_Probability.cs @@ -84,6 +84,52 @@ public void Test_JointABCD_Independent_MVN() } + /// + /// A seeded MVN batch must consume randomized-lattice draws in indicator-row order and + /// reproduce bit for bit from a fresh distribution with the same seed. + /// + [TestMethod] + public void Test_JointProbabilitiesMVN_SeededBatchUsesIndexOrder() + { + var probabilities = new[] { 0.2, 0.35, 0.5, 0.65 }; + var correlation = new double[,] + { + { 1.0, 0.2, 0.1, 0.05 }, + { 0.2, 1.0, 0.15, 0.1 }, + { 0.1, 0.15, 1.0, 0.25 }, + { 0.05, 0.1, 0.25, 1.0 } + }; + var indicators = new int[128, 4]; + for (int i = 0; i < indicators.GetLength(0); i++) + for (int j = 0; j < indicators.GetLength(1); j++) + indicators[i, j] = 1; + + var expectedMvn = new MultivariateNormal(new double[4], correlation) { MVNUNI = new MersenneTwister(12345) }; + var expected = new double[indicators.GetLength(0)]; + for (int i = 0; i < expected.Length; i++) + { + expected[i] = i < probabilities.Length + ? probabilities[i] + : Probability.JointProbabilityMVN(probabilities, indicators.GetRow(i), expectedMvn); + } + + var firstMvn = new MultivariateNormal(new double[4], correlation) { MVNUNI = new MersenneTwister(12345) }; + var clone = (MultivariateNormal)firstMvn.Clone(); + Assert.AreSame(firstMvn.MVNUNI, clone.MVNUNI, "Clone must preserve the caller-supplied generator by reference."); + var first = Probability.JointProbabilitiesMVN(probabilities, indicators, firstMvn); + var secondMvn = new MultivariateNormal(new double[4], correlation) { MVNUNI = new MersenneTwister(12345) }; + var second = Probability.JointProbabilitiesMVN(probabilities, indicators, secondMvn); + + bool repeats = true; + bool usesIndexOrder = true; + for (int i = 0; i < expected.Length; i++) + { + repeats &= BitConverter.DoubleToInt64Bits(first[i]) == BitConverter.DoubleToInt64Bits(second[i]); + usesIndexOrder &= BitConverter.DoubleToInt64Bits(first[i]) == BitConverter.DoubleToInt64Bits(expected[i]); + } + Assert.IsTrue(repeats && usesIndexOrder, $"repeatable={repeats}, indexOrdered={usesIndexOrder}"); + } + /// /// Test joint probability of ABCD using assuming independence using the Product of Conditional Marginals (PCM). /// diff --git a/Test_Numerics/Machine Learning/Supervised/Test_TreeGoldens.cs b/Test_Numerics/Machine Learning/Supervised/Test_TreeGoldens.cs index c29120cb..eac5ff9b 100644 --- a/Test_Numerics/Machine Learning/Supervised/Test_TreeGoldens.cs +++ b/Test_Numerics/Machine Learning/Supervised/Test_TreeGoldens.cs @@ -133,30 +133,28 @@ public class Test_TreeGoldens "00000000000|4008000000000000|4008000000000000|4008000000000000|4008000000000000|"; private const string GoldenForestRegressionPredictions = - "3FE9889E69E2F2A9|3FF6A4062C69EB56|3FFE5BA1712962FB|3FF66C6AF8CD8102|3FD9B4A87E38EB03|3FF06E54B73C6CC3|3FF87DCDF6987831|3FEDDB7B6D94F1B0|3FE337C563CCD90C|3FEE57C080E41615|3FF6A4062C69EB56|3FEF302658EA5" + - "D03|3FE02FE3E89D37DF|3FEB5FB77B00FD2B|3FF2E3DA277E4931|3FEDA384BC779E3D|3FEC945E61344D65|3FF783F2FC0B7EB9|3FFE70E9D51B4FE8|3FF5F4B0137E6E30|BFE93FACC9C47C00|BF9AE56B5436E3ED|3FEC0EACE14A0760|BF89728A0" + - "2423944|BFAD91DA3290F6DE|3FE467E93D9BC1E8|3FF50553CC85D517|3FE51E693E87FB36|BFF79CE9829012B2|3FFA7E4C6E988F03|3FFDC26FE4697691|3FF181030F3EB87B|BFEC109CE5FF4DD3|3FEC945E61344D65|3FFE70E9D51B4FE8|3FECB" + - "5B73CB6A25B|3FD9B4A87E38EB03|3FFA7E4C6E988F03|40028BDE18CF84B8|3FF6746FD63843DD|3FD12B552DD9D7F8|3FE8A01C14B39645|3FF4C84988094E5D|3FE5499EBFAD594E|3FDE0A4A5BC3CB1B|3FF191F8CB834BA4|3FF7B05B98821745|3" + - "FF143BC73D5621A|BF9AE56B5436E3ED|3FD8341733CE38B3|3FED3ECCE9FE0B08|3FE0585E05480136|BFC3B39C0EBEDFA4|3FDA36D945811391|3FEE57C080E41615|3FDCDFB9B5DBD42F|BF9AE56B5436E3ED|3FEC0EACE14A0760|3FFDC26FE46976" + - "91|3FEA961F39EE1697|3FEC0EACE14A0760|3FF7EF9ACD4B4CDA|3FFDC26FE4697691|3FF54424263CB7E1|3FE467E93D9BC1E8|3FF2114313A5FA25|3FF759D0B3E21A3B|3FF1A09B778359A3|3FE6687B99D451FC|3FF4C84988094E5D|3FF7579900" + - "ED65CA|3FF22C2C5F8055AB|BFA55EF0A645CF6D|3FDD78F9158019EC|3FE7608F6C9A0728|3FD8B4C24308CEDC|3FE6687B99D451FC|3FF139CDA6BC6CD8|3FFCB84BB1036D9D|3FF23C7E9E8FA422|3F9012147F93DEF0|3FE87EB2B87983A5|3FF524" + - "5E809C7B14|3FEA2768080EB0A2|BFAD91DA3290F6DE|3FF6097D675EDEDE|3FFE70E9D51B4FE8|3FF424C2D34B5749|3FF191F8CB834BA4|3FF7579900ED65CA|3FFF59FB714FF968|3FF8057BAF383AEA|3FE0CA91BED9C503|3FEFA9FEF1E306CB|3F" + - "FCF0AE63802D0C|3FF1239DFD853D19|BFA55EF0A645CF6D|3FD06B925501BA14|3FE041F3940BE600|3FCA32F6E36FD317|3FD9B4A87E38EB03|3FFA7E4C6E988F03|40028BDE18CF84B8|3FF725913DF045B4|3FEC945E61344D65|3FF191F8CB834BA" + - "4|3FF783F2FC0B7EB9|3FF1D7E4CAD10F3E|3FEC945E61344D65|3FF191F8CB834BA4|3FFCF0AE63802D0C|3FF372CB4F37605A|3FDCF0D22FE31C9A|3FEB3A04B8F757E6|3FF1C9D36DD0BDF3|3FEB2CD5599B4A36|BFA55EF0A645CF6D|3FDABFE7007" + - "FACB3|3FE5DF970EFAC4AD|3FD0920CDE5AF7D9|3FDF006245C55C15|3FEC9B0D6771A93B|3FF4C84988094E5D|3FEE478B5354EE6A|3FDF006245C55C15|3FEC9B0D6771A93B|3FFAFC1F354F6D26|3FEF5827C1604009|BFD61A3738D157CC|3FD6790" + - "13DEDA159|3FE0CA91BED9C503|3FC4A026B532BE9C|BFD2AA19EF6A5B93|BF9AE56B5436E3ED|3FEB5C3C4589299B|3FA9F319895C3ED3|BFF79CE9829012B2|3FEDE65BB45BBCBF|3FFDC26FE4697691|3FEB95D9B1B21E68|C0023160AEEC671C|3FD" + - "679013DEDA159|3FFE70E9D51B4FE8|3FB2EA6408866B76|C0023160AEEC671C|BFE8178FD41DF9E6|3FE3B628BBB5F921|BFE7DEE895252165|C0023160AEEC671C|BFD61A3738D157CC|3FEAA1DABB77E5C0|BFE23188FF686163|BFF79CE9829012B2" + - "|BFA55EF0A645CF6D|3FE3B628BBB5F921|BFD59CE6C235AA41|BFAD91DA3290F6DE|3FF2114313A5FA25|3FFCF0AE63802D0C|3FEEA7C82C0B9C0B|BFD18D3B7CD8C3A6|BF91B26166A24B6F|3FF4C84988094E5D|3FD05DCB67FA67B6|3FDF006245C5" + - "5C15|3FF2401805DB3647|3FFE437B3CB74074|3FF2751FEDCB3AE0|3FCAE6AAAEEAB99D|3FE0E4809AF005E2|3FF5102D41AC9DAD|3FE413E1CC38AA67|3FDF006245C55C15|3FEABD94CB7E990D|3FF0E29AA4868799|3FE9DDDA6607DD13|3FE6687B" + - "99D451FC|3FF234D4EE845793|3FFC35E78864E8E3|3FF2029C95B1DF7C|3F9012147F93DEF0|3FD06B925501BA14|3FF06E54B73C6CC3|3FD8D4F4961C65F8|BF91B26166A24B6F|3FE0CA91BED9C503|3FF2F08888FB10D8|3FE0B50B7B5F4D20|3FD3" + - "FCCC0E35F8B5|3FD8341733CE38B3|3FEB5FB77B00FD2B|3FDEA36F0A438CC8|BFAD91DA3290F6DD|3FE467E93D9BC1E8|3FF2F08888FB10D8|3FE5180D5B233792|3FD9B4A87E38EB03|3FF06E54B73C6CC3|3FF87DCDF6987831|3FEEFE746178AE0F|" + - "BFA83E1D6DB54B34|BFA55EF0A645CF6D|3FE1991D4D42D3E5|3FB6D4D85B6BA189|BFAD91DA3290F6DE|3FE0CA91BED9C503|3FF30991BC77E243|3FE0059CC0B47378|3FD9B4A87E38EB03|3FFA7E4C6E988F03|40028BDE18CF84B8|3FF64A1A4A733" + - "1D3|BFEC109CE5FF4DD3|3FF6097D675EDEDD|3FFCB84BB1036D9E|3FE5ACB0B4F53209|BFC3B39C0EBEDFA4|BFA55EF0A645CF6D|3FE02FE3E89D37DF|3FB938371D8961DB|3FD12B552DD9D7F8|3FDABFE7007FACB3|3FEB5FB77B00FD2B|3FDD3C0F7" + - "05CF5D8|3FE90E2EA1A907A1|3FF2114313A5FA25|3FF783F2FC0B7EB9|3FF27F31B4DE0C89|BFD2AA19EF6A5B93|3FCAE6AAAEEAB99D|3FE87EB2B87983A5|3FC5C5962448A499|BFA55EF0A645CF6D|3FE1991D4D42D3E5|3FF6A4062C69EB56|3FE5C" + - "EC38441F82B|3FE3AFD6F750B787|3FED8E35DDDD6B56|3FF234D4EE845793|3FED79CD94851F56|3FEC32F4CF4A558F|3FF234D4EE845793|3FFCBB85302B1600|3FF43AE22B1F67C6|3FDEF41446DEE1A3|3FEC32F4CF4A558F|3FF1C9D36DD0BDF3|3" + - "FEBA92E0C12C238|3FDC1F953BE6E0F5|3FEAFA34DF85B70F|3FF1C9D36DD0BDF3|3FE8947EFF033CE5|3FDABFE7007FACB3|3FEB3A04B8F757E5|3FED8E35DDDD6B56|3FE7407D099E3FDC|3FD3FCCC0E35F8B5|3FE26A769100EEBD|3FF1C9D36DD0BD" + - "F3|3FE3D6CFFEC4BA85|3FD12B552DD9D7F8|3FD8341733CE38B3|3FEFA9FEF1E306CB|3FE16D541618775D|3FE8B5669433A925|3FF0E29AA4868799|3FFC02D7B385A8F7|3FF1A23236F48A04|3FE6687B99D451FC|3FEABD94CB7E990D|3FF0E29AA4" + - "868799|3FEBB9DF55C2AAFB|"; + "3FE9889E69E2F2A9|3FF6A4062C69EB56|3FFE5BA1712962FB|3FF66AC23B91B835|3FD9B4A87E38EB03|3FF06E54B73C6CC3|3FF87DCDF6987831|3FEDDABA28118F23|3FE337C563CCD90C|3FEE57C080E41615|3FF6A4062C69EB56|3FEF3616CACFF911|3FE02FE3E89D37DF" + + "|3FEB5FB77B00FD2B|3FF275749C8A0EDC|3FEDA46C0324F819|3FEC945E61344D65|3FF783F2FC0B7EB9|3FFE70E9D51B4FE8|3FF5F1DB2BE91CC8|BFF79CE9829012B2|BF9AE56B5436E3ED|3FEC0EACE14A0760|BF91227FF018E254|BFAD91DA3290F6DE|3FE467E93D9BC1E" + + "8|3FF50553CC85D517|3FE5146DC33BA47B|BFF79CE9829012B2|3FFA7E4C6E988F03|3FFDC26FE4697691|3FF17A46774E3974|BFEC109CE5FF4DD3|3FEC945E61344D65|3FFE70E9D51B4FE8|3FECB2980CADE3A1|3FD9B4A87E38EB03|3FFA7E4C6E988F03|40028BDE18CF84" + + "B8|3FF674943D6DD8A4|3FD12B552DD9D7F8|3FE8A01C14B39645|3FF4C84988094E5D|3FE54AE2B56C5DE6|3FDE0A4A5BC3CB1B|3FF191F8CB834BA4|3FF783F2FC0B7EB9|3FF13BE378CA0F10|BF9AE56B5436E3ED|3FD8341733CE38B3|3FED3ECCE9FE0B08|3FE05C7B8BA10" + + "F5A|BFC3B39C0EBEDFA4|3FDA36D945811391|3FEE57C080E41615|3FDCEDB666CED533|BF9AE56B5436E3ED|3FEC0EACE14A0760|3FFDC26FE4697691|3FEA97CC98A91277|3FEC0EACE14A0760|3FF7EF9ACD4B4CDA|3FFDC26FE4697691|3FF5447F3601CBAC|3FE467E93D9B" + + "C1E8|3FF2114313A5FA25|3FF783F2FC0B7EB9|3FF1A3606669E58C|3FE6687B99D451FC|3FF4C84988094E5D|3FF7579900ED65CA|3FF22703F1D9A448|BFA55EF0A645CF6D|3FDD78F9158019EC|3FE7608F6C9A0728|3FD8B8836359643A|3FE6687B99D451FC|3FF139CDA6B" + + "C6CD8|3FFCB84BB1036D9D|3FF23C7E1EECE783|3F9012147F93DEF0|3FE87EB2B87983A5|3FF6A4062C69EB55|3FEA2CD6BC0C7CA8|BFAD91DA3290F6DE|3FF6097D675EDEDE|3FFE70E9D51B4FE8|3FF4205C9EA77869|3FF191F8CB834BA4|3FF7579900ED65CA|3FFF59FB71" + + "4FF968|3FF803DD345BCE5F|3FE0CA91BED9C503|3FEFA9FEF1E306CB|3FFCF0AE63802D0C|3FF12721A5D71188|BFA55EF0A645CF6D|3FD06B925501BA14|3FE1991D4D42D3E5|3FCA3DC3D5D4C98D|3FD9B4A87E38EB03|3FFA7E4C6E988F03|40028BDE18CF84B8|3FF725B5A" + + "525DA7C|3FEC945E61344D65|3FF191F8CB834BA4|3FF783F2FC0B7EB9|3FF1CF4B65AB96C2|3FEC945E61344D65|3FF191F8CB834BA4|3FFCF0AE63802D0C|3FF36D9175E6E86E|3FDCF0D22FE31C9A|3FEB3A04B8F757E6|3FF1C9D36DD0BDF3|3FEB3112E97EC3A2|BFA55EF0" + + "A645CF6D|3FDABFE7007FACB3|3FE5DF970EFAC4AD|3FD090EF92803B02|3FDF006245C55C15|3FEC9B0D6771A93B|3FF4C84988094E5D|3FEE3B0AAE948636|3FDF006245C55C15|3FEC9B0D6771A93B|3FFAFC1F354F6D26|3FEF64CA9316B483|BFD61A3738D157CC|3FD6790" + + "13DEDA159|3FE0CA91BED9C503|3FC482464AE96587|BFD2AA19EF6A5B93|BF9AE56B5436E3ED|3FEB3A04B8F757E6|3FA8B7B79B451270|BFF79CE9829012B2|3FEDE65BB45BBCBF|3FFDC26FE4697691|3FEB8C44F29E7BE1|C0023160AEEC671C|3FD679013DEDA159|3FFE70" + + "E9D51B4FE8|3FB2537E7DF08273|C0023160AEEC671C|BFE8178FD41DF9E6|3FE3B628BBB5F921|BFE7EAFA45A13497|C0023160AEEC671C|BFD61A3738D157CC|3FEAA1DABB77E5C0|BFE201A90C0E9231|BFF79CE9829012B2|BFA55EF0A645CF6D|3FE3B628BBB5F921|BFD5B" + + "B1DFF2AA98E|BFAD91DA3290F6DE|3FF2114313A5FA25|3FFCF0AE63802D0C|3FEEA9F5B2A55355|BFD18D3B7CD8C3A6|BF91B26166A24B6F|3FF4C84988094E5D|3FD05C5544884BD2|3FDF006245C55C15|3FF2401805DB3647|3FFE437B3CB74074|3FF271DE292049B3|3FCA" + + "E6AAAEEAB99D|3FE0E4809AF005E2|3FF2FFAB1C009A1B|3FE4138C664E6A6C|3FDF006245C55C15|3FEABD94CB7E990D|3FF0E29AA4868799|3FE9DCEBEAF05CBE|3FE6687B99D451FC|3FF234D4EE845793|3FFC35E78864E8E3|3FF200912962C3EF|3F9012147F93DEF0|3FD" + + "06B925501BA14|3FF06E54B73C6CC3|3FD8CC8A83C55085|BF91B26166A24B6F|3FE0CA91BED9C503|3FF2F08888FB10D8|3FE0B6887CDDC18B|3FD3FCCC0E35F8B5|3FD8341733CE38B3|3FEB5FB77B00FD2B|3FDEA11F78CDE888|BFAD91DA3290F6DD|3FE467E93D9BC1E8|3F" + + "F2F08888FB10D8|3FE5160D6F8D5552|3FD9B4A87E38EB03|3FF06E54B73C6CC3|3FF87DCDF6987831|3FEF08B88CB1A602|BFA83E1D6DB54B34|BFA55EF0A645CF6D|3FE1991D4D42D3E5|3FB6F1C775FD5F99|BFAD91DA3290F6DE|3FE0CA91BED9C503|3FF30991BC77E243|3" + + "FDFF82E31FA4ACE|3FD9B4A87E38EB03|3FFA7E4C6E988F03|40028BDE18CF84B8|3FF6489CCDB7A0CC|BFEC109CE5FF4DD3|3FF6097D675EDEDD|3FFCB84BB1036D9E|3FE5A974BAAC81C7|BFC3B39C0EBEDFA4|BFA55EF0A645CF6D|3FE02FE3E89D37DF|3FB8FE576BBC2E8E|" + + "3FD12B552DD9D7F8|3FDABFE7007FACB3|3FEB5FB77B00FD2B|3FDD3BA0A00CEA39|3FE90E2EA1A907A1|3FF2114313A5FA25|3FF783F2FC0B7EB9|3FF27BA30F718777|BFD2AA19EF6A5B93|3FCAE6AAAEEAB99D|3FE87EB2B87983A5|3FC5AFD9DF6DDB06|BFA55EF0A645CF6D" + + "|3FE1991D4D42D3E5|3FF6A4062C69EB56|3FE5CEC045B528DD|3FE3AFD6F750B787|3FED8E35DDDD6B56|3FF234D4EE845793|3FED7A9FC5D312AE|3FEC32F4CF4A558F|3FF234D4EE845793|3FFCBB85302B1600|3FF438EC1098544A|3FDF006245C55C15|3FEC32F4CF4A558" + + "F|3FF1C9D36DD0BDF3|3FEBA6AB647B34A4|3FDC1F953BE6E0F5|3FEAFA34DF85B70F|3FF1C9D36DD0BDF3|3FE89643DD295870|3FDABFE7007FACB3|3FEB3A04B8F757E5|3FED8E35DDDD6B56|3FE73ECB6E7D3205|3FD3FCCC0E35F8B5|3FE26A769100EEBD|3FF1C9D36DD0BD" + + "F3|3FE3D5A5021D03D0|3FD12B552DD9D7F8|3FD8341733CE38B3|3FEFA9FEF1E306CB|3FE16D1E9FCE7503|3FE8B5669433A925|3FF0E29AA4868799|3FFC02D7B385A8F7|3FF1A58492EDF747|3FE6687B99D451FC|3FEABD94CB7E990D|3FF0E29AA4868799|3FEBB8DD199E5" + + "19B|"; private static readonly double[] OracleX0 = { 4.744781, 6.255026, 1.160139, 8.741791, 0.319224, 1.201623, 7.703759, 8.249999, 7.763458, 2.561846, 7.844684, 9.530505, 5.237435, 7.938164, 7.634402, 3.538497, 3.908757, 0.798954, 4.233405, 4.819062, 4.704432, 8.131584, 7.138561, 5.718968, 0.230697, 8.428248, 2.642866, 0.617403, 9.190608, 5.605545, 9.120073, 5.506271, 1.210414, 7.754063, 6.823279, 8.004035, 7.097406, 5.353946, 3.109105, 1.01253 }; private static readonly double[] OracleX1 = { 6.041412, 1.540177, 0.705583, 2.134676, 3.987951, 8.706857, 4.997143, 7.688693, 7.269118, 9.894784, 2.850013, 2.908263, 4.640485, 9.239211, 2.437277, 9.911633, 5.759533, 1.255405, 2.324209, 0.226532, 2.977218, 1.713005, 4.749645, 2.982202, 8.679028, 2.531092, 8.802673, 1.569402, 4.644899, 2.859959, 8.467362, 0.291196, 5.153139, 1.003399, 3.90648, 6.822431, 9.421524, 6.81047, 2.76754, 4.48085 }; @@ -302,5 +300,45 @@ public void Test_DecisionTree_BestSplitMatchesExactOracle() Assert.AreEqual(OracleBestFeature, dt.Root.FeatureIndex); Assert.AreEqual(BitConverter.DoubleToInt64Bits(OracleBestThreshold), BitConverter.DoubleToInt64Bits(dt.Root.Threshold)); } + + /// + /// Equal predictor keys retain their node input order so the Youngs-Cramer response + /// accumulation and its selected threshold are reproducible across runtimes. + /// + [TestMethod] + public void Test_DecisionTree_EqualPredictorKeysRetainNodeOrder() + { + var x = new Matrix(new List + { + new double[] { 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2 } + }); + var y = new Vector(new double[] + { + 999902848968.3931, + 1000000117649.4011, + 1000097151031.6069, + 999896129867.9054, + 999995338077.3125, + 1000103870132.0946, + 999901810715.8024, + 999996663357.6908, + 1000098189284.1976, + 999898807264.0746, + 1000003336642.3092, + 1000101192735.9254, + 999896810273.0493, + 1000004661922.6875, + 1000103189726.9507, + 999901331037.1741, + 999999882350.5989, + 1000098668962.8259 + }); + var dt = new DecisionTree(x, y, 12345) { Features = 1 }; + + dt.Train(); + + Assert.AreEqual(0d, dt.Root.Threshold); + } + } } diff --git a/Test_Numerics/Utilities/Test_SafeProgressReporter.cs b/Test_Numerics/Utilities/Test_SafeProgressReporter.cs index f0cb8c54..56360cfd 100644 --- a/Test_Numerics/Utilities/Test_SafeProgressReporter.cs +++ b/Test_Numerics/Utilities/Test_SafeProgressReporter.cs @@ -1,5 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Utilities; +using System.Reflection; +using System.Threading; using System.Threading.Tasks; namespace Utilities @@ -69,5 +71,45 @@ public void Test_ChildReporters_SnapshotDuringRegistration() addTask.Wait(); Assert.HasCount(children, parent.ChildReporters); } + + /// + /// Registering a child across a cancellation reset must link the child to the replacement + /// cancellation source rather than the source that was current before registration. + /// + [TestMethod] + public void Test_CreateProgressModifier_ResetHandoffIsAtomic() + { + var parent = new SafeProgressReporter("parent"); + var lockField = typeof(SafeProgressReporter).GetField("_subProgReporterLock", BindingFlags.Instance | BindingFlags.NonPublic); + var sourceField = typeof(SafeProgressReporter).GetField("_cancellationTokenSource", BindingFlags.Instance | BindingFlags.NonPublic); + Assert.IsNotNull(lockField); + Assert.IsNotNull(sourceField); + + object registryLock = lockField.GetValue(parent)!; + SafeProgressReporter child = null!; + var registrationThread = new Thread(() => child = parent.CreateProgressModifier(1f, "child")); + bool registrationBlocked; + + Monitor.Enter(registryLock); + try + { + registrationThread.Start(); + registrationBlocked = SpinWait.SpinUntil( + () => (registrationThread.ThreadState & ThreadState.WaitSleepJoin) != 0, + 5000); + if (registrationBlocked) + sourceField.SetValue(parent, new CancellationTokenSource()); + } + finally + { + Monitor.Exit(registryLock); + } + + Assert.IsTrue(registrationBlocked, "Child registration did not reach the registry lock."); + Assert.IsTrue(registrationThread.Join(5000), "Child registration did not complete."); + + parent.RequestCancel(); + Assert.IsTrue(child.IsCancelRequested, "The child retained the cancellation source from before the reset handoff."); + } } } From d24db6862d36894969670bd94d16bda2804ca39e Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 29 Aug 2026 09:46:05 -0600 Subject: [PATCH 168/222] Restore bootstrap summary binary signatures --- .../Uncertainty Analysis/BootstrapAnalysis.cs | 28 +++++++++++++++---- .../Univariate/Test_BootstrapAnalysis.cs | 15 ++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs b/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs index 058320d1..c4ec8ef6 100644 --- a/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs +++ b/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs @@ -270,8 +270,17 @@ public ParameterSet[] ParameterSets(IUnivariateDistribution[]? distributions = n /// Bootstrap a list of quantiles given the input non-exceedance probabilities. /// /// List of non-exceedance probabilities. - /// Optional. Pass in an array of bootstrapped distributions. Default = null. - public double[,] Quantiles(IList probabilities, IUnivariateDistribution[]? distributions = null) + public double[,] Quantiles(IList probabilities) + { + return Quantiles(probabilities, null); + } + + /// + /// Computes quantiles from a supplied array of bootstrapped distributions. + /// + /// List of non-exceedance probabilities. + /// The bootstrapped distributions, or null to generate them. + public double[,] Quantiles(IList probabilities, IUnivariateDistribution[]? distributions) { var bootDistributions = distributions != null ? distributions : Distributions(); var Output = new double[bootDistributions.Length, probabilities.Count]; @@ -288,8 +297,17 @@ public ParameterSet[] ParameterSets(IUnivariateDistribution[]? distributions = n /// Bootstrap a list of non-exceedance probabilities given the input quantile values. /// /// List quantile values. - /// Optional. Pass in an array of bootstrapped distributions. Default = null. - public double[,] Probabilities(IList quantiles, IUnivariateDistribution[]? distributions = null) + public double[,] Probabilities(IList quantiles) + { + return Probabilities(quantiles, null); + } + + /// + /// Computes non-exceedance probabilities from a supplied array of bootstrapped distributions. + /// + /// List of quantile values. + /// The bootstrapped distributions, or null to generate them. + public double[,] Probabilities(IList quantiles, IUnivariateDistribution[]? distributions) { var bootDistributions = distributions != null ? distributions : Distributions(); var Output = new double[bootDistributions.Length, quantiles.Count]; @@ -1001,4 +1019,4 @@ private static double CubeRoot(double value) #endregion } -} \ No newline at end of file +} diff --git a/Test_Numerics/Distributions/Univariate/Test_BootstrapAnalysis.cs b/Test_Numerics/Distributions/Univariate/Test_BootstrapAnalysis.cs index b33c5ed6..4f5cdd3a 100644 --- a/Test_Numerics/Distributions/Univariate/Test_BootstrapAnalysis.cs +++ b/Test_Numerics/Distributions/Univariate/Test_BootstrapAnalysis.cs @@ -356,6 +356,21 @@ public void Test_UsesOnlySuccessfulFits_AndRejectsAllFailures() Assert.HasCount(2, aggregate.InnerExceptions); } + /// + /// Verifies that the released one-argument bootstrap summary method tokens remain + /// available to already-compiled consumers. + /// + [TestMethod] + public void Test_OneArgumentSummaryMethods_RetainBinarySignatures() + { + Type listType = typeof(IList); + + Assert.IsNotNull(typeof(BootstrapAnalysis).GetMethod( + nameof(BootstrapAnalysis.Quantiles), new[] { listType })); + Assert.IsNotNull(typeof(BootstrapAnalysis).GetMethod( + nameof(BootstrapAnalysis.Probabilities), new[] { listType })); + } + /// /// Test that the normal-approximation quantile interval preserves the sign of negative /// quantiles: the transform applied around the point estimate must remain finite and From 9b8e0aed1815c195cf03a4194431adaaf6495fde Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 29 Aug 2026 09:53:35 -0600 Subject: [PATCH 169/222] Disambiguate ensemble index sampling --- Numerics/Functions/EnsembleFunction.cs | 4 +-- .../Functions/Test_EnsembleFunction.cs | 34 +++++++++++++------ docs/functions/index.md | 2 +- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/Numerics/Functions/EnsembleFunction.cs b/Numerics/Functions/EnsembleFunction.cs index afb805fd..e19b9946 100644 --- a/Numerics/Functions/EnsembleFunction.cs +++ b/Numerics/Functions/EnsembleFunction.cs @@ -96,7 +96,7 @@ public IReadOnlyList ParameterSets /// The posterior index in [0, Count). /// A new configured function instance. /// Thrown when is outside [0, Count). - public IUnivariateFunction Sample(int index) + public IUnivariateFunction SampleAt(int index) { if (index < 0 || index >= _parameterSets.Length) throw new ArgumentOutOfRangeException(nameof(index), "The posterior index must be within [0, Count)."); @@ -120,7 +120,7 @@ public IUnivariateFunction Sample(double percentile) throw new ArgumentOutOfRangeException(nameof(percentile), "The percentile must be between 0 and 1."); int index = (int)Math.Floor(percentile * _parameterSets.Length); if (index > _parameterSets.Length - 1) index = _parameterSets.Length - 1; - return Sample(index); + return SampleAt(index); } /// diff --git a/Test_Numerics/Functions/Test_EnsembleFunction.cs b/Test_Numerics/Functions/Test_EnsembleFunction.cs index 65d96844..e4bd652b 100644 --- a/Test_Numerics/Functions/Test_EnsembleFunction.cs +++ b/Test_Numerics/Functions/Test_EnsembleFunction.cs @@ -43,8 +43,8 @@ public void Test_Sample_ByIndex() var ensemble = BuildEnsemble(); Assert.AreEqual(3, ensemble.Count); - var first = (SegmentedPowerFunction)ensemble.Sample(0); - var second = (SegmentedPowerFunction)ensemble.Sample(1); + var first = (SegmentedPowerFunction)ensemble.SampleAt(0); + var second = (SegmentedPowerFunction)ensemble.SampleAt(1); Assert.AreEqual(1.0d, first.GetBreakpoint(1), 0); Assert.AreEqual(0.9d, second.GetBreakpoint(1), 0); Assert.AreEqual(0.12d, second.Sigma, 0); @@ -52,11 +52,11 @@ public void Test_Sample_ByIndex() // Mutating one clone never touches another draw of the same index. second.SetParameters(new[] { 5d, 5d, 5d, 5d }); - var secondAgain = (SegmentedPowerFunction)ensemble.Sample(1); + var secondAgain = (SegmentedPowerFunction)ensemble.SampleAt(1); Assert.AreEqual(0.9d, secondAgain.GetBreakpoint(1), 0, "Clones must be independent."); - Assert.Throws(() => ensemble.Sample(-1)); - Assert.Throws(() => ensemble.Sample(3)); + Assert.Throws(() => ensemble.SampleAt(-1)); + Assert.Throws(() => ensemble.SampleAt(3)); } /// @@ -74,6 +74,20 @@ public void Test_Sample_ByPercentile() Assert.Throws(() => ensemble.Sample(1.1)); } + /// + /// Verifies that an integer percentile literal uses percentile sampling instead of + /// silently binding to an index overload. + /// + [TestMethod] + public void Test_Sample_IntegerPercentileLiteral_SelectsLastDraw() + { + var ensemble = BuildEnsemble(); + + var last = (SegmentedPowerFunction)ensemble.Sample(1); + + Assert.AreEqual(1.1d, last.GetBreakpoint(1), 0d); + } + /// /// Test the thread-safety contract: concurrent sampling shares no mutable state, so /// every parallel draw evaluates exactly its own parameter set. @@ -87,7 +101,7 @@ public void Test_Parallel_NoSharedMutation() Parallel.For(0, 3000, i => { int index = i % 3; - var clone = (SegmentedPowerFunction)ensemble.Sample(index); + var clone = (SegmentedPowerFunction)ensemble.SampleAt(index); if (Math.Abs(clone.GetBreakpoint(1) - expected[index]) > 0d) System.Threading.Interlocked.Increment(ref failures); }); @@ -122,8 +136,8 @@ public void Test_Serialization_RoundTrip() Assert.AreEqual(original.Count, restored.Count); for (int i = 0; i < original.Count; i++) { - var a = original.Sample(i); - var b = restored.Sample(i); + var a = original.SampleAt(i); + var b = restored.SampleAt(i); a.ConfidenceLevel = 0.75; b.ConfidenceLevel = 0.75; Assert.AreEqual(a.Function(5d), b.Function(5d), 1E-12, $"Draw {i} must evaluate identically after the round-trip."); @@ -146,11 +160,11 @@ public void Test_OwnsDeepCopies_AndValidatesXmlSets() new[] { new ParameterSet(values, 1d, 0.5d) }); values[0] = 99d; - Assert.AreEqual(1d, ((SegmentedPowerFunction)ensemble.Sample(0)).GetBreakpoint(1), 0d); + Assert.AreEqual(1d, ((SegmentedPowerFunction)ensemble.SampleAt(0)).GetBreakpoint(1), 0d); ParameterSet exposed = ensemble.ParameterSets[0]; exposed.Values[0] = 88d; - Assert.AreEqual(1d, ((SegmentedPowerFunction)ensemble.Sample(0)).GetBreakpoint(1), 0d); + Assert.AreEqual(1d, ((SegmentedPowerFunction)ensemble.SampleAt(0)).GetBreakpoint(1), 0d); Assert.Throws(() => ensemble.Sample(double.NaN)); XElement invalidValues = ensemble.ToXElement(); diff --git a/docs/functions/index.md b/docs/functions/index.md index 0ca57b67..48aab3ba 100644 --- a/docs/functions/index.md +++ b/docs/functions/index.md @@ -88,7 +88,7 @@ mutable state. ```cs var ensemble = new EnsembleFunction(rating, posteriorParameterSets); -IUnivariateFunction draw = ensemble.Sample(index); // posterior draw by index +IUnivariateFunction draw = ensemble.SampleAt(index); // posterior draw by index IUnivariateFunction pDraw = ensemble.Sample(0.37); // min(⌊u·N⌋, N − 1) ``` From 75995127c90164df7e602d1f5cd5217ae9c1b21b Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 29 Aug 2026 10:01:29 -0600 Subject: [PATCH 170/222] Reject unsupported tabular ensembles --- Numerics/Functions/CompositeFunction.cs | 14 ++++++++++- Numerics/Functions/EnsembleFunction.cs | 7 ++++-- Numerics/Functions/SegmentedPowerFunction.cs | 12 ++++++++++ .../Functions/Test_EnsembleFunction.cs | 23 +++++++++++++++++++ 4 files changed, 53 insertions(+), 3 deletions(-) diff --git a/Numerics/Functions/CompositeFunction.cs b/Numerics/Functions/CompositeFunction.cs index a1147f31..65bf502b 100644 --- a/Numerics/Functions/CompositeFunction.cs +++ b/Numerics/Functions/CompositeFunction.cs @@ -171,6 +171,10 @@ public double Maximum } /// + /// + /// These are component-wise bounds only. They do not encode the requirement that all + /// weights sum to one; callers must use . + /// public double[] MinimumOfParameters { get @@ -182,6 +186,10 @@ public double[] MinimumOfParameters } /// + /// + /// These are component-wise bounds only. They do not encode the requirement that all + /// weights sum to one; callers must use . + /// public double[] MaximumOfParameters { get @@ -227,7 +235,11 @@ public bool IsDeterministic public double ConfidenceLevel { get; set; } = -1; /// - /// The parameters are the child weights. + /// + /// The parameters are the child weights. An invalid vector is rejected atomically and + /// leaves the prior weights unchanged. Call + /// to obtain the validation error. + /// public void SetParameters(IList parameters) { var validationError = ValidateParameters(parameters, false); diff --git a/Numerics/Functions/EnsembleFunction.cs b/Numerics/Functions/EnsembleFunction.cs index e19b9946..df0703c5 100644 --- a/Numerics/Functions/EnsembleFunction.cs +++ b/Numerics/Functions/EnsembleFunction.cs @@ -20,6 +20,8 @@ namespace Numerics.Functions /// concurrent samples do not share mutable function state. The constructor stores a /// serialized template snapshot, and each sample is reconstructed through /// . + /// is not supported because its uncertain paired data cannot + /// be configured through a reusable parameter vector. /// /// /// Percentile sampling maps u ∈ [0, 1] onto the index ladder as @@ -37,12 +39,14 @@ public class EnsembleFunction /// The posterior parameter sets; each must carry one value per template parameter. /// Thrown when either argument is null. /// Thrown when no parameter sets are supplied, or a set's length does not match the template. - /// Thrown when the template is not a serializable library function type. + /// Thrown when the template is tabular or is not a serializable library function type. public EnsembleFunction(IUnivariateFunction template, IList parameterSets) { if (template == null) throw new ArgumentNullException(nameof(template)); if (parameterSets == null) throw new ArgumentNullException(nameof(parameterSets)); if (parameterSets.Count == 0) throw new ArgumentException("At least one parameter set is required.", nameof(parameterSets)); + if (template is TabularFunction) + throw new NotSupportedException("TabularFunction templates cannot be configured from ensemble parameter sets."); _templateXml = SerializeTemplate(template).ToString(SaveOptions.DisableFormatting); @@ -225,7 +229,6 @@ private static XElement SerializeTemplate(IUnivariateFunction function) { if (function is LinearFunction linear) return linear.ToXElement(); if (function is PowerFunction power) return power.ToXElement(); - if (function is TabularFunction tabular) return tabular.ToXElement(); if (function is SegmentedPowerFunction segmented) return segmented.ToXElement(); if (function is CompositeFunction composite) return composite.ToXElement(); throw new NotSupportedException("The template function type '" + function.GetType().Name + "' does not support serialization."); diff --git a/Numerics/Functions/SegmentedPowerFunction.cs b/Numerics/Functions/SegmentedPowerFunction.cs index fade4bfd..033d848d 100644 --- a/Numerics/Functions/SegmentedPowerFunction.cs +++ b/Numerics/Functions/SegmentedPowerFunction.cs @@ -141,6 +141,10 @@ public double Maximum private double _maximum = double.MaxValue; /// + /// + /// These are component-wise bounds only. They do not encode the required strict ordering + /// between breakpoint parameters; callers must use . + /// public double[] MinimumOfParameters { get @@ -158,6 +162,10 @@ public double[] MinimumOfParameters } /// + /// + /// These are component-wise bounds only. They do not encode the required strict ordering + /// between breakpoint parameters; callers must use . + /// public double[] MaximumOfParameters { get @@ -211,6 +219,10 @@ public double GetBeta(int segmentOneBased) } /// + /// + /// An invalid vector is rejected atomically and leaves the previously valid state unchanged. + /// Call to obtain the validation error. + /// public void SetParameters(IList parameters) { var validationError = ValidateParameters(parameters, false); diff --git a/Test_Numerics/Functions/Test_EnsembleFunction.cs b/Test_Numerics/Functions/Test_EnsembleFunction.cs index e4bd652b..9f5af089 100644 --- a/Test_Numerics/Functions/Test_EnsembleFunction.cs +++ b/Test_Numerics/Functions/Test_EnsembleFunction.cs @@ -2,6 +2,8 @@ using System.Threading.Tasks; using System.Xml.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Data; +using Numerics.Distributions; using Numerics.Functions; using Numerics.Mathematics.Optimization; @@ -123,6 +125,27 @@ public void Test_Construction_Guards() Assert.Throws(() => new EnsembleFunction(template, new[] { new ParameterSet(new[] { 0d, 1d }, 0) })); } + /// + /// Verifies that a tabular template is rejected at construction because its parameter + /// vector cannot be applied to sampled clones. + /// + [TestMethod] + public void Test_Construction_RejectsTabularTemplate() + { + var data = new UncertainOrderedPairedData( + new[] + { + new UncertainOrdinate(0d, new Deterministic(0d)), + new UncertainOrdinate(1d, new Deterministic(1d)), + }, + true, SortOrder.Ascending, true, SortOrder.Ascending, + UnivariateDistributionType.Deterministic); + var template = new TabularFunction(data); + var sets = new[] { new ParameterSet(new[] { 0d }, 0d) }; + + Assert.Throws(() => new EnsembleFunction(template, sets)); + } + /// /// Test the XElement round-trip: the template and every posterior draw restore, and /// restored samples evaluate identically. From d6f6daa497c62e09036d26c73b98a78e154644c8 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 29 Aug 2026 10:12:51 -0600 Subject: [PATCH 171/222] Harden function XML deserialization --- Numerics/Functions/LinearFunction.cs | 37 ++++++++--- Numerics/Functions/PowerFunction.cs | 44 ++++++++++--- Numerics/Functions/TabularFunction.cs | 54 +++++++++++++--- .../Test_UnivariateFunctionFactory.cs | 61 +++++++++++++++++++ 4 files changed, 174 insertions(+), 22 deletions(-) diff --git a/Numerics/Functions/LinearFunction.cs b/Numerics/Functions/LinearFunction.cs index 0b0a7a4e..f7c66614 100644 --- a/Numerics/Functions/LinearFunction.cs +++ b/Numerics/Functions/LinearFunction.cs @@ -215,30 +215,53 @@ public XElement ToXElement() /// /// Deserializes a linear function from an XElement produced by . - /// Missing or unparseable attributes keep the default-constructed values. + /// Missing attributes keep the default-constructed values. /// /// The XElement to deserialize. /// A new . /// Thrown when is null. + /// Thrown when a present attribute is malformed or non-finite. public static LinearFunction FromXElement(XElement xElement) { if (xElement == null) throw new ArgumentNullException(nameof(xElement)); var function = new LinearFunction(); // Set the deterministic flag first: parameter validation is gated on it. - if (bool.TryParse(xElement.Attribute(nameof(IsDeterministic))?.Value, out bool isDeterministic)) + var deterministicAttribute = xElement.Attribute(nameof(IsDeterministic)); + if (deterministicAttribute != null) + { + if (!bool.TryParse(deterministicAttribute.Value, out bool isDeterministic)) + throw new ArgumentException("The serialized deterministic flag is invalid.", nameof(xElement)); function.IsDeterministic = isDeterministic; - if (double.TryParse(xElement.Attribute(nameof(Alpha))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double alpha)) + } + if (TryReadFiniteDouble(xElement, nameof(Alpha), out double alpha)) function.Alpha = alpha; - if (double.TryParse(xElement.Attribute(nameof(Beta))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double beta)) + if (TryReadFiniteDouble(xElement, nameof(Beta), out double beta)) function.Beta = beta; - if (double.TryParse(xElement.Attribute(nameof(Sigma))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double sigma)) + if (TryReadFiniteDouble(xElement, nameof(Sigma), out double sigma)) function.Sigma = sigma; - if (double.TryParse(xElement.Attribute(nameof(Minimum))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double minimum)) + if (TryReadFiniteDouble(xElement, nameof(Minimum), out double minimum)) function.Minimum = minimum; - if (double.TryParse(xElement.Attribute(nameof(Maximum))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double maximum)) + if (TryReadFiniteDouble(xElement, nameof(Maximum), out double maximum)) function.Maximum = maximum; return function; } + /// Reads an optional finite double attribute. + private static bool TryReadFiniteDouble(XElement xElement, string attributeName, out double value) + { + var attribute = xElement.Attribute(attributeName); + if (attribute == null) + { + value = 0d; + return false; + } + if (!double.TryParse(attribute.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out value) + || !Tools.IsFinite(value)) + { + throw new ArgumentException("The serialized " + attributeName + " value is invalid.", nameof(xElement)); + } + return true; + } + } } diff --git a/Numerics/Functions/PowerFunction.cs b/Numerics/Functions/PowerFunction.cs index 7f24fe87..f85b8f39 100644 --- a/Numerics/Functions/PowerFunction.cs +++ b/Numerics/Functions/PowerFunction.cs @@ -272,32 +272,60 @@ public XElement ToXElement() /// /// Deserializes a power function from an XElement produced by . - /// Missing or unparseable attributes keep the default-constructed values. + /// Missing attributes keep the default-constructed values. /// /// The XElement to deserialize. /// A new . /// Thrown when is null. + /// Thrown when a present attribute is malformed or non-finite. public static PowerFunction FromXElement(XElement xElement) { if (xElement == null) throw new ArgumentNullException(nameof(xElement)); var function = new PowerFunction(); // Set the deterministic flag first: parameter validation is gated on it. - if (bool.TryParse(xElement.Attribute(nameof(IsDeterministic))?.Value, out bool isDeterministic)) + var deterministicAttribute = xElement.Attribute(nameof(IsDeterministic)); + if (deterministicAttribute != null) + { + if (!bool.TryParse(deterministicAttribute.Value, out bool isDeterministic)) + throw new ArgumentException("The serialized deterministic flag is invalid.", nameof(xElement)); function.IsDeterministic = isDeterministic; - if (bool.TryParse(xElement.Attribute(nameof(IsInverse))?.Value, out bool isInverse)) + } + var inverseAttribute = xElement.Attribute(nameof(IsInverse)); + if (inverseAttribute != null) + { + if (!bool.TryParse(inverseAttribute.Value, out bool isInverse)) + throw new ArgumentException("The serialized inverse flag is invalid.", nameof(xElement)); function.IsInverse = isInverse; - if (double.TryParse(xElement.Attribute(nameof(Alpha))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double alpha)) + } + if (TryReadFiniteDouble(xElement, nameof(Alpha), out double alpha)) function.Alpha = alpha; - if (double.TryParse(xElement.Attribute(nameof(Beta))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double beta)) + if (TryReadFiniteDouble(xElement, nameof(Beta), out double beta)) function.Beta = beta; - if (double.TryParse(xElement.Attribute(nameof(Xi))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double xi)) + if (TryReadFiniteDouble(xElement, nameof(Xi), out double xi)) function.Xi = xi; - if (double.TryParse(xElement.Attribute(nameof(Sigma))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double sigma)) + if (TryReadFiniteDouble(xElement, nameof(Sigma), out double sigma)) function.Sigma = sigma; - if (double.TryParse(xElement.Attribute(nameof(Maximum))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double maximum)) + if (TryReadFiniteDouble(xElement, nameof(Maximum), out double maximum)) function.Maximum = maximum; return function; } + /// Reads an optional finite double attribute. + private static bool TryReadFiniteDouble(XElement xElement, string attributeName, out double value) + { + var attribute = xElement.Attribute(attributeName); + if (attribute == null) + { + value = 0d; + return false; + } + if (!double.TryParse(attribute.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out value) + || !Tools.IsFinite(value)) + { + throw new ArgumentException("The serialized " + attributeName + " value is invalid.", nameof(xElement)); + } + return true; + } + } } diff --git a/Numerics/Functions/TabularFunction.cs b/Numerics/Functions/TabularFunction.cs index 541de7d5..c074d591 100644 --- a/Numerics/Functions/TabularFunction.cs +++ b/Numerics/Functions/TabularFunction.cs @@ -198,7 +198,7 @@ public XElement ToXElement() /// The XElement to deserialize. /// A new . /// Thrown when is null. - /// Thrown when the element carries no embedded uncertain ordered paired data. + /// Thrown when the element carries no embedded uncertain ordered paired data, or a present attribute is malformed, non-finite, or undefined. public static TabularFunction FromXElement(XElement xElement) { if (xElement == null) throw new ArgumentNullException(nameof(xElement)); @@ -207,19 +207,59 @@ public static TabularFunction FromXElement(XElement xElement) throw new ArgumentException("The serialized tabular function is missing its embedded UncertainOrderedPairedData.", nameof(xElement)); var function = new TabularFunction(new UncertainOrderedPairedData(tableElement)); - if (Enum.TryParse(xElement.Attribute(nameof(XTransform))?.Value, out Transform xTransform)) + var xTransformAttribute = xElement.Attribute(nameof(XTransform)); + if (xTransformAttribute != null) + { + if (!Enum.TryParse(xTransformAttribute.Value, out Transform xTransform) + || !Enum.IsDefined(typeof(Transform), xTransform)) + throw new ArgumentException("The serialized X transform is invalid.", nameof(xElement)); function.XTransform = xTransform; - if (Enum.TryParse(xElement.Attribute(nameof(YTransform))?.Value, out Transform yTransform)) + } + var yTransformAttribute = xElement.Attribute(nameof(YTransform)); + if (yTransformAttribute != null) + { + if (!Enum.TryParse(yTransformAttribute.Value, out Transform yTransform) + || !Enum.IsDefined(typeof(Transform), yTransform)) + throw new ArgumentException("The serialized Y transform is invalid.", nameof(xElement)); function.YTransform = yTransform; - if (Enum.TryParse(xElement.Attribute(nameof(Extrapolation))?.Value, out ExtrapolationSides extrapolation)) + } + var extrapolationAttribute = xElement.Attribute(nameof(Extrapolation)); + if (extrapolationAttribute != null) + { + if (!Enum.TryParse(extrapolationAttribute.Value, out ExtrapolationSides extrapolation) + || !Enum.IsDefined(typeof(ExtrapolationSides), extrapolation)) + throw new ArgumentException("The serialized extrapolation policy is invalid.", nameof(xElement)); function.Extrapolation = extrapolation; - if (bool.TryParse(xElement.Attribute(nameof(AllowNegativeYValues))?.Value, out bool allowNegative)) + } + var allowNegativeAttribute = xElement.Attribute(nameof(AllowNegativeYValues)); + if (allowNegativeAttribute != null) + { + if (!bool.TryParse(allowNegativeAttribute.Value, out bool allowNegative)) + throw new ArgumentException("The serialized negative-Y policy is invalid.", nameof(xElement)); function.AllowNegativeYValues = allowNegative; - if (double.TryParse(xElement.Attribute(nameof(Minimum))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double minimum)) + } + if (TryReadFiniteDouble(xElement, nameof(Minimum), out double minimum)) function.Minimum = minimum; - if (double.TryParse(xElement.Attribute(nameof(Maximum))?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double maximum)) + if (TryReadFiniteDouble(xElement, nameof(Maximum), out double maximum)) function.Maximum = maximum; return function; } + + /// Reads an optional finite double attribute. + private static bool TryReadFiniteDouble(XElement xElement, string attributeName, out double value) + { + var attribute = xElement.Attribute(attributeName); + if (attribute == null) + { + value = 0d; + return false; + } + if (!double.TryParse(attribute.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out value) + || !Tools.IsFinite(value)) + { + throw new ArgumentException("The serialized " + attributeName + " value is invalid.", nameof(xElement)); + } + return true; + } } } diff --git a/Test_Numerics/Functions/Test_UnivariateFunctionFactory.cs b/Test_Numerics/Functions/Test_UnivariateFunctionFactory.cs index 9f601a79..c0a9e46b 100644 --- a/Test_Numerics/Functions/Test_UnivariateFunctionFactory.cs +++ b/Test_Numerics/Functions/Test_UnivariateFunctionFactory.cs @@ -171,6 +171,67 @@ public void Test_TabularFunction_Extrapolation_ConditionalPresence() Assert.AreEqual(ExtrapolationSides.None, legacy.Extrapolation); } + /// + /// Verifies that every present linear-function attribute must contain a parseable, + /// finite value instead of silently preserving a constructor default. + /// + [TestMethod] + [DataRow(nameof(LinearFunction.IsDeterministic), "not-a-Boolean")] + [DataRow(nameof(LinearFunction.Alpha), "not-a-number")] + [DataRow(nameof(LinearFunction.Beta), "NaN")] + [DataRow(nameof(LinearFunction.Sigma), "Infinity")] + [DataRow(nameof(LinearFunction.Minimum), "not-a-number")] + [DataRow(nameof(LinearFunction.Maximum), "NaN")] + public void Test_LinearFunction_RejectsInvalidPresentAttribute(string attributeName, string value) + { + XElement element = new LinearFunction().ToXElement(); + element.SetAttributeValue(attributeName, value); + + Assert.Throws(() => LinearFunction.FromXElement(element)); + } + + /// + /// Verifies that every present power-function attribute must contain a parseable, + /// finite value instead of silently preserving a constructor default. + /// + [TestMethod] + [DataRow(nameof(PowerFunction.IsDeterministic), "not-a-Boolean")] + [DataRow(nameof(PowerFunction.IsInverse), "not-a-Boolean")] + [DataRow(nameof(PowerFunction.Alpha), "not-a-number")] + [DataRow(nameof(PowerFunction.Beta), "NaN")] + [DataRow(nameof(PowerFunction.Xi), "Infinity")] + [DataRow(nameof(PowerFunction.Sigma), "not-a-number")] + [DataRow(nameof(PowerFunction.Maximum), "NaN")] + public void Test_PowerFunction_RejectsInvalidPresentAttribute(string attributeName, string value) + { + XElement element = new PowerFunction().ToXElement(); + element.SetAttributeValue(attributeName, value); + + Assert.Throws(() => PowerFunction.FromXElement(element)); + } + + /// + /// Verifies that every present tabular-function attribute must contain a parseable, + /// finite value and that enum values must be defined. + /// + [TestMethod] + [DataRow(nameof(TabularFunction.XTransform), "999")] + [DataRow(nameof(TabularFunction.YTransform), "not-a-transform")] + [DataRow(nameof(TabularFunction.Extrapolation), "999")] + [DataRow(nameof(TabularFunction.AllowNegativeYValues), "not-a-Boolean")] + [DataRow(nameof(TabularFunction.Minimum), "NaN")] + [DataRow(nameof(TabularFunction.Maximum), "not-a-number")] + public void Test_TabularFunction_RejectsInvalidPresentAttribute(string attributeName, string value) + { + var table = new UncertainOrderedPairedData( + new[] { new UncertainOrdinate(0d, new Deterministic(0d)), new UncertainOrdinate(1d, new Deterministic(1d)) }, + true, SortOrder.Ascending, true, SortOrder.Ascending, UnivariateDistributionType.Deterministic); + XElement element = new TabularFunction(table).ToXElement(); + element.SetAttributeValue(attributeName, value); + + Assert.Throws(() => TabularFunction.FromXElement(element)); + } + /// /// Test that the factory rejects null and unknown serialized forms. /// From fb8133663b9c90a1320f56ddb59223450ea08fca Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 29 Aug 2026 10:21:39 -0600 Subject: [PATCH 172/222] Initialize empty paired data as valid --- .../Data/Paired Data/OrderedPairedData.cs | 3 ++- .../Data/Paired Data/Test_PairedData.cs | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Numerics/Data/Paired Data/OrderedPairedData.cs b/Numerics/Data/Paired Data/OrderedPairedData.cs index 14e62f53..41b0ec32 100644 --- a/Numerics/Data/Paired Data/OrderedPairedData.cs +++ b/Numerics/Data/Paired Data/OrderedPairedData.cs @@ -215,6 +215,7 @@ public OrderedPairedData(bool strictOnX, SortOrder xOrder, bool strictOnY, SortO StrictY = strictOnY; OrderX = xOrder; OrderY = yOrder; + Validate(); } /// @@ -1688,4 +1689,4 @@ private int RecursiveTolerance(int i, int lookAhead, double tolerance) #endregion } -} \ No newline at end of file +} diff --git a/Test_Numerics/Data/Paired Data/Test_PairedData.cs b/Test_Numerics/Data/Paired Data/Test_PairedData.cs index 881c6854..47a1e2a8 100644 --- a/Test_Numerics/Data/Paired Data/Test_PairedData.cs +++ b/Test_Numerics/Data/Paired Data/Test_PairedData.cs @@ -159,6 +159,25 @@ public void Test_RemoveRangeAndAddValidity() Assert.IsFalse(invalid.IsValid); } + /// + /// An empty collection is valid immediately after construction, so its first valid add or + /// insert preserves a usable collection state. + /// + [TestMethod] + public void Test_EmptyConstructor_InitializesValidCollection() + { + var added = new OrderedPairedData(false, SortOrder.Ascending, false, SortOrder.Ascending); + Assert.IsTrue(added.IsValid); + Assert.AreEqual(0, added.Count); + + added.Add(new Ordinate(1d, 10d)); + Assert.IsTrue(added.IsValid); + + var inserted = new OrderedPairedData(false, SortOrder.Ascending, false, SortOrder.Ascending); + inserted.Insert(0, new Ordinate(1d, 10d)); + Assert.IsTrue(inserted.IsValid); + } + /// /// Test the various OrderedPairedData object indexing and manipulation methods /// From eba08cb18a5ae9662cbacd3b1c5a946beb10df40 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 29 Aug 2026 10:30:05 -0600 Subject: [PATCH 173/222] Reject NaN uncertain ordinate equality --- .../Data/Paired Data/UncertainOrdinate.cs | 13 ++++++++----- .../Paired Data/Test_UncertainOrdinate.cs | 17 +++++++++-------- .../Paired Data/Test_UncertainPairedData.cs | 19 +++++++++++++++++++ 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/Numerics/Data/Paired Data/UncertainOrdinate.cs b/Numerics/Data/Paired Data/UncertainOrdinate.cs index c36e0c91..a2f0b229 100644 --- a/Numerics/Data/Paired Data/UncertainOrdinate.cs +++ b/Numerics/Data/Paired Data/UncertainOrdinate.cs @@ -280,12 +280,15 @@ public List OrdinateErrors() /// First uncertain ordinate to compare. /// Second uncertain ordinate to compare. /// True if two objects are numerically equal; otherwise, False. + /// + /// Finite X values compare within . An ordinate with + /// a NaN X value never compares equal, including to another ordinate whose X value is NaN. + /// public static bool operator ==(UncertainOrdinate left, UncertainOrdinate right) { - // Match Ordinate's equality convention for the shared X coordinate: allow a machine-epsilon - // slack, and (as Ordinate documents for its own operator) a NaN coordinate compares equal - // because the rejection test below is false for NaN. The tolerance matches Ordinate's - // convention so the two classes agree on the same conceptual coordinate. + if (double.IsNaN(left.X) || double.IsNaN(right.X)) + return false; + // Retain the established machine-epsilon comparison for finite X coordinates. if (Math.Abs(left.X - right.X) > Tools.DoubleMachineEpsilon) return false; if (left.Y is null && right.Y is null) @@ -352,4 +355,4 @@ public XElement ToXElement() #endregion } -} \ No newline at end of file +} diff --git a/Test_Numerics/Data/Paired Data/Test_UncertainOrdinate.cs b/Test_Numerics/Data/Paired Data/Test_UncertainOrdinate.cs index ceaf303a..a7c924e1 100644 --- a/Test_Numerics/Data/Paired Data/Test_UncertainOrdinate.cs +++ b/Test_Numerics/Data/Paired Data/Test_UncertainOrdinate.cs @@ -203,14 +203,12 @@ public void Test_ToXElement() } /// - /// Verify the equality operator compares X with the same machine-epsilon tolerance as Ordinate. + /// Verify the equality operator compares finite X values with the established + /// machine-epsilon tolerance and never treats NaN as equal. /// /// - /// The operator used to compare X with exact inequality while Ordinate's operator allows a - /// DoubleMachineEpsilon slack and (per its own documented convention) treats a NaN coordinate - /// as equal to anything. Before the alignment, an X pair differing by exactly one machine - /// epsilon compared unequal here and equal on Ordinate, and a NaN X compared unequal to - /// everything. Both classes now share one convention for the same conceptual X coordinate. + /// Finite X coordinates retain the epsilon comparison introduced when this type was aligned + /// with Ordinate. NaN coordinates are invalid and do not identify any ordinate. /// [TestMethod] public void Test_EqualityOperator_XComparisonMatchesOrdinateConvention() @@ -228,9 +226,12 @@ public void Test_EqualityOperator_XComparisonMatchesOrdinateConvention() var far = new UncertainOrdinate(1d, distribution); Assert.IsFalse(left == far); - // Ordinate's documented NaN convention: a NaN coordinate is equal in this test. + // An invalid NaN coordinate must not compare equal to a finite coordinate. var nan = new UncertainOrdinate(double.NaN, distribution); - Assert.IsTrue(nan == left); + var otherNan = new UncertainOrdinate(double.NaN, distribution); + Assert.IsFalse(nan == left); + Assert.IsFalse(left == nan); + Assert.IsFalse(nan == otherNan); } } } diff --git a/Test_Numerics/Data/Paired Data/Test_UncertainPairedData.cs b/Test_Numerics/Data/Paired Data/Test_UncertainPairedData.cs index 9bcff12a..94086b55 100644 --- a/Test_Numerics/Data/Paired Data/Test_UncertainPairedData.cs +++ b/Test_Numerics/Data/Paired Data/Test_UncertainPairedData.cs @@ -218,6 +218,25 @@ public void Test_IList() } + /// + /// A NaN X query must not match or remove a finite ordinate through the collection's + /// equality-based membership operations. + /// + [TestMethod] + public void Test_NaNOrdinate_DoesNotMatchCollectionMember() + { + var pairedData = new UncertainOrderedPairedData( + new[] { 1d }, new UnivariateDistributionBase[] { new Deterministic(1d) }, + false, SortOrder.Ascending, false, SortOrder.Ascending, + UnivariateDistributionType.Deterministic); + var nan = new UncertainOrdinate(double.NaN, new Deterministic(1d)); + + Assert.AreEqual(-1, pairedData.IndexOf(nan)); + Assert.IsFalse(pairedData.Contains(nan)); + Assert.IsFalse(pairedData.Remove(nan)); + Assert.AreEqual(1, pairedData.Count); + } + /// /// InsertRange validates every newly inserted position against its own neighbors. The old /// loop re-tested the constant first insertion index on every pass, so a violation carried From 122d998c04232778888d09db2441a2568de0723b Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 29 Aug 2026 10:38:22 -0600 Subject: [PATCH 174/222] Document expected probability overloads --- .../Uncertainty Analysis/BootstrapAnalysis.cs | 15 +++++++++++++-- docs/distributions/uncertainty-analysis.md | 8 +++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs b/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs index c4ec8ef6..6a130a1e 100644 --- a/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs +++ b/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs @@ -379,6 +379,11 @@ public UncertaintyAnalysisResults Estimate(IList probabilities, double a /// The probabilities to interpolate. /// Optional precomputed bootstrap distributions. /// The interpolated quantiles. + /// + /// Despite the historical method name, this overload returns one quantile for each entry in + /// . The two-argument overload instead returns mean CDF + /// probabilities evaluated at sorted quantiles. + /// public double[] ExpectedProbabilities(IList quantiles, IList probabilities, IUnivariateDistribution[]? distributions = null) { if (quantiles == null) throw new ArgumentNullException(nameof(quantiles)); @@ -475,10 +480,16 @@ private static double[] MeanCDFs(double[] quantiles, IUnivariateDistribution[] d return expected; } /// - /// Bootstrap the expected non-exceedance probabilities given the input quantile values. + /// Computes mean non-exceedance probabilities at the input quantiles. /// - /// List quantile values. + /// Quantile values, which are copied and sorted in ascending order. /// Optional. Pass in an array of bootstrapped distributions. Default = null. + /// Mean CDF probabilities corresponding to the quantiles in ascending order. + /// + /// The returned positions follow ascending quantile order, not the caller's input order. + /// The three-argument overload instead interpolates and returns quantiles at requested + /// probabilities. + /// public double[] ExpectedProbabilities(IList quantiles, IUnivariateDistribution[]? distributions = null) { var quants = quantiles.ToArray(); diff --git a/docs/distributions/uncertainty-analysis.md b/docs/distributions/uncertainty-analysis.md index 9aba68ab..abaa1f94 100644 --- a/docs/distributions/uncertainty-analysis.md +++ b/docs/distributions/uncertainty-analysis.md @@ -480,11 +480,17 @@ Console.WriteLine($"Mean τ₄: {Enumerable.Range(0, R).Average(i => lMoments[i, ## Expected Probability (Rare Events) -For very rare events, compute expected probabilities: +For very rare events, compute expected probabilities. The two-argument overload shown here copies +and sorts the quantiles, then returns their mean non-exceedance probabilities in ascending-quantile +order. It does not preserve an unsorted caller input order. By contrast, +`ExpectedProbabilities(quantiles, probabilities, distributions)` interpolates the mean bootstrap CDF +and returns a quantile for each requested probability; its historical name does not describe that +return quantity. ```cs // Quantiles of interest (e.g., design floods) var quantiles = new double[] { 15000, 20000, 25000, 30000 }; +Array.Sort(quantiles); // Match the ascending-quantile order used by the returned values. // Expected probabilities from bootstrap ensemble double[] expectedProbs = bootstrap.ExpectedProbabilities(quantiles); From 7c6184398763a58f7443cb157f72706083c45bf1 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 29 Aug 2026 10:47:33 -0600 Subject: [PATCH 175/222] Correct validation parameter names --- .../Linear Algebra/Support/Vector.cs | 14 +++++---- Numerics/Sampling/MCMC/SNIS.cs | 2 +- Test_Numerics/Sampling/MCMC/Test_SNIS.cs | 19 ++++++++++++ .../Utilities/Test_ArgumentExceptionOrder.cs | 30 +++++++++++++++---- 4 files changed, 54 insertions(+), 11 deletions(-) diff --git a/Numerics/Mathematics/Linear Algebra/Support/Vector.cs b/Numerics/Mathematics/Linear Algebra/Support/Vector.cs index 1ab21fc8..a1cfdb6f 100644 --- a/Numerics/Mathematics/Linear Algebra/Support/Vector.cs +++ b/Numerics/Mathematics/Linear Algebra/Support/Vector.cs @@ -160,9 +160,10 @@ public double NormSquared() /// /// Left-side vector. /// Right-side vector. + /// Thrown when does not have the same length as . public static double Distance(Vector A, Vector B) { - if (A.Length != B.Length) throw new ArgumentException("The vectors must be the same length.", nameof(A.Length)); + if (A.Length != B.Length) throw new ArgumentException("The vectors must be the same length.", nameof(B)); double d = 0; for (int i = 0; i < A.Length; i++) { @@ -177,9 +178,10 @@ public static double Distance(Vector A, Vector B) /// /// Left-side vector. /// Right-side vector. + /// Thrown when does not have the same length as . public static double DotProduct(Vector A, Vector B) { - if (A.Length != B.Length) throw new ArgumentException("The vectors must be the same length.", nameof(A.Length)); + if (A.Length != B.Length) throw new ArgumentException("The vectors must be the same length.", nameof(B)); double sum = 0; for (int i = 0; i < A.Length; i++) sum += A[i] * B[i]; @@ -277,9 +279,10 @@ public Vector Multiply(Matrix matrix) /// Multiply by a vector. /// /// The right-side vector. + /// Thrown when does not have the same length as this vector. public Vector Multiply(Vector vector) { - if (Length != vector.Length) throw new ArgumentException("The vectors must be the same length.", nameof(Length)); + if (Length != vector.Length) throw new ArgumentException("The vectors must be the same length.", nameof(vector)); var result = new Vector(Length); for (int i = 0; i < Length; i++) result[i] = _vector[i] * vector[i]; @@ -290,9 +293,10 @@ public Vector Multiply(Vector vector) /// Multiply by an array. /// /// The right-side array. + /// Thrown when does not have the same length as this vector. public Vector Multiply(double[] vector) { - if (Length != vector.Length) throw new ArgumentException("The vectors must be the same length.", nameof(Length)); + if (Length != vector.Length) throw new ArgumentException("The vectors must be the same length.", nameof(vector)); var result = new Vector(Length); for (int i = 0; i < Length; i++) result[i] = _vector[i] * vector[i]; @@ -427,4 +431,4 @@ public Vector Divide(double scalar) } } -} \ No newline at end of file +} diff --git a/Numerics/Sampling/MCMC/SNIS.cs b/Numerics/Sampling/MCMC/SNIS.cs index f3e2952d..96049300 100644 --- a/Numerics/Sampling/MCMC/SNIS.cs +++ b/Numerics/Sampling/MCMC/SNIS.cs @@ -69,7 +69,7 @@ protected override ParameterSet ChainIteration(int index, ParameterSet state) /// protected override void ValidateSettings() { - if (NumberOfChains != 1) throw new ArgumentException("There can only be 1 chain with this method.", nameof(InitialIterations)); + if (NumberOfChains != 1) throw new ArgumentException("There can only be 1 chain with this method.", nameof(NumberOfChains)); if (OutputLength < 100) throw new ArgumentException("The output length must be at least 100.", nameof(OutputLength)); if (Iterations < OutputLength) throw new ArgumentException("The number of iterations cannot be less than the output length.", nameof(Iterations)); if (WarmupIterations != 0) throw new ArgumentException("There are no warmup iterations with this method.", nameof(WarmupIterations)); diff --git a/Test_Numerics/Sampling/MCMC/Test_SNIS.cs b/Test_Numerics/Sampling/MCMC/Test_SNIS.cs index 6bedc43d..b7b498fc 100644 --- a/Test_Numerics/Sampling/MCMC/Test_SNIS.cs +++ b/Test_Numerics/Sampling/MCMC/Test_SNIS.cs @@ -132,6 +132,25 @@ public void Test_SNIS_MixedFiniteAndInvalidWeights_NormalizesFiniteWeights() Assert.AreEqual(1d, sum, 1e-12); } + /// + /// SNIS identifies NumberOfChains when rejecting configurations with more than one chain. + /// + [TestMethod] + public void Test_SNIS_MultipleChains_ReportsNumberOfChainsParameterName() + { + var priors = new List { new Uniform(0d, 1d) }; + var sampler = new SNIS(priors, x => 0d) + { + NumberOfChains = 2, + InitialIterations = 2, + Iterations = 100, + OutputLength = 100 + }; + + var exception = Assert.Throws(() => sampler.Sample()); + Assert.AreEqual(nameof(SNIS.NumberOfChains), exception.ParamName); + } + /// /// This test verifies that the resampling sort is stable — draws tied at the same fitness /// keep their original draw order — and that an identically-seeded run reproduces the diff --git a/Test_Numerics/Utilities/Test_ArgumentExceptionOrder.cs b/Test_Numerics/Utilities/Test_ArgumentExceptionOrder.cs index c53b615f..909b2100 100644 --- a/Test_Numerics/Utilities/Test_ArgumentExceptionOrder.cs +++ b/Test_Numerics/Utilities/Test_ArgumentExceptionOrder.cs @@ -148,16 +148,36 @@ public void Test_ExtensionMethods_ReportsParameterName() } /// - /// Verifies the corrected argument order on a representative site in . + /// Verifies static vector operations identify the incompatible right-side vector. /// [TestMethod] - public void Test_Vector_ReportsParameterName() + public void Test_Vector_StaticOperations_ReportSecondVectorParameterName() { var a = new Vector(new[] { 1d, 2d }); var b = new Vector(new[] { 1d }); - var exception = AssertThrows(() => Vector.DotProduct(a, b)); - Assert.AreEqual("Length", exception.ParamName); - StringAssert.Contains(exception.Message, "The vectors must be the same length."); + var distanceException = AssertThrows(() => Vector.Distance(a, b)); + var dotProductException = AssertThrows(() => Vector.DotProduct(a, b)); + + Assert.AreEqual("B", distanceException.ParamName); + Assert.AreEqual("B", dotProductException.ParamName); + StringAssert.Contains(distanceException.Message, "The vectors must be the same length."); + StringAssert.Contains(dotProductException.Message, "The vectors must be the same length."); + } + + /// + /// Verifies instance multiply operations identify the incompatible right-side vector. + /// + [TestMethod] + public void Test_Vector_MultiplyOperations_ReportVectorParameterName() + { + var left = new Vector(new[] { 1d, 2d }); + var vectorException = AssertThrows(() => left.Multiply(new Vector(new[] { 1d }))); + var arrayException = AssertThrows(() => left.Multiply(new[] { 1d })); + + Assert.AreEqual("vector", vectorException.ParamName); + Assert.AreEqual("vector", arrayException.ParamName); + StringAssert.Contains(vectorException.Message, "The vectors must be the same length."); + StringAssert.Contains(arrayException.Message, "The vectors must be the same length."); } /// From 4cdb8d9ee67f8f9a46638d1f68036259faa910fc Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 29 Aug 2026 10:59:04 -0600 Subject: [PATCH 176/222] Document Simpson 2D constructor guards --- Numerics/Mathematics/Integration/AdaptiveSimpsonsRule2D.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Numerics/Mathematics/Integration/AdaptiveSimpsonsRule2D.cs b/Numerics/Mathematics/Integration/AdaptiveSimpsonsRule2D.cs index 08c43865..18b0fd7a 100644 --- a/Numerics/Mathematics/Integration/AdaptiveSimpsonsRule2D.cs +++ b/Numerics/Mathematics/Integration/AdaptiveSimpsonsRule2D.cs @@ -26,6 +26,11 @@ public class AdaptiveSimpsonsRule2D : Integrator /// The maximum x-value under which the integral must be computed. /// The minimum y-value under which the integral must be computed. /// The maximum y-value under which the integral must be computed. + /// + /// Thrown when is null, is less than or + /// equal to , or is less than or equal to + /// . + /// public AdaptiveSimpsonsRule2D(Func function, double minX, double maxX, double minY, double maxY) { if (maxX <= minX) throw new ArgumentNullException(nameof(maxX), "The maximum x-value cannot be less than or equal to the minimum x-value."); From 43deb626cf85b8ee9b1c30ea6c5dd1c880376c8e Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 29 Aug 2026 11:47:31 -0600 Subject: [PATCH 177/222] Guard dense probability enumeration --- Numerics/Data/Statistics/Probability.cs | 6 +++ .../Test_ProbabilityLazyExclusive.cs | 52 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/Numerics/Data/Statistics/Probability.cs b/Numerics/Data/Statistics/Probability.cs index 3e1fb79f..9dde4497 100644 --- a/Numerics/Data/Statistics/Probability.cs +++ b/Numerics/Data/Statistics/Probability.cs @@ -1243,6 +1243,7 @@ public static double[] IndependentExclusive(IList probabilities, int[,] /// An array of probabilities for each event. /// An array of exclusive probabilities for all possible combinations of the events, assuming independence. /// Thrown if the probabilities array is null or empty. + /// Thrown when the number of event combinations exceeds the signed 32-bit array limit. public static double[] IndependentExclusive(IList probabilities) { // Validation Checks @@ -1250,6 +1251,8 @@ public static double[] IndependentExclusive(IList probabilities) throw new ArgumentException("The probabilities array must have a length greater than 0.", nameof(probabilities)); int n = probabilities.Count; + if (n >= 31) + throw new ArgumentOutOfRangeException(nameof(probabilities), "The event count is too large; the number of combinations exceeds the signed 32-bit array limit."); int f = (int)Math.Pow(2, n) - 1; // Number of non-empty subsets var result = new double[f]; int t = 0; @@ -1709,6 +1712,7 @@ public static double[] PositivelyDependentExclusive(IList probabilities, /// An array of probabilities for each event. /// An array of exclusive probabilities for each event combination, assuming perfect positive dependence. /// Thrown if the probabilities array is null, empty, or if any event combination is not valid. + /// Thrown when the number of event combinations exceeds the signed 32-bit array limit. public static double[] PositivelyDependentExclusive(IList probabilities) { // Validation Checks @@ -1716,6 +1720,8 @@ public static double[] PositivelyDependentExclusive(IList probabilities) throw new ArgumentException("The probabilities array must have a length greater than 0.", nameof(probabilities)); int n = probabilities.Count; + if (n >= 31) + throw new ArgumentOutOfRangeException(nameof(probabilities), "The event count is too large; the number of combinations exceeds the signed 32-bit array limit."); int f = (int)Math.Pow(2, n) - 1; var result = new double[f]; int t = 0; diff --git a/Test_Numerics/Data/Statistics/Test_ProbabilityLazyExclusive.cs b/Test_Numerics/Data/Statistics/Test_ProbabilityLazyExclusive.cs index 8a759d10..a06477bc 100644 --- a/Test_Numerics/Data/Statistics/Test_ProbabilityLazyExclusive.cs +++ b/Test_Numerics/Data/Statistics/Test_ProbabilityLazyExclusive.cs @@ -332,6 +332,58 @@ public void Test_DependentLazyEnumeration_ExceedsTwentyEvents() Assert.IsLessThan(Math.Pow(2d, dimension), eventProbabilities.Count); } + /// + /// Verifies dense convenience methods reject dimensions whose combination count exceeds + /// the signed 32-bit array limit before attempting exponential allocation or recursion. + /// + [TestMethod] + public void Test_DenseExclusiveConvenienceMethods_RejectInt32OverflowDimension() + { + var probabilities = new double[31]; + + Assert.Throws( + () => Probability.IndependentExclusive(probabilities)); + Assert.Throws( + () => Probability.PositivelyDependentExclusive(probabilities)); + } + + /// + /// Verifies PCM enumeration remains convergence-driven beyond the dense matrix limit and + /// returns only finite probabilities without imposing a combination cap. + /// + [TestMethod] + public void Test_LazyPCM_BeyondDenseDimensionLimit_ConvergesToProbabilities() + { + const int dimension = 32; + var probabilities = new double[dimension]; + for (int i = 0; i < dimension; i++) probabilities[i] = 5E-6d + i * 1E-8d; + double[,] correlation = CorrelationMatrix(dimension, 0.1d); + + Assert.Throws( + () => Factorial.AllCombinations(dimension)); + + double union = Probability.UnionPCMLazy( + probabilities, correlation, out var unionStatus); + Assert.AreEqual(Probability.ExclusiveEnumerationStatus.Converged, unionStatus); + Assert.IsTrue(!double.IsNaN(union) && !double.IsInfinity(union) && + union >= 0d && union <= 1d, $"UnionPCM returned {union:R}."); + + var eventProbabilities = new List(); + var eventIndicators = new List(); + var exclusiveStatus = Probability.ExclusivePCMLazy( + probabilities, correlation, eventProbabilities, eventIndicators); + + Assert.AreEqual(Probability.ExclusiveEnumerationStatus.Converged, exclusiveStatus); + Assert.HasCount(eventProbabilities.Count, eventIndicators); + Assert.IsLessThan((double)int.MaxValue, eventProbabilities.Count); + foreach (double probability in eventProbabilities) + { + Assert.IsTrue(!double.IsNaN(probability) && !double.IsInfinity(probability) && + probability >= 0d && probability <= 1d, + $"ExclusivePCM returned {probability:R}."); + } + } + /// /// Pins every new lazy API's default convergence tolerances at 1E-4. /// From 14fa353511685c41a37f566c6adf775ad2695c70 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 29 Aug 2026 12:14:23 -0600 Subject: [PATCH 178/222] Clarify Frechet lower-bound semantics --- Numerics/Data/Statistics/Probability.cs | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/Numerics/Data/Statistics/Probability.cs b/Numerics/Data/Statistics/Probability.cs index 9dde4497..e9501560 100644 --- a/Numerics/Data/Statistics/Probability.cs +++ b/Numerics/Data/Statistics/Probability.cs @@ -35,8 +35,13 @@ public enum DependencyType /// PerfectlyPositive, /// - /// Perfectly negatively dependent. + /// Uses the Fréchet–Hoeffding lower-bound convention for negative dependence. /// + /// + /// The bound is attainable by a countermonotonic coupling for two events. For three + /// or more events it is a pointwise lower bound and generally does not define an + /// attainable joint distribution or copula. + /// PerfectlyNegative, /// /// User-defined correlation matrix. @@ -242,15 +247,16 @@ public static double PositiveJointProbability(IList probabilities, int[] } /// - /// Returns the joint probability assuming perfect negative dependence. + /// Returns the Fréchet–Hoeffding lower bound for the joint probability. /// /// List of probabilities. /// The Fréchet–Hoeffding lower bound max(0, Σpᵢ − (n − 1)), where n is the number of events. /// - /// For two events with probabilities 0.8 and 0.9 the joint probability is 0.7: under perfect - /// negative dependence the events overlap only by the amount their total probability exceeds one. - /// When the probabilities sum to one or less, perfectly negatively dependent events are disjoint - /// and the joint probability is zero. + /// For two events the bound is attained by a countermonotonic coupling; for example, + /// probabilities 0.8 and 0.9 have a minimum joint probability of 0.7. For three or more + /// events this expression remains the pointwise Fréchet–Hoeffding lower bound but is + /// generally not an attainable joint distribution or copula. It must not be interpreted + /// as a globally realizable perfect-negative-dependence model in that case. /// public static double NegativeJointProbability(IList probabilities) { @@ -261,14 +267,17 @@ public static double NegativeJointProbability(IList probabilities) } /// - /// Returns the joint probability assuming perfect negative dependence. + /// Returns the Fréchet–Hoeffding lower bound over the indicated events. /// /// An array of probabilities for each event. /// An array of indicators, 0 means the event did not occur, 1 means the event did occur. /// The Fréchet–Hoeffding lower bound max(0, Σpᵢ − (k − 1)) over the indicated events, where k is the number of indicated events. /// /// Only the events whose indicator is 1 participate, matching the other joint-probability - /// overloads. With no indicated events the joint probability of the empty intersection is one. + /// overloads. With no indicated events the joint probability of the empty intersection is + /// one. For two indicated events the bound is attainable by a countermonotonic coupling; + /// for three or more it is a pointwise lower bound and generally does not define an + /// attainable joint distribution or copula. /// public static double NegativeJointProbability(IList probabilities, int[] indicators) { From 99db59a823fd007dfe51a7cac7aa39d049c9a8c2 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 29 Aug 2026 12:14:54 -0600 Subject: [PATCH 179/222] Stabilize logarithmic distribution lower tails --- Numerics/Distributions/Univariate/LnNormal.cs | 4 +- .../Distributions/Univariate/LogNormal.cs | 2 +- .../Univariate/LogPearsonTypeIII.cs | 4 +- .../Test_LogDistributionFarTails.cs | 77 +++++++++++++++++++ .../Univariate/Test_LogNormal.cs | 6 +- .../Test_SpecialFunctions.cs | 17 ++++ 6 files changed, 103 insertions(+), 7 deletions(-) create mode 100644 Test_Numerics/Distributions/Univariate/Test_LogDistributionFarTails.cs diff --git a/Numerics/Distributions/Univariate/LnNormal.cs b/Numerics/Distributions/Univariate/LnNormal.cs index 3effb1cd..6c1246eb 100644 --- a/Numerics/Distributions/Univariate/LnNormal.cs +++ b/Numerics/Distributions/Univariate/LnNormal.cs @@ -472,7 +472,7 @@ public override double CDF(double x) ValidateParameters(Mu, Sigma, true); if (x <= Minimum) return 0d; - return 0.5d * (1.0d + Erf.Function((Math.Log(x) - Mu) / (Sigma * Math.Sqrt(2.0d)))); + return Normal.StandardCDF((Math.Log(x) - Mu) / Sigma); } /// @@ -634,4 +634,4 @@ public override double[] ConditionalMoments(double a, double b) } } -} \ No newline at end of file +} diff --git a/Numerics/Distributions/Univariate/LogNormal.cs b/Numerics/Distributions/Univariate/LogNormal.cs index 5ea0f9b1..f6672346 100644 --- a/Numerics/Distributions/Univariate/LogNormal.cs +++ b/Numerics/Distributions/Univariate/LogNormal.cs @@ -527,7 +527,7 @@ public override double CDF(double x) ValidateParameters(Mu, Sigma, true); if (x <= Minimum) return 0d; - return 0.5d * (1.0d + Erf.Function((Math.Log(x, Base) - Mu) / (Sigma * Math.Sqrt(2.0d)))); + return Normal.StandardCDF((Math.Log(x, Base) - Mu) / Sigma); } /// diff --git a/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs b/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs index 37a20630..28817564 100644 --- a/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs +++ b/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs @@ -807,7 +807,7 @@ public override double CDF(double x) if (x >= Maximum) return 1d; if (Math.Abs(Gamma) <= NearZero) { - return 0.5d * (1.0d + Erf.Function((Math.Log(x, Base) - Mu) / (Sigma * Math.Sqrt(2.0d)))); + return Normal.StandardCDF((Math.Log(x, Base) - Mu) / Sigma); } else if (Beta > 0d) { @@ -1060,4 +1060,4 @@ public IList QuantileGradientForMoments(double probability) } } -} \ No newline at end of file +} diff --git a/Test_Numerics/Distributions/Univariate/Test_LogDistributionFarTails.cs b/Test_Numerics/Distributions/Univariate/Test_LogDistributionFarTails.cs new file mode 100644 index 00000000..083758eb --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Test_LogDistributionFarTails.cs @@ -0,0 +1,77 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Distributions; + +namespace Distributions.Univariate +{ + /// + /// Verifies lower-tail accuracy shared by the logarithmic normal-family distributions. + /// + [TestClass] + public class Test_LogDistributionFarTails + { + private static readonly double[] Probabilities = { 1E-17d, 1E-40d, 1E-100d }; + private static readonly double[] BaseTenQuantiles = + { + 3.207796253986819E-9d, + 4.887408373653465E-14d, + 5.327781911580459E-22d, + }; + private static readonly double[] NaturalQuantiles = + { + 2.0473517891357346E-4d, + 1.6563049483082078E-6d, + 5.768415132086823E-10d, + }; + + /// + /// Verifies the supplied distribution against externally computed lower-tail quantiles and + /// checks CDF/inverse-CDF round trips where the cancelling error-function form returns zero. + /// + /// The distribution under test. + /// The externally computed quantiles. + private static void AssertFarLowerTail(UnivariateDistributionBase distribution, double[] quantiles) + { + for (int i = 0; i < Probabilities.Length; i++) + { + double probability = Probabilities[i]; + double quantile = quantiles[i]; + double computedQuantile = distribution.InverseCDF(probability); + + Assert.AreEqual(quantile, computedQuantile, quantile * 2E-8d, + $"InverseCDF at p={probability:R}."); + Assert.AreEqual(probability, distribution.CDF(quantile), probability * 2E-9d, + $"CDF at the reference quantile for p={probability:R}."); + Assert.AreEqual(probability, distribution.CDF(computedQuantile), probability * 2E-8d, + $"CDF/InverseCDF round trip at p={probability:R}."); + } + } + + /// + /// Pins base-10 LogNormal lower-tail quantiles computed with mpmath 1.4.1 at 100 digits. + /// + [TestMethod] + public void Test_LogNormal_FarLowerTail() + { + AssertFarLowerTail(new LogNormal(0d, 1d), BaseTenQuantiles); + } + + /// + /// Pins natural-log LnNormal lower-tail quantiles computed with mpmath 1.4.1 at 100 digits. + /// + [TestMethod] + public void Test_LnNormal_FarLowerTail() + { + AssertFarLowerTail(new LnNormal { Mu = 0d, Sigma = 1d }, NaturalQuantiles); + } + + /// + /// Pins the zero-skew, base-10 LogPearson III limit against the same normal-tail oracle. + /// + [TestMethod] + public void Test_LogPearsonTypeIII_ZeroSkewFarLowerTail() + { + AssertFarLowerTail(new LogPearsonTypeIII(0d, 1d, 0d), BaseTenQuantiles); + } + } +} diff --git a/Test_Numerics/Distributions/Univariate/Test_LogNormal.cs b/Test_Numerics/Distributions/Univariate/Test_LogNormal.cs index 462e0302..8c933a70 100644 --- a/Test_Numerics/Distributions/Univariate/Test_LogNormal.cs +++ b/Test_Numerics/Distributions/Univariate/Test_LogNormal.cs @@ -252,13 +252,15 @@ public void Test_PDF() } /// - /// Testing CDF method. + /// Testing the CDF, including the finite lower-tail probability at z = -25. /// + /// The lower-tail reference was computed with mpmath 1.4.1 at 50 significant digits. [TestMethod()] public void Test_CDF() { var LogN = new LogNormal(1.5, 0.1); - Assert.AreEqual(0, LogN.CDF(0.1)); + const double lowerTail = 3.056696706382561E-138d; + Assert.AreEqual(lowerTail, LogN.CDF(0.1), lowerTail * 5E-12d); var LogN2 = new LogNormal(1.5, 1.5); Assert.AreEqual(0.11493, LogN2.CDF(0.5), 1e-05); diff --git a/Test_Numerics/Mathematics/Special Functions/Test_SpecialFunctions.cs b/Test_Numerics/Mathematics/Special Functions/Test_SpecialFunctions.cs index bd21db82..0b46bbad 100644 --- a/Test_Numerics/Mathematics/Special Functions/Test_SpecialFunctions.cs +++ b/Test_Numerics/Mathematics/Special Functions/Test_SpecialFunctions.cs @@ -412,5 +412,22 @@ public void Test_NormalCDF_FarTail() Assert.AreEqual(5.725571222524577E-300, d.CDF(-37.0), 5.725571222524577E-300 * 5E-12); } + /// + /// Pins the standard-normal CDF through its moderate range after routing it through the + /// MVNPHI implementation. + /// + /// + /// Reference values were computed with Python mpmath 1.4.1 at 50 significant digits. + /// + [TestMethod] + public void Test_NormalCDF_ModerateRange() + { + Assert.AreEqual(0.0013498980316300945d, Numerics.Distributions.Normal.StandardCDF(-3d), 5E-13d); + Assert.AreEqual(0.15865525393145707d, Numerics.Distributions.Normal.StandardCDF(-1d), 5E-13d); + Assert.AreEqual(0.5d, Numerics.Distributions.Normal.StandardCDF(0d), 5E-13d); + Assert.AreEqual(0.8413447460685429d, Numerics.Distributions.Normal.StandardCDF(1d), 5E-13d); + Assert.AreEqual(0.9986501019683699d, Numerics.Distributions.Normal.StandardCDF(3d), 5E-13d); + } + } } From 23dd96bf2be300c1692b9d3571b5b29e91c68ce5 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 29 Aug 2026 12:15:17 -0600 Subject: [PATCH 180/222] Make EVD effective sample size scale invariant --- .../Linear Algebra/EigenValueDecomposition.cs | 18 ++++++-- .../Test_EigenValueDecomposition.cs | 44 +++++++++++++++++++ 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/Numerics/Mathematics/Linear Algebra/EigenValueDecomposition.cs b/Numerics/Mathematics/Linear Algebra/EigenValueDecomposition.cs index 62a9d6dd..748c59c7 100644 --- a/Numerics/Mathematics/Linear Algebra/EigenValueDecomposition.cs +++ b/Numerics/Mathematics/Linear Algebra/EigenValueDecomposition.cs @@ -139,19 +139,31 @@ public EigenValueDecomposition(Matrix A) /// /// Returns the effective sample size based on Dutilleul's method (1993). /// + /// The scale-invariant effective sample size, or zero for an all-zero spectrum. + /// + /// Eigenvalues are normalized by the largest absolute eigenvalue before evaluating + /// (Σλ)² / Σλ². Negative eigenvalues no larger than 1E-10 of that spectral + /// scale are treated as numerical roundoff; materially negative eigenvalues retain the + /// established formula behavior. + /// public double EffectiveSampleSize() { + double spectralScale = 0d; + for (int i = 0; i < EigenValues.Length; i++) + spectralScale = Math.Max(spectralScale, Math.Abs(EigenValues[i])); + if (spectralScale == 0d) return 0d; + double sum = 0; double sumsq = 0; for (int i = 0; i < EigenValues.Length; i++) { - // Clip tiny negative eigenvalues that can appear from numerical error - double lambda = EigenValues[i]; + // Normalize first so the roundoff threshold and ESS are independent of matrix scale. + double lambda = EigenValues[i] / spectralScale; if (lambda < 0.0 && Math.Abs(lambda) <= 1e-10) lambda = 0.0; sum += lambda; sumsq += lambda * lambda; } - if (sumsq <= 1E-12) return 0.0; // degenerate case + if (sumsq == 0d) return 0d; double neff = (sum * sum) / sumsq; return neff; } diff --git a/Test_Numerics/Mathematics/Linear Algebra/Test_EigenValueDecomposition.cs b/Test_Numerics/Mathematics/Linear Algebra/Test_EigenValueDecomposition.cs index 8ff1f1e5..02f7da63 100644 --- a/Test_Numerics/Mathematics/Linear Algebra/Test_EigenValueDecomposition.cs +++ b/Test_Numerics/Mathematics/Linear Algebra/Test_EigenValueDecomposition.cs @@ -317,5 +317,49 @@ public void Test_ScaleInvariantConvergence_And_InputPreserved() } } + /// + /// Verifies Dutilleul effective sample size is invariant to covariance scale, including + /// scales below the former absolute degeneracy threshold. + /// + [TestMethod] + public void Test_EffectiveSampleSize_IsScaleInvariant() + { + const double expected = 1.8d; // (1 + 2)^2 / (1^2 + 2^2) + foreach (double scale in new double[] { 1E-9d, 1d, 1E+6d }) + { + var matrix = new Matrix(new double[,] + { + { scale, 0d }, + { 0d, 2d * scale }, + }); + + double actual = new EigenValueDecomposition(matrix).EffectiveSampleSize(); + Assert.AreEqual(expected, actual, 1E-12d, $"scale={scale:R}"); + } + } + + /// + /// Verifies the zero spectrum remains degenerate and tiny negative roundoff is clipped + /// relative to the spectrum rather than by an absolute eigenvalue threshold. + /// + [TestMethod] + public void Test_EffectiveSampleSize_HandlesZeroAndRelativeNegativeRoundoff() + { + Assert.AreEqual(0d, + new EigenValueDecomposition(new Matrix(new double[2, 2])).EffectiveSampleSize(), 0d); + + foreach (double scale in new double[] { 1E-9d, 1d, 1E+6d }) + { + var matrix = new Matrix(new double[,] + { + { scale, 0d }, + { 0d, -5E-11d * scale }, + }); + + double actual = new EigenValueDecomposition(matrix).EffectiveSampleSize(); + Assert.AreEqual(1d, actual, 1E-12d, $"scale={scale:R}"); + } + } + } } From c082c9de0839842ccd642b9d2e7173fa9a202e1e Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 29 Aug 2026 12:42:00 -0600 Subject: [PATCH 181/222] Route custom-weight network detours consistently --- .../Optimization/Dynamic/Network.cs | 62 ++++++++++++++++++- .../Optimization/Dynamic/Test_Network.cs | 27 ++++++++ 2 files changed, 86 insertions(+), 3 deletions(-) diff --git a/Numerics/Mathematics/Optimization/Dynamic/Network.cs b/Numerics/Mathematics/Optimization/Dynamic/Network.cs index c9c02452..5dc7f480 100644 --- a/Numerics/Mathematics/Optimization/Dynamic/Network.cs +++ b/Numerics/Mathematics/Optimization/Dynamic/Network.cs @@ -389,7 +389,9 @@ private void EnsureScratch() /// /// When the table's recorded route from the start node avoids every excluded edge it is /// returned directly — exclusions only remove paths, so a surviving unexcluded optimum - /// stays optimal. Otherwise the path is re-solved with the exclusions applied. + /// stays optimal. Otherwise the path is re-solved with the exclusions applied using the + /// edge weights supplied to the constructor. Use the four-argument overload when + /// was solved with custom weights. /// /// Thrown when the edge indices or table are null. /// Thrown when the table dimensions are not [, 3]. @@ -425,6 +427,57 @@ private void EnsureScratch() return FindDetourPath(removed, startNodeIndex) ?? new List(); } + /// + /// Finds an alternative path avoiding the specified edges, using a pre-computed + /// custom-weight result table to skip the solve when the recorded route is unaffected. + /// + /// Edge indices to exclude from the path. The array is not modified; every edge bearing a listed index is excluded. + /// The starting node index. + /// A result table previously solved on this network with , toward its destinations and without exclusions. + /// Custom weights, one per edge, positional with the constructor's edge array. + /// + /// The ordered edge indices from the start node to its nearest network destination + /// avoiding the excluded edges; an empty list when the start node is itself a + /// destination or when no path exists. + /// + /// + /// The existing table and custom weights must describe the same solve. When the table's + /// recorded route is blocked, the detour is re-solved with . + /// + /// Thrown when the edge indices, table, or weights are null. + /// Thrown when the table dimensions are not [, 3] or the weight count does not equal the edge count. + /// Thrown when the start node is outside the network. + public List GetPath(int[] edgesToRemove, int startNodeIndex, float[,] existingResultsTable, float[] edgeWeights) + { + if (edgesToRemove == null) throw new ArgumentNullException(nameof(edgesToRemove)); + ValidateResultTable(existingResultsTable); + ValidateWeights(edgeWeights); + if (startNodeIndex < 0 || startNodeIndex >= _nodeCount) + throw new ArgumentOutOfRangeException(nameof(startNodeIndex), $"The start node index must be within [0, {_nodeCount})."); + + if (float.IsPositiveInfinity(existingResultsTable[startNodeIndex, 2])) return new List(); + + var removed = new HashSet(); + for (int i = 0; i < edgesToRemove.Length; i++) removed.Add(edgesToRemove[i]); + + List? recorded = Dijkstra.GetPath(existingResultsTable, startNodeIndex); + if (recorded != null) + { + bool blocked = false; + for (int i = 0; i < recorded.Count; i++) + { + if (removed.Contains(recorded[i])) + { + blocked = true; + break; + } + } + if (!blocked) return recorded; + } + + return FindDetourPath(removed, startNodeIndex, edgeWeights) ?? new List(); + } + /// /// Runs a forward Dijkstra search from the start node over the outgoing adjacency, /// skipping excluded edges, stopping at the first settled destination (the nearest one), @@ -432,8 +485,9 @@ private void EnsureScratch() /// /// The excluded edge indices. /// The starting node index. + /// Optional custom weights positional with the constructor's edge array; null uses the constructor weights. /// The ordered edge indices to the nearest destination; an empty list when the start node is a destination; null when every destination is unreachable. - private List? FindDetourPath(HashSet removed, int startNodeIndex) + private List? FindDetourPath(HashSet removed, int startNodeIndex, float[]? edgeWeights = null) { if (_isDestination[startNodeIndex]) return new List(); @@ -456,6 +510,7 @@ private void EnsureScratch() int[] toNodes = _outgoingAdjacency.ToNode; float[] weights = _outgoingAdjacency.Weight; int[] edgeIndexes = _outgoingAdjacency.EdgeIndex; + int[]? sourcePositions = _outgoingAdjacency.SourcePosition; int reachedDestination = -1; while (heap.Count > 0) @@ -475,7 +530,8 @@ private void EnsureScratch() { if (removed.Contains(edgeIndexes[k])) continue; int to = toNodes[k]; - float newCost = cost + weights[k]; + float weight = edgeWeights == null ? weights[k] : edgeWeights[sourcePositions![k]]; + float newCost = cost + weight; if (newCost < dist[to]) { dist[to] = newCost; diff --git a/Test_Numerics/Mathematics/Optimization/Dynamic/Test_Network.cs b/Test_Numerics/Mathematics/Optimization/Dynamic/Test_Network.cs index ab66f2a2..4c278980 100644 --- a/Test_Numerics/Mathematics/Optimization/Dynamic/Test_Network.cs +++ b/Test_Numerics/Mathematics/Optimization/Dynamic/Test_Network.cs @@ -406,6 +406,31 @@ public void GetPathFastPathMatchesFullSolve() CollectionAssert.AreEqual(direct, viaTable); } + /// + /// A detour from a custom-weight result table is solved with the same positional weights; + /// using the constructor weights would choose the other available route. + /// + [TestMethod] + public void GetPathCustomWeightTableUsesCustomWeightsForDetour() + { + var edges = new[] + { + new Edge(0, 1, 1, 0), + new Edge(1, 4, 1, 1), + new Edge(0, 2, 1, 2), + new Edge(2, 4, 1, 3), + new Edge(0, 3, 10, 4), + new Edge(3, 4, 10, 5), + }; + var customWeights = new float[] { 1, 1, 100, 100, 2, 2 }; + var network = new Network(edges, new[] { 4 }); + var customTable = network.Solve(customWeights); + + CollectionAssert.AreEqual( + new List { 4, 5 }, + network.GetPath(new[] { 1 }, 0, customTable, customWeights)); + } + /// /// GetPath rejects null inputs, out-of-range start nodes, and wrong table dimensions /// with clear argument exceptions. @@ -423,6 +448,8 @@ public void GetPathValidationThrows() Assert.Throws(() => network.GetPath(new int[0], 0, null!)); Assert.Throws(() => network.GetPath(new int[0], 0, new float[2, 3])); Assert.Throws(() => network.GetPath(new int[0], 9, table)); + Assert.Throws(() => network.GetPath(new int[0], 0, table, null!)); + Assert.Throws(() => network.GetPath(new int[0], 0, table, new float[1])); } /// From cf5175d67499d9571aae2e0b6b0de4822a66db75 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 29 Aug 2026 12:45:20 -0600 Subject: [PATCH 182/222] Reject negative network edge indices --- .../Optimization/Dynamic/CompactAdjacency.cs | 8 ++++++-- Numerics/Mathematics/Optimization/Dynamic/Dijkstra.cs | 10 +++++----- Numerics/Mathematics/Optimization/Dynamic/Network.cs | 2 +- .../Mathematics/Optimization/Dynamic/Test_Network.cs | 1 + .../Optimization/Dynamic/Test_ShortestPath.cs | 8 ++++++++ 5 files changed, 21 insertions(+), 8 deletions(-) diff --git a/Numerics/Mathematics/Optimization/Dynamic/CompactAdjacency.cs b/Numerics/Mathematics/Optimization/Dynamic/CompactAdjacency.cs index 110c1008..bb80a557 100644 --- a/Numerics/Mathematics/Optimization/Dynamic/CompactAdjacency.cs +++ b/Numerics/Mathematics/Optimization/Dynamic/CompactAdjacency.cs @@ -83,7 +83,7 @@ private CompactAdjacency(int nodeCount, int[] rowStart, int[] fromNode, int[] to /// True to group slots by ; false to group by . /// The caller's parameter name for exception reporting. /// The compact view. - /// Thrown when an edge references a node index outside [0, nodeCount). + /// Thrown when an edge references a node index outside [0, nodeCount), or has a negative edge index. internal static CompactAdjacency FromEdges(IList edges, int nodeCount, bool groupByEndNode, string parameterName) { int edgeCount = edges.Count; @@ -98,6 +98,8 @@ internal static CompactAdjacency FromEdges(IList edges, int nodeCount, boo $"{(edge.FromIndex < 0 || edge.FromIndex >= nodeCount ? edge.FromIndex : edge.ToIndex)}, " + $"outside the node range [0, {nodeCount}).", parameterName); } + if (edge.Index < 0) + throw new ArgumentException($"The edge at position {i} has negative edge index {edge.Index}.", parameterName); rowStart[(groupByEndNode ? edge.ToIndex : edge.FromIndex) + 1]++; } for (int n = 0; n < nodeCount; n++) rowStart[n + 1] += rowStart[n]; @@ -137,7 +139,7 @@ internal static CompactAdjacency FromEdges(IList edges, int nodeCount, boo /// The number of nodes; the array length must equal it. /// The caller's parameter name for exception reporting. /// The compact view, without source positions. - /// Thrown when a listed edge references a node index outside [0, nodeCount). + /// Thrown when a listed edge references a node index outside [0, nodeCount), or has a negative edge index. internal static CompactAdjacency FromIncomingLists(List[] edgesToNodes, int nodeCount, string parameterName) { var rowStart = new int[nodeCount + 1]; @@ -168,6 +170,8 @@ internal static CompactAdjacency FromIncomingLists(List[] edgesToNodes, in $"{(edge.FromIndex < 0 || edge.FromIndex >= nodeCount ? edge.FromIndex : edge.ToIndex)}, " + $"outside the node range [0, {nodeCount}).", parameterName); } + if (edge.Index < 0) + throw new ArgumentException($"The incoming-edge list for node {n} contains negative edge index {edge.Index}.", parameterName); fromNode[slot] = edge.FromIndex; toNode[slot] = edge.ToIndex; weight[slot] = edge.Weight; diff --git a/Numerics/Mathematics/Optimization/Dynamic/Dijkstra.cs b/Numerics/Mathematics/Optimization/Dynamic/Dijkstra.cs index 67d54357..446cfd07 100644 --- a/Numerics/Mathematics/Optimization/Dynamic/Dijkstra.cs +++ b/Numerics/Mathematics/Optimization/Dynamic/Dijkstra.cs @@ -12,7 +12,7 @@ namespace Numerics.Mathematics.Optimization /// Node index at start of edge. /// Node index at end of edge. /// Weight (or Cost) of the edge. - /// Index of the edge. + /// Nonnegative index of the edge. public struct Edge(int fromNodeIndex, int toNodeIndex, float edgeWeight, int edgeIndex) { /// @@ -28,7 +28,7 @@ public struct Edge(int fromNodeIndex, int toNodeIndex, float edgeWeight, int edg /// public float Weight = edgeWeight; /// - /// Index of the edge, often used as an index to the edge source (e.g., road segment). + /// Nonnegative index of the edge, often used as an index to the edge source (e.g., road segment). /// public int Index = edgeIndex; } @@ -166,7 +166,7 @@ public static bool TryGetPath(float[,] resultTable, int startNodeIndex, out List /// destination. Duplicate destination indices are tolerated. /// /// Thrown when the edges or destination indices are null. - /// Thrown when the destination array is empty, the node count cannot be derived, or an edge references a node outside the network. + /// Thrown when the destination array is empty, the node count cannot be derived, or an edge references a node outside the network or has a negative index. /// Thrown when the node count is not positive, or a destination index is outside the network. public static float[,] SolveNearest(IList edges, int[] destinationIndices, int nodeCount = -1) { @@ -245,7 +245,7 @@ internal static void SolveNearestCore(in CompactAdjacency adjacency, float[]? we /// the array wins. An empty destination array returns an all-unreachable table. /// /// Thrown when the edges or destination indices are null. - /// Thrown when the node count cannot be derived, or an edge references a node outside the network. + /// Thrown when the node count cannot be derived, or an edge references a node outside the network or has a negative index. /// Thrown when the node count is not positive, or a destination index is outside the network. public static float[,] Solve(IList edges, int[] destinationIndices, int nodeCount = -1, List[]? edgesFromNodes = null) { @@ -322,7 +322,7 @@ internal static void SolveMergedCore(in CompactAdjacency adjacency, float[]? wei /// Optional list of incoming edges for each node in the network. If not provided or mismatched with the node count it will be calculated internally. /// Lookup table of shortest paths from any given node. /// Thrown when the edges are null. - /// Thrown when the node count cannot be derived, or an edge references a node outside the network. + /// Thrown when the node count cannot be derived, or an edge references a node outside the network or has a negative index. /// Thrown when the node count is not positive, or the destination index is outside the network. public static float[,] Solve(IList edges, int destinationIndex, int nodeCount = -1, List[]? edgesToNodes = null) { diff --git a/Numerics/Mathematics/Optimization/Dynamic/Network.cs b/Numerics/Mathematics/Optimization/Dynamic/Network.cs index 5dc7f480..f33fcbcd 100644 --- a/Numerics/Mathematics/Optimization/Dynamic/Network.cs +++ b/Numerics/Mathematics/Optimization/Dynamic/Network.cs @@ -102,7 +102,7 @@ public class Network /// The edges that define the network. The array is copied. /// The destination node indices. The array is copied. /// Thrown when either array is null. - /// Thrown when either array is empty, or an edge references a negative node index. + /// Thrown when either array is empty, or an edge references a negative node index or has a negative edge index. /// Thrown when a destination index is outside the network. public Network(Edge[] edges, int[] destinationIndices) { diff --git a/Test_Numerics/Mathematics/Optimization/Dynamic/Test_Network.cs b/Test_Numerics/Mathematics/Optimization/Dynamic/Test_Network.cs index 4c278980..fc00341d 100644 --- a/Test_Numerics/Mathematics/Optimization/Dynamic/Test_Network.cs +++ b/Test_Numerics/Mathematics/Optimization/Dynamic/Test_Network.cs @@ -98,6 +98,7 @@ public void CtorValidationThrows() Assert.Throws(() => new Network(edges, new int[0])); Assert.Throws(() => new Network(edges, [5])); Assert.Throws(() => new Network(new[] { new Edge(-1, 1, 1, 0) }, [0])); + Assert.Throws(() => new Network(new[] { new Edge(0, 1, 1, -1) }, [0])); } /// diff --git a/Test_Numerics/Mathematics/Optimization/Dynamic/Test_ShortestPath.cs b/Test_Numerics/Mathematics/Optimization/Dynamic/Test_ShortestPath.cs index 56f5a2ec..c5c0ad2c 100644 --- a/Test_Numerics/Mathematics/Optimization/Dynamic/Test_ShortestPath.cs +++ b/Test_Numerics/Mathematics/Optimization/Dynamic/Test_ShortestPath.cs @@ -526,6 +526,14 @@ public void SolveValidationThrows() Assert.Throws(() => Dijkstra.Solve(edges, 2, 2)); Assert.Throws(() => Dijkstra.Solve(edges, [0, 5], 2)); Assert.Throws(() => Dijkstra.Solve(new List { new Edge(0, 9, 1, 0) }, 0, 3)); + Assert.Throws(() => Dijkstra.Solve(new List { new Edge(0, 1, 1, -1) }, 1, 2)); + + var incomingEdges = new List[] + { + new(), + new() { new Edge(0, 1, 1, -1) } + }; + Assert.Throws(() => Dijkstra.Solve(edges, 1, 2, incomingEdges)); Assert.Throws(() => Dijkstra.PathExists(null!, 0)); Assert.Throws(() => Dijkstra.PathExists(new float[2, 3], 2)); From 657dc03f27d60a79024f9399371bb083e2406433 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 29 Aug 2026 12:49:09 -0600 Subject: [PATCH 183/222] Preserve survival precision in hurdle CDF --- Numerics/Distributions/Univariate/Mixture.cs | 6 ++- .../Distributions/Univariate/Test_Mixture.cs | 40 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/Numerics/Distributions/Univariate/Mixture.cs b/Numerics/Distributions/Univariate/Mixture.cs index f3c240b8..c45736eb 100644 --- a/Numerics/Distributions/Univariate/Mixture.cs +++ b/Numerics/Distributions/Univariate/Mixture.cs @@ -262,10 +262,14 @@ private double PositiveConditionalLogPDF(int componentIndex, double x) /// The zero-based component index. /// The value at which to evaluate the distribution function. /// The positive-conditional cumulative probability. + /// + /// Uses the survival ratio 1 - S(x) / S(0) so a component's direct survival + /// evaluation can retain upper-tail probability after its CDF has rounded to one. + /// private double PositiveConditionalCDF(int componentIndex, double x) { if (x <= 0.0 || !TryGetPositiveMass(componentIndex, out double positiveMass)) return 0.0; - double probability = (Distributions[componentIndex].CDF(x) - Distributions[componentIndex].CDF(0.0)) / positiveMass; + double probability = 1.0 - Distributions[componentIndex].CCDF(x) / positiveMass; return Clamp(probability, 0.0, 1.0); } diff --git a/Test_Numerics/Distributions/Univariate/Test_Mixture.cs b/Test_Numerics/Distributions/Univariate/Test_Mixture.cs index a365e3a5..f41b1918 100644 --- a/Test_Numerics/Distributions/Univariate/Test_Mixture.cs +++ b/Test_Numerics/Distributions/Univariate/Test_Mixture.cs @@ -459,6 +459,27 @@ public void Test_Mixture_ZeroInflatedNormal_UsesPositiveHurdleIdentities() Assert.AreEqual(expectedQuantile, mixture.InverseCDF(probability), 1E-6); } + /// + /// Verifies the positive-hurdle CDF uses a component's retained survival probability + /// when its CDF has rounded to one. + /// + [TestMethod] + public void Test_Mixture_ZeroInflatedCDF_UsesSurvivalRatio() + { + var distribution = new TailAwareDistribution(); + var mixture = new Mixture(new[] { 1.0 }, new UnivariateDistributionBase[] { distribution }) + { + IsZeroInflated = true, + ZeroWeight = 0.0 + }; + + double expected = 1.0 - distribution.CCDF(1.0) / distribution.CCDF(0.0); + + Assert.AreEqual(0.5, expected, 0.0); + Assert.AreEqual(expected, mixture.CDF(1.0), 0.0); + Assert.IsTrue(mixture.CDF(1.0) >= 0.0 && mixture.CDF(1.0) <= 1.0); + } + /// /// Verifies the atom at zero and absence of negative support under the hurdle model. /// @@ -611,5 +632,24 @@ public void Test_Mixture_EmpiricalUnderTheHood_NoRegression() Assert.IsLessThan(1d, single.CDF(400d)); } + /// + /// Test distribution whose direct survival function retains a tail that its CDF cannot + /// represent after subtraction from one. + /// + private sealed class TailAwareDistribution : Cauchy + { + /// + public override double CDF(double x) + { + return x <= 0.0 ? 0.9999999999999999 : 1.0; + } + + /// + public override double CCDF(double x) + { + return x <= 0.0 ? 1E-16 : 5E-17; + } + } + } } From ed32cf570365208d726a76789c61b573050340b8 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 29 Aug 2026 12:52:16 -0600 Subject: [PATCH 184/222] Document singular boundary log densities --- .../Univariate/GammaDistribution.cs | 4 +++- .../Univariate/LogPearsonTypeIII.cs | 3 +++ .../Distributions/Univariate/PearsonTypeIII.cs | 5 ++++- Numerics/Distributions/Univariate/Weibull.cs | 4 +++- .../Univariate/Test_GammaDistribution.cs | 9 +++++++++ .../Univariate/Test_LogPearsonTypeIII.cs | 16 ++++++++++++++++ .../Univariate/Test_PearsonTypeIII.cs | 16 ++++++++++++++++ .../Distributions/Univariate/Test_Weibull.cs | 9 +++++++++ 8 files changed, 63 insertions(+), 3 deletions(-) diff --git a/Numerics/Distributions/Univariate/GammaDistribution.cs b/Numerics/Distributions/Univariate/GammaDistribution.cs index f50b3f0f..7f835ea3 100644 --- a/Numerics/Distributions/Univariate/GammaDistribution.cs +++ b/Numerics/Distributions/Univariate/GammaDistribution.cs @@ -555,6 +555,8 @@ public override double PDF(double X) /// /// Evaluated in log space, so far-tail densities that underflow /// keep a finite log density. + /// When X = 0 and the shape κ < 1, the Gamma density has a genuine + /// integrable singularity and this method intentionally returns positive infinity. /// public override double LogPDF(double X) { @@ -967,4 +969,4 @@ static double Pg(double s, double z) } } -} \ No newline at end of file +} diff --git a/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs b/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs index 28817564..3cea1360 100644 --- a/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs +++ b/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs @@ -770,6 +770,9 @@ public override double PDF(double x) /// /// Evaluated in log space, so far-tail densities that underflow /// keep a finite log density. + /// When the shape α < 1, the density has a genuine integrable singularity at + /// the transformed support boundary x = Base^ξ and this method intentionally + /// returns positive infinity for either skew direction. /// public override double LogPDF(double x) { diff --git a/Numerics/Distributions/Univariate/PearsonTypeIII.cs b/Numerics/Distributions/Univariate/PearsonTypeIII.cs index c9f5713c..744164fd 100644 --- a/Numerics/Distributions/Univariate/PearsonTypeIII.cs +++ b/Numerics/Distributions/Univariate/PearsonTypeIII.cs @@ -590,6 +590,9 @@ public override double PDF(double x) /// /// Evaluated in log space, so far-tail densities that underflow /// keep a finite log density. + /// When the shape α < 1, the density has a genuine integrable singularity at + /// the support boundary x = ξ and this method intentionally returns positive + /// infinity for either skew direction. /// public override double LogPDF(double x) { @@ -1172,4 +1175,4 @@ double CdfGamma(int r, double x) } } -} \ No newline at end of file +} diff --git a/Numerics/Distributions/Univariate/Weibull.cs b/Numerics/Distributions/Univariate/Weibull.cs index c8d5c220..c1661afa 100644 --- a/Numerics/Distributions/Univariate/Weibull.cs +++ b/Numerics/Distributions/Univariate/Weibull.cs @@ -411,6 +411,8 @@ public override double PDF(double x) /// /// Evaluated in log space, so far-tail densities that underflow /// keep a finite log density. + /// When x = 0 and the shape κ < 1, the Weibull density has a genuine + /// integrable singularity and this method intentionally returns positive infinity. /// public override double LogPDF(double x) { @@ -536,4 +538,4 @@ public double[] QuantileGradient(double probability) } } -} \ No newline at end of file +} diff --git a/Test_Numerics/Distributions/Univariate/Test_GammaDistribution.cs b/Test_Numerics/Distributions/Univariate/Test_GammaDistribution.cs index 998cf0df..bd676fb3 100644 --- a/Test_Numerics/Distributions/Univariate/Test_GammaDistribution.cs +++ b/Test_Numerics/Distributions/Univariate/Test_GammaDistribution.cs @@ -380,6 +380,15 @@ public void Test_PDF() Assert.AreEqual(0.0000453999, G2.PDF(10), 1e-10); } + /// + /// A Gamma shape below one has a genuine density singularity at zero. + /// + [TestMethod] + public void Test_LogPDF_ShapeBelowOneIsPositiveInfinityAtZero() + { + Assert.AreEqual(double.PositiveInfinity, new GammaDistribution(2.0, 0.5).LogPDF(0.0)); + } + /// /// Checking CDF function with different parameters at different locations. /// diff --git a/Test_Numerics/Distributions/Univariate/Test_LogPearsonTypeIII.cs b/Test_Numerics/Distributions/Univariate/Test_LogPearsonTypeIII.cs index 34f72fdf..31800da8 100644 --- a/Test_Numerics/Distributions/Univariate/Test_LogPearsonTypeIII.cs +++ b/Test_Numerics/Distributions/Univariate/Test_LogPearsonTypeIII.cs @@ -405,5 +405,21 @@ public void Test_LP3_ParameterConstraints_AllowNegativeLogMean() Assert.AreEqual(double.NegativeInfinity, new LogPearsonTypeIII().MinimumOfParameters[0]); } + /// + /// Log-Pearson III shapes below one have a genuine density singularity at the + /// transformed support boundary for both skew directions. + /// + [TestMethod] + public void Test_LogPDF_ShapeBelowOneIsPositiveInfinityAtLocation() + { + var positiveSkew = new LogPearsonTypeIII(1.0, 1.5, 3.0); + var negativeSkew = new LogPearsonTypeIII(-1.0, 1.5, -3.0); + + Assert.AreEqual(0.0, positiveSkew.Xi, 0.0); + Assert.AreEqual(0.0, negativeSkew.Xi, 0.0); + Assert.AreEqual(double.PositiveInfinity, positiveSkew.LogPDF(1.0)); + Assert.AreEqual(double.PositiveInfinity, negativeSkew.LogPDF(1.0)); + } + } } diff --git a/Test_Numerics/Distributions/Univariate/Test_PearsonTypeIII.cs b/Test_Numerics/Distributions/Univariate/Test_PearsonTypeIII.cs index c11f3d58..28af8624 100644 --- a/Test_Numerics/Distributions/Univariate/Test_PearsonTypeIII.cs +++ b/Test_Numerics/Distributions/Univariate/Test_PearsonTypeIII.cs @@ -398,6 +398,22 @@ public void Test_LinearMoments_SignedSmallAndZeroSkew() Assert.AreEqual(expectedDerivative, zeroSkew.QuantileGradientForMoments(0.9d)[2], 1E-14); } + /// + /// Pearson III shapes below one have a genuine density singularity at the support + /// boundary for both skew directions. + /// + [TestMethod] + public void Test_LogPDF_ShapeBelowOneIsPositiveInfinityAtLocation() + { + var positiveSkew = new PearsonTypeIII(1.0, 1.5, 3.0); + var negativeSkew = new PearsonTypeIII(-1.0, 1.5, -3.0); + + Assert.AreEqual(0.0, positiveSkew.Xi, 0.0); + Assert.AreEqual(0.0, negativeSkew.Xi, 0.0); + Assert.AreEqual(double.PositiveInfinity, positiveSkew.LogPDF(positiveSkew.Xi)); + Assert.AreEqual(double.PositiveInfinity, negativeSkew.LogPDF(negativeSkew.Xi)); + } + } diff --git a/Test_Numerics/Distributions/Univariate/Test_Weibull.cs b/Test_Numerics/Distributions/Univariate/Test_Weibull.cs index b37d6354..9289818c 100644 --- a/Test_Numerics/Distributions/Univariate/Test_Weibull.cs +++ b/Test_Numerics/Distributions/Univariate/Test_Weibull.cs @@ -273,6 +273,15 @@ public void Test_PDF() Assert.AreEqual(0.00004539, W.PDF(10), 1e-08); } + /// + /// A Weibull shape below one has a genuine density singularity at zero. + /// + [TestMethod] + public void Test_LogPDF_ShapeBelowOneIsPositiveInfinityAtZero() + { + Assert.AreEqual(double.PositiveInfinity, new Weibull(2.0, 0.5).LogPDF(0.0)); + } + /// /// Testing CDF method. /// From f4c4c93a5f9914b24df9c5378e8eb10be4f3fd9a Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 29 Aug 2026 12:54:54 -0600 Subject: [PATCH 185/222] Preserve logarithm bases in resampling --- .../Distributions/Univariate/LogNormal.cs | 6 ++- .../Univariate/LogPearsonTypeIII.cs | 3 +- .../Univariate/Test_LogNormal.cs | 46 +++++++++++++++++++ .../Univariate/Test_LogPearsonTypeIII.cs | 14 ++++++ 4 files changed, 66 insertions(+), 3 deletions(-) diff --git a/Numerics/Distributions/Univariate/LogNormal.cs b/Numerics/Distributions/Univariate/LogNormal.cs index f6672346..b4da1815 100644 --- a/Numerics/Distributions/Univariate/LogNormal.cs +++ b/Numerics/Distributions/Univariate/LogNormal.cs @@ -289,9 +289,10 @@ public void Estimate(IList sample, ParameterEstimationMethod estimationM } /// + /// The bootstrap distribution retains the configured . public IUnivariateDistribution Bootstrap(ParameterEstimationMethod estimationMethod, int sampleSize, int seed = -1) { - var newDistribution = new LogNormal(Mu, Sigma); + var newDistribution = new LogNormal(Mu, Sigma) { Base = Base }; var sample = newDistribution.GenerateRandomValues(sampleSize, seed); newDistribution.Estimate(sample, estimationMethod); if (newDistribution.ParametersValid == false) @@ -555,6 +556,7 @@ public override double InverseCDF(double probability) /// List of confidence percentiles for confidence interval output. /// /// This is the same sampling approach as used in HEC-FDA. + /// Each simulated distribution retains the configured . /// public double[,] MonteCarloConfidenceIntervals(int sampleSize, int realizations, IList quantiles, IList percentiles) { @@ -588,7 +590,7 @@ public override double InverseCDF(double probability) var Chi = new ChiSquared(sampleSize - 1); double NewSigma = Math.Sqrt((sampleSize - 1) * Math.Pow(OriginalStdDev, 2d) / Chi.InverseCDF(rndStdDev[idx])); // Create a new distribution with the new parameters - MonteCarloDistributions[idx] = new LogNormal(NewMu, NewSigma); + MonteCarloDistributions[idx] = new LogNormal(NewMu, NewSigma) { Base = Base }; }); // Create confidence intervals diff --git a/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs b/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs index 3cea1360..67807936 100644 --- a/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs +++ b/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs @@ -432,9 +432,10 @@ public void Estimate(IList sample, ParameterEstimationMethod estimationM } /// + /// The bootstrap distribution retains the configured . public IUnivariateDistribution Bootstrap(ParameterEstimationMethod estimationMethod, int sampleSize, int seed = -1) { - var newDistribution = new LogPearsonTypeIII(Mu, Sigma, Gamma); + var newDistribution = new LogPearsonTypeIII(Mu, Sigma, Gamma) { Base = Base }; var sample = newDistribution.GenerateRandomValues(sampleSize, seed); newDistribution.Estimate(sample, estimationMethod); if (newDistribution.ParametersValid == false) diff --git a/Test_Numerics/Distributions/Univariate/Test_LogNormal.cs b/Test_Numerics/Distributions/Univariate/Test_LogNormal.cs index 8c933a70..f336e813 100644 --- a/Test_Numerics/Distributions/Univariate/Test_LogNormal.cs +++ b/Test_Numerics/Distributions/Univariate/Test_LogNormal.cs @@ -180,6 +180,52 @@ public void Test_Clone_PreservesBase() Assert.AreEqual(source.CDF(75d), clone.CDF(75d), 0d); } + /// + /// Verifies bootstrap distributions retain the configured logarithm base. + /// + [TestMethod] + public void Test_Bootstrap_PreservesBase() + { + var source = new LogNormal(4.2d, 0.4d) { Base = Math.E }; + + var bootstrap = (LogNormal)source.Bootstrap( + ParameterEstimationMethod.MethodOfMoments, 40, 12345); + + Assert.AreEqual(source.Base, bootstrap.Base, 0d); + } + + /// + /// Equivalent base-10 and natural-log parameterizations produce the same seeded Monte + /// Carlo confidence intervals. + /// + [TestMethod] + public void Test_MonteCarloConfidenceIntervals_PreserveBase() + { + const double mu10 = 2.0; + const double sigma10 = 0.3; + var base10 = new LogNormal(mu10, sigma10); + var natural = new LogNormal(mu10 * Math.Log(10.0), sigma10 * Math.Log(10.0)) + { + Base = Math.E + }; + double[] quantiles = [0.5, 0.9]; + double[] percentiles = [0.1, 0.5, 0.9]; + + double[,] expected = base10.MonteCarloConfidenceIntervals( + 25, 100, quantiles, percentiles); + double[,] actual = natural.MonteCarloConfidenceIntervals( + 25, 100, quantiles, percentiles); + + for (int i = 0; i < expected.GetLength(0); i++) + { + for (int j = 0; j < expected.GetLength(1); j++) + { + Assert.AreEqual(expected[i, j], actual[i, j], + 1E-10 * Math.Max(1.0, Math.Abs(expected[i, j]))); + } + } + } + /// /// Testing Log-Normal with bad parameters. /// diff --git a/Test_Numerics/Distributions/Univariate/Test_LogPearsonTypeIII.cs b/Test_Numerics/Distributions/Univariate/Test_LogPearsonTypeIII.cs index 31800da8..204dba09 100644 --- a/Test_Numerics/Distributions/Univariate/Test_LogPearsonTypeIII.cs +++ b/Test_Numerics/Distributions/Univariate/Test_LogPearsonTypeIII.cs @@ -44,6 +44,20 @@ public void Test_Clone_PreservesBase() Assert.AreEqual(source.CDF(75d), clone.CDF(75d), 0d); } + /// + /// Verifies bootstrap distributions retain the configured logarithm base. + /// + [TestMethod] + public void Test_Bootstrap_PreservesBase() + { + var source = new LogPearsonTypeIII(4.2d, 0.4d, 0.25d) { Base = Math.E }; + + var bootstrap = (LogPearsonTypeIII)source.Bootstrap( + ParameterEstimationMethod.MethodOfMoments, 40, 12345); + + Assert.AreEqual(source.Base, bootstrap.Base, 0d); + } + // Reference: "The Gamma Family and Derived Distributions Applied in Hydrology", B. Bobee & F. Ashkar, Water Resources Publications, 1991. // Table 1.2 Maximum annual peak discharge values in cms, observed at the Harricana River at Amos (Quebec, Canada) private double[] sample = new double[] { 122d, 244d, 214d, 173d, 229d, 156d, 212d, 263d, 146d, 183d, 161d, 205d, 135d, 331d, 225d, 174d, 98.8d, 149d, 238d, 262d, 132d, 235d, 216d, 240d, 230d, 192d, 195d, 172d, 173d, 172d, 153d, 142d, 317d, 161d, 201d, 204d, 194d, 164d, 183d, 161d, 167d, 179d, 185d, 117d, 192d, 337d, 125d, 166d, 99.1d, 202d, 230d, 158d, 262d, 154d, 164d, 182d, 164d, 183d, 171d, 250d, 184d, 205d, 237d, 177d, 239d, 187d, 180d, 173d, 174d }; From fb88800aa0126565bad1197c9537dd2983018a15 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 29 Aug 2026 12:56:48 -0600 Subject: [PATCH 186/222] Floor automatic subnormal KDE bandwidths --- .../Distributions/Univariate/KernelDensity.cs | 21 ++++++++++---- .../Univariate/Test_KernelDensity.cs | 28 +++++++++++++++++++ 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/Numerics/Distributions/Univariate/KernelDensity.cs b/Numerics/Distributions/Univariate/KernelDensity.cs index d7fcc78a..37aa2d5e 100644 --- a/Numerics/Distributions/Univariate/KernelDensity.cs +++ b/Numerics/Distributions/Univariate/KernelDensity.cs @@ -568,10 +568,16 @@ public double BandwidthRule(IList sample, IList? w = null) /// /// The absolute bandwidth assigned when a zero-dispersion sample supplies no usable - /// magnitude (an all-zero or subnormal constant). + /// magnitude, or its relative automatic bandwidth would be subnormal. /// public const double DegenerateAbsoluteBandwidth = 1E-9; + /// + /// The smallest positive normal IEEE 754 double, used only to floor automatically + /// derived bandwidths before reciprocal evaluation can overflow. + /// + private const double SmallestNormalBandwidth = 2.2250738585072014E-308; + /// /// Produces a finite, strictly positive automatic bandwidth when the sample dispersion is zero or non-finite. /// @@ -584,10 +590,11 @@ public double BandwidthRule(IList sample, IList? w = null) /// supports no spread estimate, so the density must not invent one from the constant's /// magnitude: the bandwidth is the magnitude times , /// a near-point mass at the observed value, falling back to - /// when the constant is zero or so small the product - /// underflows. A non-finite dispersion on a genuinely spread sample arises only from variance - /// overflow; the largest absolute observation then supplies the scale for the standard - /// bandwidth rule, with guards against overflow and underflow. + /// when the constant is zero or the derived + /// relative bandwidth is subnormal or non-finite. A non-finite dispersion on a genuinely + /// spread sample arises only from variance overflow; the largest absolute observation then + /// supplies the scale for the standard bandwidth rule, with guards against overflow and + /// underflow. /// private static double EnsurePositiveBandwidth(double dispersion, double factor, IList sample) { @@ -607,7 +614,9 @@ private static double EnsurePositiveBandwidth(double dispersion, double factor, { double magnitude = Math.Abs(sample[0]); double degenerate = magnitude * DegenerateRelativeBandwidth; - return degenerate > 0d && Tools.IsFinite(degenerate) ? degenerate : DegenerateAbsoluteBandwidth; + return degenerate >= SmallestNormalBandwidth && Tools.IsFinite(degenerate) + ? degenerate + : DegenerateAbsoluteBandwidth; } } diff --git a/Test_Numerics/Distributions/Univariate/Test_KernelDensity.cs b/Test_Numerics/Distributions/Univariate/Test_KernelDensity.cs index f6b8e449..b62849ff 100644 --- a/Test_Numerics/Distributions/Univariate/Test_KernelDensity.cs +++ b/Test_Numerics/Distributions/Univariate/Test_KernelDensity.cs @@ -123,6 +123,22 @@ public void ConstantZeroSample_UsesAbsoluteDegenerateBandwidth() Assert.IsGreaterThan(0d, distribution.PDF(0d)); } + /// + /// A constant whose relative automatic bandwidth is subnormal uses the absolute + /// degenerate fallback, avoiding an infinite density caused by reciprocal overflow. + /// + [TestMethod] + public void ConstantWithSubnormalDerivedBandwidth_UsesAbsoluteDegenerateBandwidth() + { + double[] constantSample = { 1E-300, 1E-300, 1E-300, 1E-300 }; + + var distribution = new KernelDensity(constantSample); + + Assert.AreEqual(KernelDensity.DegenerateAbsoluteBandwidth, distribution.Bandwidth, 0d); + Assert.IsTrue(Tools.IsFinite(distribution.PDF(1E-300))); + Assert.IsGreaterThan(0d, distribution.PDF(1E-300)); + } + /// /// Verifies that a weighted constant sample yields the near-point-mass bandwidth from the /// constant's magnitude, independent of the weights. @@ -151,6 +167,18 @@ public void ExplicitZeroBandwidth_RemainsInvalid() new KernelDensity(new[] { -1d, 0d, 1d }, KernelDensity.KernelType.Gaussian, 0d)); } + /// + /// The automatic-bandwidth floor does not alter a positive explicit bandwidth. + /// + [TestMethod] + public void ExplicitSubnormalBandwidth_RemainsAsSupplied() + { + var distribution = new KernelDensity( + new[] { -1d, 0d, 1d }, KernelDensity.KernelType.Gaussian, double.Epsilon); + + Assert.AreEqual(double.Epsilon, distribution.Bandwidth, 0d); + } + /// /// Creates a deterministic sample large enough for the density reduction to be partitioned /// across several threads. From 2dbfa7f269e9a0a6b8b4f3a680703c8832faabbe Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 29 Aug 2026 12:59:16 -0600 Subject: [PATCH 187/222] Round-trip empirical probability order --- .../Univariate/EmpiricalDistribution.cs | 16 +++++------- .../Test_DistributionXElementRoundTrips.cs | 26 ++++++++++++++++++- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/Numerics/Distributions/Univariate/EmpiricalDistribution.cs b/Numerics/Distributions/Univariate/EmpiricalDistribution.cs index 003efd2b..0302491f 100644 --- a/Numerics/Distributions/Univariate/EmpiricalDistribution.cs +++ b/Numerics/Distributions/Univariate/EmpiricalDistribution.cs @@ -888,12 +888,9 @@ public override XElement ToXElement() result.SetAttributeValue(nameof(XValues), string.Join("|", xValues)); result.SetAttributeValue(nameof(ProbabilityValues), string.Join("|", pValues)); - // The stored probability ladder may run ascending (non-exceedance) or descending - // (exceedance); record the order so deserialization restores the same convention. - var order = SortOrder.Ascending; - if (ProbabilityValues.Count > 1 && ProbabilityValues[0] > ProbabilityValues[ProbabilityValues.Count - 1]) - order = SortOrder.Descending; - result.SetAttributeValue("ProbabilityOrder", order.ToString()); + // Preserve the configured order, including None for ladders that intentionally use + // linear search rather than a monotonic smart-search contract. + result.SetAttributeValue(nameof(ProbabilityOrder), ProbabilityOrder.ToString()); return result; } @@ -929,11 +926,10 @@ public static EmpiricalDistribution FromXElement(XElement xElement) throw new ArgumentException("The serialized empirical distribution contains an invalid table value.", nameof(xElement)); } - var orderAttribute = xElement.Attribute("ProbabilityOrder"); + var orderAttribute = xElement.Attribute(nameof(ProbabilityOrder)); if (orderAttribute == null || !Enum.TryParse(orderAttribute.Value, out SortOrder order) - || !Enum.IsDefined(typeof(SortOrder), order) - || (order != SortOrder.Ascending && order != SortOrder.Descending)) + || !Enum.IsDefined(typeof(SortOrder), order)) throw new ArgumentException("The serialized empirical distribution has an invalid probability order.", nameof(xElement)); var distribution = new EmpiricalDistribution(xValues, pValues, SortOrder.Ascending, order); @@ -1186,4 +1182,4 @@ public static EmpiricalDistribution Convolve(IList distri } } -} \ No newline at end of file +} diff --git a/Test_Numerics/Distributions/Univariate/Test_DistributionXElementRoundTrips.cs b/Test_Numerics/Distributions/Univariate/Test_DistributionXElementRoundTrips.cs index 1b2580f8..5707282c 100644 --- a/Test_Numerics/Distributions/Univariate/Test_DistributionXElementRoundTrips.cs +++ b/Test_Numerics/Distributions/Univariate/Test_DistributionXElementRoundTrips.cs @@ -72,6 +72,30 @@ public void Test_EmpiricalDistribution_DescendingProbabilities_RoundTrip() Assert.AreEqual(original.CDF(10d), restored.CDF(10d), 1E-12); } + /// + /// Test that a probability ladder explicitly configured without a sort order serializes + /// and restores that order rather than inferring one from its endpoints. + /// + [TestMethod] + public void Test_EmpiricalDistribution_NoneProbabilityOrder_RoundTrip() + { + var original = new EmpiricalDistribution( + new[] { 1d, 2d, 3d }, + new[] { 0.1d, 0.9d, 0.4d }, + SortOrder.Ascending, + SortOrder.None); + + XElement element = original.ToXElement(); + Assert.AreEqual(nameof(SortOrder.None), + element.Attribute(nameof(EmpiricalDistribution.ProbabilityOrder))?.Value); + + var restored = EmpiricalDistribution.FromXElement(element); + Assert.AreEqual(SortOrder.None, restored.ProbabilityOrder); + Assert.HasCount(original.ProbabilityValues.Count, restored.ProbabilityValues); + for (int i = 0; i < original.ProbabilityValues.Count; i++) + Assert.AreEqual(original.ProbabilityValues[i], restored.ProbabilityValues[i], 0d); + } + /// /// Test the kernel density round-trip: the sample, kernel type, bandwidth, transforms, /// and the optional per-sample weights restore exactly, through both the direct @@ -114,7 +138,7 @@ public void Test_MalformedXElements_AreRejected() { var empirical = new EmpiricalDistribution(new[] { 1d, 2d }, new[] { 0d, 1d }); XElement invalidOrder = empirical.ToXElement(); - invalidOrder.SetAttributeValue("ProbabilityOrder", "999"); + invalidOrder.SetAttributeValue(nameof(EmpiricalDistribution.ProbabilityOrder), "999"); Assert.Throws(() => UnivariateDistributionFactory.CreateDistribution(invalidOrder)); XElement invalidTransform = empirical.ToXElement(); From 138c0651664457e9f46976fc4fac109fc10df8c9 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 29 Aug 2026 13:02:27 -0600 Subject: [PATCH 188/222] Harden uncertainty ensemble filtering --- .../UncertaintyAnalysisResults.cs | 47 +++++++++++++++---- .../Univariate/Test_BootstrapAnalysis.cs | 42 +++++++++++++++++ 2 files changed, 80 insertions(+), 9 deletions(-) diff --git a/Numerics/Distributions/Univariate/Uncertainty Analysis/UncertaintyAnalysisResults.cs b/Numerics/Distributions/Univariate/Uncertainty Analysis/UncertaintyAnalysisResults.cs index 8ab35afd..a04f7c45 100644 --- a/Numerics/Distributions/Univariate/Uncertainty Analysis/UncertaintyAnalysisResults.cs +++ b/Numerics/Distributions/Univariate/Uncertainty Analysis/UncertaintyAnalysisResults.cs @@ -49,6 +49,9 @@ public UncertaintyAnalysisResults() { } /// Minimum probability for mean curve computation (default = 0.001). /// Maximum probability for mean curve computation (default = 1 - 1e-9). /// If true, stores all parameter sets from sampled distributions. + /// Thrown when is null. + /// Thrown when a required array is null or empty. + /// Thrown when every sampled distribution is null or no finite sampled quantile is available. public UncertaintyAnalysisResults(UnivariateDistributionBase parentDistribution, UnivariateDistributionBase[] sampledDistributions, double[] probabilities, @@ -59,8 +62,7 @@ public UncertaintyAnalysisResults(UnivariateDistributionBase parentDistribution, { if (parentDistribution is null) throw new ArgumentNullException(nameof(parentDistribution)); - if (sampledDistributions == null || sampledDistributions.Length == 0) - throw new ArgumentException("Sampled distributions cannot be null or empty.", nameof(sampledDistributions)); + ValidateSampledDistributions(sampledDistributions); if (probabilities == null || probabilities.Length == 0) throw new ArgumentException("Probabilities cannot be null or empty.", nameof(probabilities)); @@ -357,10 +359,12 @@ public void ProcessModeCurve(UnivariateDistributionBase parentDistribution, doub /// The list of sampled distributions to process. /// Array of non-exceedance probabilities. /// The confidence level; Default = 0.1, which will result in the 90% confidence intervals. + /// Thrown when a required array is null or empty. + /// Thrown when is not strictly between zero and one. + /// Thrown when every sampled distribution is null or no finite sampled quantile is available. public void ProcessConfidenceIntervals(UnivariateDistributionBase[] sampledDistributions, double[] probabilities, double alpha = 0.1) { - if (sampledDistributions == null || sampledDistributions.Length == 0) - throw new ArgumentException("Sampled distributions cannot be null or empty.", nameof(sampledDistributions)); + ValidateSampledDistributions(sampledDistributions); if (probabilities == null || probabilities.Length == 0) throw new ArgumentException("Probabilities cannot be null or empty.", nameof(probabilities)); if (alpha <= 0 || alpha >= 1) @@ -386,14 +390,17 @@ public void ProcessConfidenceIntervals(UnivariateDistributionBase[] sampledDistr int validCount = 0; for (int j = 0; j < B; j++) { - if (!double.IsNaN(XValues[j])) validCount++; + if (Tools.IsFinite(XValues[j])) validCount++; } + if (validCount == 0) + throw new InvalidOperationException($"No finite sampled quantiles are available for probability {probabilities[i]}."); + var validValues = new double[validCount]; int writeIdx = 0; for (int j = 0; j < B; j++) { - if (!double.IsNaN(XValues[j])) + if (Tools.IsFinite(XValues[j])) validValues[writeIdx++] = XValues[j]; } @@ -413,10 +420,11 @@ public void ProcessConfidenceIntervals(UnivariateDistributionBase[] sampledDistr /// Array of non-exceedance probabilities for interpolation. /// Minimum probability for range determination (default = 0.001). /// Maximum probability for range determination (default = 1 - 1e-9). + /// Thrown when a required array is null or empty. + /// Thrown when every sampled distribution is null. public void ProcessMeanCurve(UnivariateDistributionBase[] sampledDistributions, double[] probabilities, double minProbability = 0.001, double maxProbability = 1 - 1e-9) { - if (sampledDistributions == null || sampledDistributions.Length == 0) - throw new ArgumentException("Sampled distributions cannot be null or empty.", nameof(sampledDistributions)); + ValidateSampledDistributions(sampledDistributions); if (probabilities == null || probabilities.Length == 0) throw new ArgumentException("Probabilities cannot be null or empty.", nameof(probabilities)); @@ -493,7 +501,8 @@ public void ProcessMeanCurve(UnivariateDistributionBase[] sampledDistributions, int validDistributions = 0; for (int c = 0; c < chunkCount; c++) validDistributions += chunkValid[c]; - if (validDistributions == 0) validDistributions = 1; + if (validDistributions == 0) + throw new InvalidOperationException("At least one sampled distribution must be non-null."); var expected = new double[bins]; for (int i = 0; i < bins; i++) @@ -532,6 +541,26 @@ public void ProcessMeanCurve(UnivariateDistributionBase[] sampledDistributions, MeanCurve = linint.Interpolate(probabilities); } + /// + /// Validates that an ensemble is present and contains at least one successful + /// distribution. + /// + /// The ensemble to validate. + /// Thrown when the ensemble is null or empty. + /// Thrown when every ensemble member is null. + private static void ValidateSampledDistributions(UnivariateDistributionBase[] sampledDistributions) + { + if (sampledDistributions == null || sampledDistributions.Length == 0) + throw new ArgumentException("Sampled distributions cannot be null or empty.", nameof(sampledDistributions)); + + for (int i = 0; i < sampledDistributions.Length; i++) + { + if (sampledDistributions[i] is not null) return; + } + + throw new InvalidOperationException("At least one sampled distribution must be non-null."); + } + /// /// Processes and stores the parameter sets from all sampled distributions. A sampled /// distribution that failed to fit contributes a parameter set of NaN values rather than a diff --git a/Test_Numerics/Distributions/Univariate/Test_BootstrapAnalysis.cs b/Test_Numerics/Distributions/Univariate/Test_BootstrapAnalysis.cs index 4f5cdd3a..290658ad 100644 --- a/Test_Numerics/Distributions/Univariate/Test_BootstrapAnalysis.cs +++ b/Test_Numerics/Distributions/Univariate/Test_BootstrapAnalysis.cs @@ -185,6 +185,48 @@ public void Test_BootstrapAnalysis_UncertaintyAnalysisResults_Equivalence() } } + /// + /// Confidence intervals exclude NaN and both infinities before computing percentiles. + /// + [TestMethod] + public void Test_UncertaintyAnalysisResults_ConfidenceIntervalsUseFiniteValuesOnly() + { + var results = new UncertaintyAnalysisResults(); + UnivariateDistributionBase[] sampledDistributions = + [ + new Normal(0.0, 1.0), + new Deterministic(7.0), + null! + ]; + + results.ProcessConfidenceIntervals(sampledDistributions, [0.0, 1.0]); + + double[,] confidenceIntervals = results.ConfidenceIntervals!; + Assert.AreEqual(7.0, confidenceIntervals[0, 0], 0.0); + Assert.AreEqual(7.0, confidenceIntervals[0, 1], 0.0); + Assert.AreEqual(7.0, confidenceIntervals[1, 0], 0.0); + Assert.AreEqual(7.0, confidenceIntervals[1, 1], 0.0); + } + + /// + /// An ensemble containing no successful distribution fails explicitly in both the + /// aggregate constructor and the public mean-curve processor. + /// + [TestMethod] + public void Test_UncertaintyAnalysisResults_AllNullEnsembleThrowsClearly() + { + UnivariateDistributionBase[] allNull = [null!, null!]; + + InvalidOperationException constructorException = Assert.Throws(() => + new UncertaintyAnalysisResults(new Normal(), allNull, [0.5])); + StringAssert.Contains(constructorException.Message, "At least one sampled distribution"); + + var results = new UncertaintyAnalysisResults(); + InvalidOperationException meanException = Assert.Throws(() => + results.ProcessMeanCurve(allNull, [0.5])); + StringAssert.Contains(meanException.Message, "At least one sampled distribution"); + } + /// /// Verifies Estimate() is bit-reproducible across calls at the same seed. Compares raw /// bits: a tolerance assert cannot detect a reduction-order difference. From 14d547eb81d2bf4e6b2904b286be83b83d4163a5 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 29 Aug 2026 13:04:18 -0600 Subject: [PATCH 189/222] Remove dead bootstrap standard error path --- .../Uncertainty Analysis/BootstrapAnalysis.cs | 68 ------------------- 1 file changed, 68 deletions(-) diff --git a/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs b/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs index 6a130a1e..e9a2ba37 100644 --- a/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs +++ b/Numerics/Distributions/Univariate/Uncertainty Analysis/BootstrapAnalysis.cs @@ -929,75 +929,7 @@ private double[] BootstrapStandardError(UnivariateDistributionBase parentDist, I } return standardErrors; } - /// - /// Estimates jackknife standard errors from successful leave-one-out fits. - /// - /// The observed sample. - /// The non-exceedance probabilities. - /// The cube-root-transformed fitted population quantiles. - /// One jackknife standard error per probability. - /// Thrown when every leave-one-out fit fails. - /// Chunked as . - private double[] StandardError(IList sampleData, IList probabilities, IList thetaHats) - { - int sampleCount = sampleData.Count; - int probabilityCount = probabilities.Count; - var standardErrors = new double[probabilityCount]; - if (sampleCount == 0) return standardErrors; - - int chunks = Math.Min(ReductionChunks, sampleCount); - var chunkSecondMoments = new double[chunks][]; - var chunkSuccesses = new int[chunks]; - var failures = new Exception?[sampleCount]; - for (int chunk = 0; chunk < chunks; chunk++) - chunkSecondMoments[chunk] = new double[probabilityCount]; - - Parallel.For(0, chunks, chunk => - { - var secondMoments = chunkSecondMoments[chunk]; - int start = (int)((long)chunk * sampleCount / chunks); - int end = (int)((long)(chunk + 1) * sampleCount / chunks); - int successes = 0; - for (int index = start; index < end; index++) - { - var jackknifeSample = new double[sampleCount - 1]; - for (int k = 0; k < index; k++) jackknifeSample[k] = sampleData[k]; - for (int k = index + 1; k < sampleCount; k++) jackknifeSample[k - 1] = sampleData[k]; - - var distribution = ((UnivariateDistributionBase)Distribution).Clone(); - try - { - ((IEstimation)distribution).Estimate(jackknifeSample, EstimationMethod); - for (int i = 0; i < probabilityCount; i++) - { - double difference = thetaHats[i] - CubeRoot(distribution.InverseCDF(probabilities[i])); - secondMoments[i] += difference * difference; - } - successes++; - } - catch (Exception exception) - { - failures[index] = exception; - } - } - chunkSuccesses[chunk] = successes; - }); - - int successfulFits = 0; - for (int chunk = 0; chunk < chunks; chunk++) successfulFits += chunkSuccesses[chunk]; - if (successfulFits == 0) - throw new AggregateException("Every jackknife standard-error fit failed.", failures.Where(exception => exception != null).Cast()); - for (int i = 0; i < probabilityCount; i++) - { - double secondMoment = 0d; - for (int chunk = 0; chunk < chunks; chunk++) secondMoment += chunkSecondMoments[chunk][i]; - standardErrors[i] = successfulFits > 1 - ? Math.Sqrt((successfulFits - 1d) / successfulFits * secondMoment) - : 0d; - } - return standardErrors; - } /// /// Returns finite results from successful fits and enforces a minimum sample count. /// From 033949b711db4ac26ed83ead8f6f51fa4b52197d Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 29 Aug 2026 13:07:10 -0600 Subject: [PATCH 190/222] Stabilize global sensitivity tie ranks --- Numerics/Data/Statistics/GlobalSensitivity.cs | 18 ++++++++--------- .../Data/Statistics/Test_GlobalSensitivity.cs | 20 +++++++++++++++++++ 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/Numerics/Data/Statistics/GlobalSensitivity.cs b/Numerics/Data/Statistics/GlobalSensitivity.cs index b1b74b4b..cd03b035 100644 --- a/Numerics/Data/Statistics/GlobalSensitivity.cs +++ b/Numerics/Data/Statistics/GlobalSensitivity.cs @@ -19,8 +19,8 @@ namespace Numerics.Data.Statistics /// the PAWN indices measure conditional-versus-unconditional distribution shifts through /// Kolmogorov-Smirnov statistics (sensitive to changes a variance ratio misses), and the /// Borgonovo delta is a moment-independent total-variation measure suited to tail-driven - /// outputs. All three are deterministic: binning is by rank with ties resolved by the sort's - /// deterministic order, and no randomness is used anywhere. + /// outputs. All three are deterministic: binning is by rank with ties resolved by original + /// sample index, and no randomness is used anywhere. /// /// References: /// @@ -225,22 +225,22 @@ private static void ValidateSamples(IList x, IList y, int bins) } /// - /// Returns the sample indices sorted ascending by value. Ties keep the sort's deterministic - /// order, so identical inputs always produce identical bin assignments. + /// Returns the sample indices sorted ascending by value. Ties are ordered by original + /// sample index so bin assignments do not depend on framework sort stability. /// /// The values to rank. /// The sorted index array. private static int[] SortIndicesBy(IList values) { int n = values.Count; - var keys = new double[n]; var order = new int[n]; for (int i = 0; i < n; i++) - { - keys[i] = values[i]; order[i] = i; - } - Array.Sort(keys, order); + Array.Sort(order, (first, second) => + { + int comparison = values[first].CompareTo(values[second]); + return comparison != 0 ? comparison : first.CompareTo(second); + }); return order; } diff --git a/Test_Numerics/Data/Statistics/Test_GlobalSensitivity.cs b/Test_Numerics/Data/Statistics/Test_GlobalSensitivity.cs index b517fd13..2ed12e39 100644 --- a/Test_Numerics/Data/Statistics/Test_GlobalSensitivity.cs +++ b/Test_Numerics/Data/Statistics/Test_GlobalSensitivity.cs @@ -180,6 +180,26 @@ public void Test_Determinism_RepeatedCallsBitEqual() Assert.AreEqual(first[b], second[b], 0d); } + /// + /// Equal rank values are resolved by their original sample indices, so a tied input with + /// outputs split by original position forms the same two pure bins on every framework. + /// + [TestMethod] + public void Test_TiedRanks_UseOriginalIndexOrder() + { + var x = new double[32]; + var y = new double[32]; + for (int i = 0; i < x.Length; i++) + { + x[i] = 1d; + y[i] = i < x.Length / 2 ? 0d : 1d; + } + + Assert.AreEqual(1d, GlobalSensitivity.FirstOrderSobol(x, y, 2), 0d); + CollectionAssert.AreEqual(new double[] { 0.5d, 0.5d }, GlobalSensitivity.Pawn(x, y, 2)); + Assert.AreEqual(0.5d, GlobalSensitivity.BorgonovoDelta(x, y, 2, 2), 0d); + } + /// /// Guard matrix: nulls, length mismatches, degenerate bin counts, samples shorter than the /// bin counts, and non-finite values all throw the documented exceptions, and a From b7afd7ff5535d951c4a337083825e14480e91e11 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 29 Aug 2026 13:10:13 -0600 Subject: [PATCH 191/222] Harden Archimedean conditional boundaries --- .../Base/ArchimedeanCopula.cs | 10 +- .../Bivariate Copulas/Base/BivariateCopula.cs | 5 +- .../Bivariate Copulas/Test_BivariateCopula.cs | 123 ++++++++++++++++++ 3 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 Test_Numerics/Distributions/Bivariate Copulas/Test_BivariateCopula.cs diff --git a/Numerics/Distributions/Bivariate Copulas/Base/ArchimedeanCopula.cs b/Numerics/Distributions/Bivariate Copulas/Base/ArchimedeanCopula.cs index dd62bbea..535dd53d 100644 --- a/Numerics/Distributions/Bivariate Copulas/Base/ArchimedeanCopula.cs +++ b/Numerics/Distributions/Bivariate Copulas/Base/ArchimedeanCopula.cs @@ -121,11 +121,19 @@ public override double CDF(double u, double v) /// Joe h = (1−u)^(θ−1)·[1 − (1−v)^θ]·A^(1/θ−1) with A = (1−u)^θ + (1−v)^θ − (1−u)^θ(1−v)^θ; /// Ali-Mikhail-Haq h = v(1 − θ(1−v))/D² with D = 1 − θ(1−u)(1−v). /// + /// + /// The generic generator ratio has conditioning domain 0 < u < 1. Within that domain, + /// the dependent-variable boundaries are exact: h(0|u) = 0 and h(1|u) = 1. Conditioning + /// endpoint limits are family-specific, so this base implementation does not impose a + /// family-independent value at u = 0 or u = 1. + /// /// public override double ConditionalCDF(double u, double v) { // Validate parameters if (_parametersValid == false) ValidateParameter(Theta, true); + if (v == 0d) return 0d; + if (v == 1d) return 1d; return GeneratorPrime(u) / GeneratorPrime(CDF(u, v)); } @@ -157,4 +165,4 @@ public override double[] InverseCDF(double u, double v) } } -} \ No newline at end of file +} diff --git a/Numerics/Distributions/Bivariate Copulas/Base/BivariateCopula.cs b/Numerics/Distributions/Bivariate Copulas/Base/BivariateCopula.cs index 570ac9fa..5c2ddab4 100644 --- a/Numerics/Distributions/Bivariate Copulas/Base/BivariateCopula.cs +++ b/Numerics/Distributions/Bivariate Copulas/Base/BivariateCopula.cs @@ -156,7 +156,10 @@ public virtual double ConditionalCDF(double u, double v) /// implementation delegates to that array form; every copula shipped with the library /// overrides this method with the scalar computation and recomposes the array form on /// top of it, so hot loops can invert conditional probabilities without per-call - /// allocation. + /// allocation. Subclass authors must not implement + /// by delegating its second coordinate back to this inherited method, because the two + /// base calls would recurse. Override this method when composing the array form from a + /// scalar conditional inverse. /// public virtual double InverseConditionalCDF(double u, double t) { diff --git a/Test_Numerics/Distributions/Bivariate Copulas/Test_BivariateCopula.cs b/Test_Numerics/Distributions/Bivariate Copulas/Test_BivariateCopula.cs new file mode 100644 index 00000000..e8db0a01 --- /dev/null +++ b/Test_Numerics/Distributions/Bivariate Copulas/Test_BivariateCopula.cs @@ -0,0 +1,123 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Distributions.Copulas; +using System; +using System.Collections.Generic; + +namespace Distributions.BivariateCopulas +{ + /// + /// Unit tests for behavior supplied by the bivariate and Archimedean copula base classes. + /// + [TestClass] + public class Test_BivariateCopula + { + /// + /// A subclass that implements only the required product-copula primitives receives the + /// finite-difference conditional and array-delegating inverse fallbacks from the base class. + /// + [TestMethod] + public void Test_BaseConditionalFallbacks() + { + var copula = new ProductFallbackCopula(); + + Assert.AreEqual(0.63d, copula.ConditionalCDF(0.37d, 0.63d), 1E-10); + Assert.AreEqual(0.63d, copula.ConditionalCDF(0d, 0.63d), 1E-10); + Assert.AreEqual(0.63d, copula.ConditionalCDF(1d, 0.63d), 1E-10); + Assert.AreEqual(0.42d, copula.InverseConditionalCDF(0.37d, 0.42d), 0d); + } + + /// + /// Every generic Archimedean h-function returns the exact dependent-variable boundaries + /// for an interior conditioning probability, without evaluating singular generators. + /// + [TestMethod] + public void Test_ArchimedeanConditionalCDF_DependentVariableBoundaries() + { + var copulas = new ArchimedeanCopula[] + { + new AMHCopula(0.5d), + new ClaytonCopula(2d), + new FrankCopula(5d), + new GumbelCopula(2d), + new JoeCopula(2d) + }; + + foreach (ArchimedeanCopula copula in copulas) + { + Assert.AreEqual(0d, copula.ConditionalCDF(0.4d, 0d), 0d, copula.DisplayName); + Assert.AreEqual(1d, copula.ConditionalCDF(0.4d, 1d), 0d, copula.DisplayName); + } + } + + /// + /// Minimal product copula used to exercise the virtual base implementations directly. + /// + private sealed class ProductFallbackCopula : BivariateCopula + { + /// + public override CopulaType Type => CopulaType.Independence; + + /// + public override double ThetaMinimum => 0d; + + /// + public override double ThetaMaximum => 0d; + + /// + public override string[,] ParameterToString => new string[0, 2]; + + /// + public override string ParameterNameShortForm => string.Empty; + + /// + public override string DisplayName => "Product fallback"; + + /// + public override string ShortDisplayName => "Product"; + + /// + public override double UpperTailDependence => 0d; + + /// + public override double LowerTailDependence => 0d; + + /// + public override int NumberOfCopulaParameters => 0; + + /// + public override double[] GetCopulaParameters => Array.Empty(); + + /// + public override double PDF(double u, double v) => 1d; + + /// + public override double CDF(double u, double v) => u * v; + + /// + public override double[] InverseCDF(double u, double v) => new double[] { u, v }; + + /// + public override void SetCopulaParameters(double[] parameters) + { + } + + /// + public override double[,] ParameterConstraints(IList sampleDataX, IList sampleDataY) + { + return new double[0, 2]; + } + + /// + public override ArgumentOutOfRangeException ValidateParameter(double parameter, bool throwException) + { + return null; + } + + /// + public override BivariateCopula Clone() + { + return new ProductFallbackCopula(); + } + } + } +} From 00a9a5779432413a820584fef2a5845702a3bf0f Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 29 Aug 2026 13:14:47 -0600 Subject: [PATCH 192/222] Reject indexed time series division by zero --- Numerics/Data/Time Series/TimeSeries.cs | 5 ++++- .../Data/Time Series/Test_TimeSeries.cs | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Numerics/Data/Time Series/TimeSeries.cs b/Numerics/Data/Time Series/TimeSeries.cs index 8230e529..e5d443c0 100644 --- a/Numerics/Data/Time Series/TimeSeries.cs +++ b/Numerics/Data/Time Series/TimeSeries.cs @@ -298,6 +298,7 @@ public void Multiply(double constant, IList indexes) /// Divide each value in the time-series by a constant. Missing values are kept as missing. /// /// Factor to divide each value by in the series. + /// Thrown when is zero. public void Divide(double constant) { if (constant == 0) throw new ArgumentException("Cannot divide by zero.", nameof(constant)); @@ -315,8 +316,10 @@ public void Divide(double constant) /// /// Factor to divide each value by in the series. /// List of integer index values (0 based) for each ordinate in the time series to apply the calculation to. + /// Thrown when is zero. public void Divide(double constant, IList indexes) { + if (constant == 0) throw new ArgumentException("Cannot divide by zero.", nameof(constant)); SuppressCollectionChanged = true; for (int i = 0; i < indexes.Count; i++) { @@ -2359,4 +2362,4 @@ public TimeSeries Clone() } } -} \ No newline at end of file +} diff --git a/Test_Numerics/Data/Time Series/Test_TimeSeries.cs b/Test_Numerics/Data/Time Series/Test_TimeSeries.cs index c8fb2ba2..e5b788ea 100644 --- a/Test_Numerics/Data/Time Series/Test_TimeSeries.cs +++ b/Test_Numerics/Data/Time Series/Test_TimeSeries.cs @@ -227,6 +227,25 @@ public void Test_Math() Equal(ts, values); } + /// + /// The indexed divide overload rejects a zero divisor with the same exception contract as + /// its all-values twin and leaves the selected values unchanged. + /// + [TestMethod] + public void Test_Divide_Indexed_RejectsZeroLikeTwin() + { + var plain = new TimeSeries(TimeInterval.OneDay, new DateTime(2023, 01, 01), new double[] { 2d, 4d }); + var indexed = plain.Clone(); + + var expected = Assert.Throws(() => plain.Divide(0d)); + var actual = Assert.Throws(() => indexed.Divide(0d, new[] { 1 })); + + Assert.AreEqual(expected.ParamName, actual.ParamName); + Assert.AreEqual(expected.Message, actual.Message); + Assert.AreEqual(2d, indexed[0].Value, 0d); + Assert.AreEqual(4d, indexed[1].Value, 0d); + } + /// /// Verifies that the indexed log transform skips out-of-range indexes like its sibling overloads. /// From 4f63ac9055d8c57fcc1841b40de02969311e5c25 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 29 Aug 2026 13:16:25 -0600 Subject: [PATCH 193/222] Correct table path nullability contract --- Numerics/Mathematics/Optimization/Dynamic/Network.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Numerics/Mathematics/Optimization/Dynamic/Network.cs b/Numerics/Mathematics/Optimization/Dynamic/Network.cs index f33fcbcd..42752bb3 100644 --- a/Numerics/Mathematics/Optimization/Dynamic/Network.cs +++ b/Numerics/Mathematics/Optimization/Dynamic/Network.cs @@ -396,7 +396,7 @@ private void EnsureScratch() /// Thrown when the edge indices or table are null. /// Thrown when the table dimensions are not [, 3]. /// Thrown when the start node is outside the network. - public List? GetPath(int[] edgesToRemove, int startNodeIndex, float[,] existingResultsTable) + public List GetPath(int[] edgesToRemove, int startNodeIndex, float[,] existingResultsTable) { if (edgesToRemove == null) throw new ArgumentNullException(nameof(edgesToRemove)); ValidateResultTable(existingResultsTable); From 9b66ad7f77d91dd60e3870104dc0edee907a830b Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sun, 30 Aug 2026 07:27:01 -0600 Subject: [PATCH 194/222] Fix exponential MLE location constraint --- .../Distributions/Univariate/Exponential.cs | 4 ++-- .../Univariate/Test_Exponential.cs | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Numerics/Distributions/Univariate/Exponential.cs b/Numerics/Distributions/Univariate/Exponential.cs index 185b850d..8a86d85a 100644 --- a/Numerics/Distributions/Univariate/Exponential.cs +++ b/Numerics/Distributions/Univariate/Exponential.cs @@ -325,7 +325,7 @@ public Tuple GetParameterConstraints(IList // Get bounds of location if (initialVals[0] == 0d) initialVals[0] = Tools.DoubleMachineEpsilon; lowerVals[0] = initialVals[0] - Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])))); - upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[0]) + 1d)); + upperVals[0] = minData; // Get bounds of scale lowerVals[1] = Tools.DoubleMachineEpsilon; @@ -596,4 +596,4 @@ static double Sn(double t, int n) } -} \ No newline at end of file +} diff --git a/Test_Numerics/Distributions/Univariate/Test_Exponential.cs b/Test_Numerics/Distributions/Univariate/Test_Exponential.cs index 3113712f..1ef09ac2 100644 --- a/Test_Numerics/Distributions/Univariate/Test_Exponential.cs +++ b/Test_Numerics/Distributions/Univariate/Test_Exponential.cs @@ -100,6 +100,25 @@ public void Test_EXP_MLE_Fit() Assert.IsLessThan(0.01d, (a - true_a) / true_a); } + /// + /// Verifies that a negative maximum-likelihood location initializer receives finite bounds. + /// + [TestMethod()] + public void Test_EXP_ParameterConstraints_NegativeLocationInitial_HasFiniteBounds() + { + var exponential = new Exponential(); + double[] values = [0.1d, 10d, 20d, 30d]; + + var constraints = exponential.GetParameterConstraints(values); + + Assert.IsLessThan(0d, constraints.Item1[0]); + Assert.IsFalse(double.IsNaN(constraints.Item2[0]) || double.IsInfinity(constraints.Item2[0])); + Assert.IsFalse(double.IsNaN(constraints.Item3[0]) || double.IsInfinity(constraints.Item3[0])); + Assert.AreEqual(0.1d, constraints.Item3[0]); + Assert.IsLessThan(constraints.Item3[0], constraints.Item1[0]); + Assert.IsGreaterThan(constraints.Item2[0], constraints.Item1[0]); + } + /// /// Test the quantile function for the Exponential Distribution. /// From 7e59af7204518f6119e8ec4026e039d12dd305c6 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sun, 30 Aug 2026 16:43:41 -0600 Subject: [PATCH 195/222] Condition GMM covariance and align competing-risk recovery tests --- .../Unsupervised/GaussianMixtureModel.cs | 6 +- .../Linear Algebra/MatrixRegularization.cs | 29 +- .../Univariate/Test_CompetingRisks.cs | 1217 ++++++++--------- .../Machine Learning/Unsupervised/Test_GMM.cs | 67 +- .../Test_MatrixRegularization.cs | 37 +- 5 files changed, 661 insertions(+), 695 deletions(-) diff --git a/Numerics/Machine Learning/Unsupervised/GaussianMixtureModel.cs b/Numerics/Machine Learning/Unsupervised/GaussianMixtureModel.cs index 193c0e06..7a16e845 100644 --- a/Numerics/Machine Learning/Unsupervised/GaussianMixtureModel.cs +++ b/Numerics/Machine Learning/Unsupervised/GaussianMixtureModel.cs @@ -367,9 +367,9 @@ private void MStep() } // Ensure the full covariance matrix remains symmetric positive-definite. The helper is - // pure — it returns a symmetrized copy with a trace-scaled ridge — so its result must be - // assigned; a discarded call leaves the repair a no-op and the next E-step's Cholesky - // factorization protected only by the diagonal floor above. + // pure: it returns the symmetrized covariance unchanged when usable and adds a + // trace-scaled ridge only when needed. Its result must be assigned so an actual repair + // reaches the covariance consumed by the next E-step's Cholesky factorization. Sigmas[k] = MatrixRegularization.MakeSymmetricPositiveDefinite(Sigmas[k]); } } diff --git a/Numerics/Mathematics/Linear Algebra/MatrixRegularization.cs b/Numerics/Mathematics/Linear Algebra/MatrixRegularization.cs index 98a5bbe7..8f790a00 100644 --- a/Numerics/Mathematics/Linear Algebra/MatrixRegularization.cs +++ b/Numerics/Mathematics/Linear Algebra/MatrixRegularization.cs @@ -104,14 +104,39 @@ private static double MedianFromVector(Vector v) } /// - /// Makes the matrix symmetric and positive definite. + /// Determines whether the Cholesky decomposition accepts a matrix as positive definite. + /// + /// The symmetric matrix to test. + /// when the decomposition succeeds; otherwise, . + private static bool CholeskyAccepts(Matrix matrix) + { + try + { + _ = new CholeskyDecomposition(matrix); + return true; + } + catch (Exception) + { + return false; + } + } + + /// + /// Makes the matrix symmetric and, when necessary, adds a ridge until Cholesky accepts it as + /// positive definite. /// /// The matrix to adjust. /// A symmetric and positive definite matrix. + /// + /// The symmetric input is returned without a ridge when its Cholesky decomposition succeeds. + /// A failed decomposition enters the existing trace-scaled ridge escalation. + /// public static Matrix MakeSymmetricPositiveDefinite(Matrix M) { // Symmetrize var S = 0.5 * (M + M.Transpose()); + if (CholeskyAccepts(S)) return S; + // Tiny trace-scaled ridge double tr = 0.0; for (int i = 0; i < S.NumberOfRows; i++) tr += S[i, i]; @@ -123,7 +148,7 @@ public static Matrix MakeSymmetricPositiveDefinite(Matrix M) var T = S.Clone(); double ridge = baseRidge * Math.Pow(10.0, k); for (int i = 0; i < T.NumberOfRows; i++) T[i, i] += ridge; - try { var _ = new CholeskyDecomposition(T); return T; } catch { /* retry bigger ridge */ } + if (CholeskyAccepts(T)) return T; } // Last resort: add a biggish ridge var U = S.Clone(); diff --git a/Test_Numerics/Distributions/Univariate/Test_CompetingRisks.cs b/Test_Numerics/Distributions/Univariate/Test_CompetingRisks.cs index fe86d4f4..bd925638 100644 --- a/Test_Numerics/Distributions/Univariate/Test_CompetingRisks.cs +++ b/Test_Numerics/Distributions/Univariate/Test_CompetingRisks.cs @@ -1,10 +1,13 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Data; +using Numerics; using Numerics.Distributions; using Numerics.Data.Statistics; using Numerics.Mathematics; using Numerics.Mathematics.Integration; +using Numerics.Mathematics.LinearAlgebra; using Numerics.Mathematics.SpecialFunctions; +using Numerics.Sampling; using System; using System.Reflection; using System.Xml.Linq; @@ -163,136 +166,6 @@ public void Test_LogPDF_StableForMLE() Assert.IsFalse(double.IsNaN(logLik), "Log-likelihood should not be NaN"); } - /// - /// Verifies that MLE converges to approximately correct parameter values for a - /// competing risks model under the minimum rule (series system). - /// - /// - /// - /// Background: - /// Under the minimum rule, the competing risks model represents a series system - /// where the observed outcome is the minimum of the competing random variables. - /// This is commonly used in reliability analysis where system failure occurs - /// when the first component fails. - /// - /// - /// Test Strategy: - /// - /// Create a "true" model with known Weibull parameters: - /// - /// Distribution 1: Weibull(shape=2.0, scale=100) - /// Distribution 2: Weibull(shape=3.0, scale=120) - /// - /// - /// Generate a sample of 500 observations from the true model - /// Fit a new competing risks model using MLE - /// Verify estimated parameters are within 20% of true values - /// - /// - /// - /// Expected Behavior: - /// The MLE should converge to parameter estimates close to the true values. - /// A tolerance of 20% accounts for sampling variability with n=500. - /// - /// - /// Note: - /// The minimum rule is generally more numerically stable than the maximum rule - /// because the survival function S(x) = 1 - F(x) is bounded away from zero - /// in the left tail where most data typically falls. - /// - /// - [TestMethod] - public void Test_MLE_ConvergesForMinRule() - { - // True parameters - var trueDist1 = new Weibull(2.0, 100); - var trueDist2 = new Weibull(3.0, 120); - var trueCR = new CompetingRisks(new[] { trueDist1, trueDist2 }); - trueCR.MinimumOfRandomVariables = true; - - // Generate sample - var sample = trueCR.GenerateRandomValues(500, 12345); - - // Fit model - var fitDist1 = new Weibull(); - var fitDist2 = new Weibull(); - var fitCR = new CompetingRisks(new[] { fitDist1, fitDist2 }); - fitCR.MinimumOfRandomVariables = true; - - var mleParams = fitCR.MLE(sample); - fitCR.SetParameters(mleParams); - - // Verify parameters are reasonable (within 20% of true) - Assert.AreEqual(2.0, mleParams[0], 0.4, "Shape1 should be close to 2.0"); - Assert.AreEqual(100.0, mleParams[1], 20.0, "Scale1 should be close to 100"); - } - - /// - /// Verifies that MLE converges to valid (non-NaN) parameter values for a - /// competing risks model under the maximum rule (parallel system). - /// - /// - /// - /// Background: - /// Under the maximum rule, the competing risks model represents a parallel system - /// where the observed outcome is the maximum of the competing random variables. - /// This is used in reliability analysis where system failure occurs only when - /// all components have failed (redundant systems). - /// - /// - /// Test Strategy: - /// - /// Create a "true" model with known Weibull parameters: - /// - /// Distribution 1: Weibull(shape=2.0, scale=100) - /// Distribution 2: Weibull(shape=3.0, scale=120) - /// - /// - /// Generate a sample of 500 observations from the true model - /// Fit a new competing risks model using MLE - /// Verify estimated parameters are not NaN - /// - /// - /// - /// Expected Behavior: - /// The MLE should converge without numerical failures. This test uses a weaker - /// assertion (not NaN) rather than checking closeness to true values because - /// the maximum rule has inherent identifiability challenges - with only the - /// maximum observed, distinguishing between component distributions is difficult. - /// - /// - /// Note: - /// The maximum rule is more prone to numerical instability because the CDF F(x) - /// approaches zero in the left tail, causing division issues in the PDF formula. - /// This test specifically validates that the numerical stability improvements - /// allow the optimizer to complete without NaN propagation. - /// - /// - [TestMethod] - public void Test_MLE_ConvergesForMaxRule() - { - // True parameters - var trueDist1 = new Weibull(2.0, 100); - var trueDist2 = new Weibull(3.0, 120); - var trueCR = new CompetingRisks(new[] { trueDist1, trueDist2 }); - trueCR.MinimumOfRandomVariables = false; // Maximum rule - - // Generate sample - var sample = trueCR.GenerateRandomValues(500, 12345); - - // Fit model - var fitDist1 = new Weibull(); - var fitDist2 = new Weibull(); - var fitCR = new CompetingRisks(new[] { fitDist1, fitDist2 }); - fitCR.MinimumOfRandomVariables = false; - - var mleParams = fitCR.MLE(sample); - fitCR.SetParameters(mleParams); - - // Verify parameters are reasonable - Assert.IsFalse(mleParams.Any(p => double.IsNaN(p)), "MLE parameters should not be NaN"); - } - /// /// Verifies that the PDF integrates to approximately 1 over the support of the distribution. /// @@ -575,615 +448,641 @@ public void Test_GenerateRandomValues_CorrelationMatrixRejectsNonPositiveDefinit StringAssert.Contains(exception.Message, "positive definite"); } - // Tolerances - competing risks MLE is harder than single distribution MLE - private const double SHAPE_TOLERANCE_PERCENT = 0.25; // 25% relative error - private const double SCALE_TOLERANCE_PERCENT = 0.30; // 30% relative error - private const int SAMPLE_SIZE = 1000; - private const int RANDOM_SEED = 12345; + private const int RECOVERY_SAMPLE_SIZE = 1000; + private const int RECOVERY_SEED = 12345; /// - /// Tests MLE for minimum of Exponential and Weibull distributions. + /// Verifies MLE recovery for the identified independent minimum dog leg formed by + /// Weibull(50, 1) and Weibull(80, 3). /// - /// - /// - /// Configuration Rationale: - /// This is the classic "random failures + wear-out failures" model: - /// - /// Exponential(λ=0.02): Constant hazard rate, models random/early failures - /// Weibull(k=3, λ=80): Increasing hazard rate (k>1), models wear-out/aging - /// - /// - /// - /// Why This Is Identifiable: - /// The Exponential has constant hazard h(t) = λ, while the Weibull with k=3 has - /// hazard h(t) = (k/λ)(t/λ)^(k-1) which increases with t. The exponential dominates - /// early (small t) while the Weibull dominates later (large t). This creates - /// a characteristic "bathtub curve" effect that allows separation. - /// - /// - /// Expected Behavior: - /// The exponential rate parameter and Weibull shape/scale should be recoverable - /// within tolerance. The Weibull scale may have higher variance due to fewer - /// observations in the wear-out region. - /// - /// [TestMethod] - public void Test_MLE_MinRule_2Dist_Exponential_Weibull() + public void Test_MLE_MinRule_2Dist_Weibull_DogLeg() { - // True parameters - designed for identifiability - // Weibull(k=1) ≡ Exponential, dominates early failures - // Weibull(k=3) has increasing hazard, dominates wear-out - // Note: Weibull(scale, shape=1) is used instead of Exponential to avoid - // the Exponential's 2-parameter (location+scale) MLE issues in competing risks context. - double trueScale1 = 50.0; // Weibull(50,1) = Exponential with mean 50 - double trueShape1 = 1.0; // Constant hazard - double trueScale2 = 80.0; // Mean ≈ 71 - double trueShape2 = 3.0; // Increasing hazard - - var trueDist1 = new Weibull(trueScale1, trueShape1); - var trueDist2 = new Weibull(trueScale2, trueShape2); - var trueCR = new CompetingRisks(new UnivariateDistributionBase[] { trueDist1, trueDist2 }); - trueCR.MinimumOfRandomVariables = true; + var parent = new CompetingRisks(new UnivariateDistributionBase[] + { + new Weibull(50d, 1d), + new Weibull(80d, 3d) + }) + { + MinimumOfRandomVariables = true + }; + var fitted = new CompetingRisks(new UnivariateDistributionBase[] + { + new Weibull(), + new Weibull() + }) + { + MinimumOfRandomVariables = true + }; - // Generate sample - var sample = trueCR.GenerateRandomValues(SAMPLE_SIZE, RANDOM_SEED); - - // Verify sample statistics are reasonable - double sampleMean = sample.Average(); - double sampleMin = sample.Min(); - double sampleMax = sample.Max(); - Console.WriteLine($"Sample: n={SAMPLE_SIZE}, mean={sampleMean:F2}, min={sampleMin:F2}, max={sampleMax:F2}"); - - // Fit model - var fitDist1 = new Weibull(); - var fitDist2 = new Weibull(); - var fitCR = new CompetingRisks(new UnivariateDistributionBase[] { fitDist1, fitDist2 }); - fitCR.MinimumOfRandomVariables = true; - - var mleParams = fitCR.MLE(sample); - fitCR.SetParameters(mleParams); - - // Weibull params are [scale, shape] for each component - Console.WriteLine($"True: Weibull({trueScale1},{trueShape1}), Weibull({trueScale2},{trueShape2})"); - Console.WriteLine($"Fitted: Weibull({mleParams[0]:F3},{mleParams[1]:F3}), Weibull({mleParams[2]:F3},{mleParams[3]:F3})"); - - // Assertions with tolerance - Assert.IsFalse(mleParams.Any(p => double.IsNaN(p) || double.IsInfinity(p)), "All parameters should be finite"); - - // Verify overall fit - double ksStatistic = ComputeKSStatistic(sample, fitCR); - Console.WriteLine($"KS statistic: {ksStatistic:F4}"); - Assert.IsLessThan(0.05, ksStatistic, "KS statistic should indicate good fit"); + VerifyIdentifiedMleRecovery(parent, fitted, "independent two-Weibull minimum dog leg"); } /// - /// Tests MLE for minimum of two Weibull distributions with different shapes. + /// Verifies MLE recovery for the identified independent maximum dog leg formed by + /// Weibull(100, 3) and Gumbel(80, 20). /// - /// - /// - /// Configuration Rationale: - /// Two Weibulls with contrasting shapes and well-separated scales: - /// - /// Weibull(k=0.8, λ=30): Decreasing hazard (k<1), dominates very early - /// Weibull(k=3.0, λ=100): Increasing hazard (k>1), dominates later - /// - /// - /// - /// Why This Is Identifiable: - /// The k=0.8 distribution has decreasing hazard (infant mortality pattern), - /// while k=3.0 has increasing hazard (wear-out pattern). Combined with the - /// 3:1 scale ratio, the distributions contribute in clearly different time regions. - /// The first dominates the left tail, the second shapes the right tail. - /// - /// [TestMethod] - public void Test_MLE_MinRule_2Dist_Weibull_DifferentShapes() + public void Test_MLE_MaxRule_2Dist_Weibull_Gumbel_DogLeg() { - // Weibull 1: Decreasing hazard (infant mortality) - double trueShape1 = 0.8; - double trueScale1 = 30.0; - - // Weibull 2: Increasing hazard (wear-out) - double trueShape2 = 3.0; - double trueScale2 = 100.0; - - var trueDist1 = new Weibull(trueScale1, trueShape1); - var trueDist2 = new Weibull(trueScale2, trueShape2); - var trueCR = new CompetingRisks(new UnivariateDistributionBase[] { trueDist1, trueDist2 }); - trueCR.MinimumOfRandomVariables = true; - - var sample = trueCR.GenerateRandomValues(SAMPLE_SIZE, RANDOM_SEED); - - Console.WriteLine($"Sample: n={SAMPLE_SIZE}, mean={sample.Average():F2}, median={sample.OrderBy(x => x).ElementAt(SAMPLE_SIZE / 2):F2}"); - - // Fit model - var fitDist1 = new Weibull(); - var fitDist2 = new Weibull(); - var fitCR = new CompetingRisks(new UnivariateDistributionBase[] { fitDist1, fitDist2 }); - fitCR.MinimumOfRandomVariables = true; - - var mleParams = fitCR.MLE(sample); - fitCR.SetParameters(mleParams); - - Console.WriteLine($"True: Weibull({trueShape1}, {trueScale1}), Weibull({trueShape2}, {trueScale2})"); - Console.WriteLine($"Fitted: Weibull({mleParams[0]:F3}, {mleParams[1]:F3}), Weibull({mleParams[2]:F3}, {mleParams[3]:F3})"); - - // Verify no NaN - Assert.IsFalse(mleParams.Any(double.IsNaN), "No parameters should be NaN"); - - // Check parameter recovery (allowing for label switching) - // Weibull params are [scale, shape], so shape indices are 1 and 3 - bool config1 = IsCloseRelative(mleParams[1], trueShape1, SHAPE_TOLERANCE_PERCENT) && - IsCloseRelative(mleParams[3], trueShape2, SHAPE_TOLERANCE_PERCENT); - bool config2 = IsCloseRelative(mleParams[1], trueShape2, SHAPE_TOLERANCE_PERCENT) && - IsCloseRelative(mleParams[3], trueShape1, SHAPE_TOLERANCE_PERCENT); + var parent = new CompetingRisks(new UnivariateDistributionBase[] + { + new Weibull(100d, 3d), + new Gumbel(80d, 20d) + }) + { + MinimumOfRandomVariables = false + }; + var fitted = new CompetingRisks(new UnivariateDistributionBase[] + { + new Weibull(), + new Gumbel() + }) + { + MinimumOfRandomVariables = false + }; - Assert.IsTrue(config1 || config2, - "Fitted shapes should match true shapes (allowing for label switching)"); + VerifyIdentifiedMleRecovery(parent, fitted, "independent Weibull-Gumbel maximum dog leg"); } - #endregion - - #region Minimum Rule - 3 Distributions - /// - /// Tests MLE for minimum of three distributions: Exponential + two Weibulls. + /// Verifies MLE recovery for the identified fixed-correlation minimum dog leg formed by + /// Weibull(50, 1) and Weibull(80, 3) at latent Gaussian correlation 0.6. /// - /// - /// - /// Configuration Rationale: - /// A three-component "bathtub curve" model: - /// - /// Weibull(k=0.7, λ=20): Decreasing hazard - infant mortality - /// Exponential(λ=0.005): Constant hazard - random failures (useful life) - /// Weibull(k=4, λ=150): Steeply increasing hazard - wear-out - /// - /// - /// - /// Why This Is Identifiable: - /// Each distribution dominates a different time region: - /// - /// Early: Weibull(0.7, 20) with its decreasing hazard - /// Middle: Exponential provides the "flat bottom" of the bathtub - /// Late: Weibull(4, 150) causes the upturn in failure rate - /// - /// The three distinct hazard behaviors create sufficient structure for identification. - /// - /// - /// Note: - /// With 5 parameters and complex interactions, this is a challenging estimation - /// problem. Larger samples (n=1500+) and relaxed tolerances are appropriate. - /// - /// [TestMethod] - public void Test_MLE_MinRule_3Dist_BathtubCurve() + public void Test_MLE_CorrelatedMinRule_2Dist_Weibull_DogLeg() { - // Three-component bathtub curve using Weibulls only - // Weibull(scale, shape=1) ≡ Exponential, avoids location parameter MLE issues - var trueDist1 = new Weibull(20, 0.7); // scale=20, shape=0.7 Infant mortality (decreasing hazard) - var trueDist2 = new Weibull(200, 1.0); // scale=200, shape=1.0 Random failures (constant hazard, ≡ Exponential) - var trueDist3 = new Weibull(150, 4.0); // scale=150, shape=4.0 Wear-out (increasing hazard) - - var trueCR = new CompetingRisks(new UnivariateDistributionBase[] { trueDist1, trueDist2, trueDist3 }); - trueCR.MinimumOfRandomVariables = true; - - // Use larger sample for 3-distribution case - int n = 1500; - var sample = trueCR.GenerateRandomValues(n, RANDOM_SEED); - - Console.WriteLine($"Sample: n={n}, mean={sample.Average():F2}, min={sample.Min():F2}, max={sample.Max():F2}"); - - // Fit model - var fitDist1 = new Weibull(); - var fitDist2 = new Weibull(); - var fitDist3 = new Weibull(); - var fitCR = new CompetingRisks(new UnivariateDistributionBase[] { fitDist1, fitDist2, fitDist3 }); - fitCR.MinimumOfRandomVariables = true; - - var mleParams = fitCR.MLE(sample); - fitCR.SetParameters(mleParams); - - // Params: Weibull[scale,shape] x 3 - Console.WriteLine($"True parameters: Weibull(20, 0.7), Weibull(200, 1.0), Weibull(150, 4.0)"); - Console.WriteLine($"Fitted parameters: Weibull({mleParams[0]:F3}, {mleParams[1]:F3}), " + - $"Weibull({mleParams[2]:F3}, {mleParams[3]:F3}), Weibull({mleParams[4]:F3}, {mleParams[5]:F3})"); - - // Verify convergence (no NaN/Inf) - Assert.IsFalse(mleParams.Any(p => double.IsNaN(p) || double.IsInfinity(p)), - "All parameters should be finite"); - - // Verify the overall distribution fit (CDF comparison) - double ksStatistic = ComputeKSStatistic(sample, fitCR); - Console.WriteLine($"KS statistic: {ksStatistic:F4}"); - Assert.IsLessThan(0.05, ksStatistic, "KS statistic should indicate good fit"); - } + double[,] correlation = { { 1d, 0.6d }, { 0.6d, 1d } }; + var parent = new CompetingRisks(new UnivariateDistributionBase[] + { + new Weibull(50d, 1d), + new Weibull(80d, 3d) + }) + { + CorrelationMatrix = (double[,])correlation.Clone(), + Dependency = Probability.DependencyType.CorrelationMatrix, + MinimumOfRandomVariables = true + }; + var fitted = new CompetingRisks(new UnivariateDistributionBase[] + { + new Weibull(), + new Weibull() + }) + { + CorrelationMatrix = (double[,])correlation.Clone(), + Dependency = Probability.DependencyType.CorrelationMatrix, + MinimumOfRandomVariables = true + }; - /// - /// Tests MLE for minimum of three Weibull distributions with distinct characteristics. - /// - /// - /// - /// Configuration Rationale: - /// Three Weibulls spanning the shape parameter space: - /// - /// Weibull(k=0.5, λ=15): Strongly decreasing hazard - /// Weibull(k=1.5, λ=60): Mildly increasing hazard - /// Weibull(k=4.0, λ=120): Strongly increasing hazard - /// - /// - /// - /// Why This Is Identifiable: - /// The shapes span k < 1, 1 < k < 2, and k > 2, giving three distinct - /// hazard behaviors. The scales are chosen to create overlapping but distinguishable - /// contributions: the k=0.5 dominates the extreme left tail, k=1.5 the middle-left, - /// and k=4.0 shapes the right tail. - /// - /// - [TestMethod] - public void Test_MLE_MinRule_3Dist_ThreeWeibulls() - { - var trueDist1 = new Weibull(15, 0.5); // Strongly decreasing hazard - var trueDist2 = new Weibull(60, 1.5); // Mildly increasing hazard - var trueDist3 = new Weibull(120, 4.0); // Strongly increasing hazard - - var trueCR = new CompetingRisks(new UnivariateDistributionBase[] { trueDist1, trueDist2, trueDist3 }); - trueCR.MinimumOfRandomVariables = true; - - int n = 1500; - var sample = trueCR.GenerateRandomValues(n, RANDOM_SEED); - - // Fit model - var fitDist1 = new Weibull(); - var fitDist2 = new Weibull(); - var fitDist3 = new Weibull(); - var fitCR = new CompetingRisks(new UnivariateDistributionBase[] { fitDist1, fitDist2, fitDist3 }); - fitCR.MinimumOfRandomVariables = true; - - var mleParams = fitCR.MLE(sample); - fitCR.SetParameters(mleParams); - - Console.WriteLine($"True: Weibull(0.5, 15), Weibull(1.5, 60), Weibull(4.0, 120)"); - Console.WriteLine($"Fitted: Weibull({mleParams[0]:F2}, {mleParams[1]:F2}), " + - $"Weibull({mleParams[2]:F2}, {mleParams[3]:F2}), " + - $"Weibull({mleParams[4]:F2}, {mleParams[5]:F2})"); - - Assert.IsFalse(mleParams.Any(p => double.IsNaN(p) || double.IsInfinity(p)), - "All parameters should be finite"); - - // Verify overall fit quality - double ksStatistic = ComputeKSStatistic(sample, fitCR); - Console.WriteLine($"KS statistic: {ksStatistic:F4}"); - Assert.IsLessThan(0.05, ksStatistic, "KS statistic should indicate good fit"); + VerifyIdentifiedMleRecovery(parent, fitted, "fixed-correlation two-Weibull minimum dog leg"); } #endregion - #region Maximum Rule - 2 Distributions + #region Seed and Serialization /// - /// Tests MLE for maximum of two Normal distributions with separated means. + /// Test that reseeding invalidates the lazily built multivariate-normal and + /// empirical-CDF caches, that the seed survives cloning and the XML round-trip, and + /// that undefined dependency ordinals are rejected on deserialization. /// - /// - /// - /// Configuration Rationale: - /// Two Normals with well-separated means and different standard deviations: - /// - /// Normal(μ=50, σ=8): Lower component - /// Normal(μ=85, σ=12): Upper component with larger spread - /// - /// - /// - /// Why This Is Identifiable: - /// For the maximum of two Normals, the lower distribution primarily influences - /// the left tail of the max distribution (when both draws happen to be low), - /// while the upper distribution dominates the right tail. With a ~4σ separation - /// between means, the contributions are clearly distinguishable. - /// - /// - /// Note: - /// Normal distributions work well for the maximum rule because their symmetric, - /// bounded tails avoid the numerical issues that arise with heavy-tailed distributions. - /// - /// [TestMethod] - public void Test_MLE_MaxRule_2Dist_TwoNormals() + public void Test_PRNGSeed_InvalidatesCachesAndRoundTrips() { - double trueMu1 = 50, trueSigma1 = 8; - double trueMu2 = 85, trueSigma2 = 12; - - var trueDist1 = new Normal(trueMu1, trueSigma1); - var trueDist2 = new Normal(trueMu2, trueSigma2); - var trueCR = new CompetingRisks(new UnivariateDistributionBase[] { trueDist1, trueDist2 }); - trueCR.MinimumOfRandomVariables = false; // Maximum rule - - var sample = trueCR.GenerateRandomValues(SAMPLE_SIZE, RANDOM_SEED); - - Console.WriteLine($"Sample: n={SAMPLE_SIZE}, mean={sample.Average():F2}, min={sample.Min():F2}, max={sample.Max():F2}"); + var distribution = new CompetingRisks(new UnivariateDistributionBase[] + { + new Normal(0d, 1d), + new Exponential(2d), + }) { PRNGSeed = 2468 }; - // Fit model - var fitDist1 = new Normal(); - var fitDist2 = new Normal(); - var fitCR = new CompetingRisks(new UnivariateDistributionBase[] { fitDist1, fitDist2 }); - fitCR.MinimumOfRandomVariables = false; + FieldInfo mvnCreated = typeof(CompetingRisks).GetField("_mvnCreated", BindingFlags.Instance | BindingFlags.NonPublic)!; + FieldInfo empiricalCreated = typeof(CompetingRisks).GetField("_empiricalCDFCreated", BindingFlags.Instance | BindingFlags.NonPublic)!; + mvnCreated.SetValue(distribution, true); + empiricalCreated.SetValue(distribution, true); + distribution.PRNGSeed = 1357; + Assert.IsFalse((bool)mvnCreated.GetValue(distribution)!); + Assert.IsFalse((bool)empiricalCreated.GetValue(distribution)!); - var mleParams = fitCR.MLE(sample); - fitCR.SetParameters(mleParams); + var clone = (CompetingRisks)distribution.Clone(); + Assert.AreEqual(1357, clone.PRNGSeed); + var restored = (CompetingRisks)UnivariateDistributionFactory.CreateDistribution(distribution.ToXElement()); + Assert.AreEqual(1357, restored.PRNGSeed); - Console.WriteLine($"True: Normal({trueMu1}, {trueSigma1}), Normal({trueMu2}, {trueSigma2})"); - Console.WriteLine($"Fitted: Normal({mleParams[0]:F2}, {mleParams[1]:F2}), Normal({mleParams[2]:F2}, {mleParams[3]:F2})"); + XElement malformed = distribution.ToXElement(); + malformed.SetAttributeValue(nameof(CompetingRisks.Dependency), "999"); + Assert.Throws(() => CompetingRisks.FromXElement(malformed)); + } - // Verify no NaN - Assert.IsFalse(mleParams.Any(double.IsNaN), "No parameters should be NaN"); + #endregion - // Check overall fit quality - double ksStatistic = ComputeKSStatistic(sample, fitCR); - Console.WriteLine($"KS statistic: {ksStatistic:F4}"); - Assert.IsLessThan(0.05, ksStatistic, "KS statistic should indicate good fit"); + #region Helper Methods - // Verify means are approximately recovered (allowing label switching) - var fittedMeans = new[] { mleParams[0], mleParams[2] }.OrderBy(x => x).ToArray(); - var trueMeans = new[] { trueMu1, trueMu2 }.OrderBy(x => x).ToArray(); + /// + /// Fits one predeclared competing-risk parent and requires central-95-percent coordinate + /// recovery from the full-likelihood observed-information covariance. + /// + /// The known generating competing-risk distribution. + /// The fresh competing-risk distribution to estimate. + /// The scientific fixture label. + private static void VerifyIdentifiedMleRecovery( + CompetingRisks parent, + CompetingRisks fitted, + string label) + { + double[] sample = parent.GenerateRandomValues(RECOVERY_SAMPLE_SIZE, RECOVERY_SEED); + AssertIdentifiableRecoveryDesign(parent, sample, label); + + double[] rawParameters = fitted.MLE(sample); + Assert.IsFalse( + rawParameters.Any(parameter => !Tools.IsFinite(parameter)), + $"{label}: every fitted coordinate must be finite."); + + Matrix rawCovariance = ComputeObservedInformationCovariance( + fitted, + sample, + rawParameters, + label); + (double[] parameters, Matrix covariance) = CanonicalizeRecoveryCoordinates( + parent, + rawParameters, + rawCovariance); + double[] truth = parent.GetParameters; + var evidence = new List(truth.Length); + var standardizedErrors = new double[truth.Length]; + for (int parameterIndex = 0; parameterIndex < truth.Length; parameterIndex++) + { + double standardError = Math.Sqrt(covariance[parameterIndex, parameterIndex]); + standardizedErrors[parameterIndex] = + Math.Abs(parameters[parameterIndex] - truth[parameterIndex]) / standardError; + evidence.Add( + $"coordinate {parameterIndex + 1}: fit={parameters[parameterIndex]:G8}, " + + $"truth={truth[parameterIndex]:G8}, SE={standardError:G8}, " + + $"|z|={standardizedErrors[parameterIndex]:G6}"); + } - Assert.IsTrue(IsCloseRelative(fittedMeans[0], trueMeans[0], SCALE_TOLERANCE_PERCENT), - $"Lower mean should be close to {trueMeans[0]}"); - Assert.IsTrue(IsCloseRelative(fittedMeans[1], trueMeans[1], SCALE_TOLERANCE_PERCENT), - $"Upper mean should be close to {trueMeans[1]}"); + Assert.IsTrue( + standardizedErrors.All(error => error <= 1.96d), + $"{label}: one or more identified MLE coordinates missed the central 95% " + + $"observed-information interval. fitted logL={EvaluateLogLikelihood(fitted, sample, parameters):G12}, " + + $"parent logL={parent.LogLikelihood(sample):G12}. {string.Join("; ", evidence)}"); } /// - /// Tests MLE for maximum of Weibull and Gumbel (GEV Type I) distributions. + /// Computes a symmetric observed-information covariance from the complete competing-risk + /// sample likelihood without adding a ridge or changing production estimation. /// - /// - /// - /// Configuration Rationale: - /// Combining distributions with different tail behaviors: - /// - /// Weibull(k=2, λ=50): Light right tail (bounded support if k>1 effective tail) - /// Gumbel(μ=70, σ=15): Heavy right tail (extreme value distribution) - /// - /// - /// - /// Why This Is Identifiable: - /// For the maximum, the right tail behavior is critical. The Weibull's relatively - /// light tail means it rarely produces extreme maxima, while the Gumbel's heavy - /// tail dominates extreme values. The bulk of the distribution is shaped by both, - /// with the Gumbel's influence increasing in the upper quantiles. - /// - /// - [TestMethod] - public void Test_MLE_MaxRule_2Dist_Weibull_Gumbel() + /// The fitted distribution defining the model families and rule. + /// The generated composite observations. + /// The raw MLE coordinates. + /// The scientific fixture label. + /// The inverse observed-information covariance. + private static Matrix ComputeObservedInformationCovariance( + CompetingRisks fitted, + IList sample, + double[] parameters, + string label) { - double trueWeibullShape = 2.0; - double trueWeibullScale = 50.0; - double trueGumbelLocation = 70.0; - double trueGumbelScale = 15.0; - - var trueDist1 = new Weibull(trueWeibullScale, trueWeibullShape); - var trueDist2 = new Gumbel(trueGumbelLocation, trueGumbelScale); - var trueCR = new CompetingRisks(new UnivariateDistributionBase[] { trueDist1, trueDist2 }); - trueCR.MinimumOfRandomVariables = false; // Maximum rule - - var sample = trueCR.GenerateRandomValues(SAMPLE_SIZE, RANDOM_SEED); - - Console.WriteLine($"Sample: n={SAMPLE_SIZE}, mean={sample.Average():F2}, min={sample.Min():F2}, max={sample.Max():F2}"); - - // Fit model - var fitDist1 = new Weibull(); - var fitDist2 = new Gumbel(); - var fitCR = new CompetingRisks(new UnivariateDistributionBase[] { fitDist1, fitDist2 }); - fitCR.MinimumOfRandomVariables = false; - - var mleParams = fitCR.MLE(sample); - fitCR.SetParameters(mleParams); - - Console.WriteLine($"True: Weibull({trueWeibullShape}, {trueWeibullScale}), Gumbel({trueGumbelLocation}, {trueGumbelScale})"); - Console.WriteLine($"Fitted: Weibull({mleParams[0]:F2}, {mleParams[1]:F2}), Gumbel({mleParams[2]:F2}, {mleParams[3]:F2})"); - - Assert.IsFalse(mleParams.Any(double.IsNaN), "No parameters should be NaN"); + Tuple constraints = fitted.GetParameterConstraints(sample); + double LogLikelihood(double[] candidate) => EvaluateLogLikelihood(fitted, sample, candidate); + + Matrix rawInformation = ComputeRecoveryHessian( + LogLikelihood, + parameters, + constraints.Item2, + constraints.Item3) * -1d; + var information = new Matrix(parameters.Length, parameters.Length); + var scaledInformation = new Matrix(parameters.Length, parameters.Length); + for (int row = 0; row < parameters.Length; row++) + { + double rowScale = Math.Max(1d, Math.Abs(parameters[row])); + for (int column = 0; column < parameters.Length; column++) + { + double value = 0.5d * (rawInformation[row, column] + rawInformation[column, row]); + information[row, column] = value; + double columnScale = Math.Max(1d, Math.Abs(parameters[column])); + scaledInformation[row, column] = value * rowScale * columnScale; + } + } - // Verify overall fit - double ksStatistic = ComputeKSStatistic(sample, fitCR); - Console.WriteLine($"KS statistic: {ksStatistic:F4}"); - Assert.IsLessThan(0.05, ksStatistic, "KS statistic should indicate good fit"); + var scaledDecomposition = new SingularValueDecomposition(scaledInformation); + Assert.AreEqual( + parameters.Length, + scaledDecomposition.Rank(), + $"{label}: scale-normalized observed information is rank deficient; " + + $"inverse condition={scaledDecomposition.InverseCondition:G6}."); + Assert.IsTrue( + Tools.IsFinite(scaledDecomposition.InverseCondition) && + scaledDecomposition.InverseCondition > 0d, + $"{label}: scale-normalized observed-information condition is unusable."); + + CholeskyDecomposition cholesky; + try + { + cholesky = new CholeskyDecomposition(information); + } + catch (Exception exception) + { + Assert.Fail($"{label}: observed information is not positive definite: {exception.Message}"); + throw; + } + Matrix covariance = cholesky.InverseA(); + for (int parameterIndex = 0; parameterIndex < parameters.Length; parameterIndex++) + { + Assert.IsTrue( + Tools.IsFinite(covariance[parameterIndex, parameterIndex]) && + covariance[parameterIndex, parameterIndex] > 0d, + $"{label}: coordinate {parameterIndex + 1} covariance diagonal is invalid."); + } + return covariance; } - #endregion - - #region Maximum Rule - 3 Distributions - /// - /// Tests MLE for maximum of three Normal distributions representing a trimodal scenario. + /// Computes a bounded central-difference Hessian for the test-only uncertainty oracle. /// - /// - /// - /// Configuration Rationale: - /// Three well-separated Normals: - /// - /// Normal(μ=40, σ=6): Low component - /// Normal(μ=70, σ=8): Middle component - /// Normal(μ=100, σ=10): High component - /// - /// - /// - /// Why This Is Identifiable: - /// With ~4σ separation between adjacent means, each Normal dominates a distinct - /// region. For the maximum, the observed values come from all three components - /// but with different frequencies based on the probability that each component - /// produces the largest value. The lowest component rarely "wins" but influences - /// the extreme left tail of the maximum distribution. - /// - /// - [TestMethod] - public void Test_MLE_MaxRule_3Dist_ThreeNormals() + /// The scalar log-likelihood function. + /// The coordinate vector at which to differentiate. + /// The coordinate lower bounds. + /// The coordinate upper bounds. + /// The symmetric finite-difference Hessian. + private static Matrix ComputeRecoveryHessian( + Func function, + double[] parameters, + IReadOnlyList lowerBounds, + IReadOnlyList upperBounds) { - var trueDist1 = new Normal(40, 6); - var trueDist2 = new Normal(70, 8); - var trueDist3 = new Normal(100, 10); - - var trueCR = new CompetingRisks(new UnivariateDistributionBase[] { trueDist1, trueDist2, trueDist3 }); - trueCR.MinimumOfRandomVariables = false; // Maximum rule - - int n = 1500; - var sample = trueCR.GenerateRandomValues(n, RANDOM_SEED); - - Console.WriteLine($"Sample: n={n}, mean={sample.Average():F2}, min={sample.Min():F2}, max={sample.Max():F2}"); - - // Fit model - var fitDist1 = new Normal(); - var fitDist2 = new Normal(); - var fitDist3 = new Normal(); - var fitCR = new CompetingRisks(new UnivariateDistributionBase[] { fitDist1, fitDist2, fitDist3 }); - fitCR.MinimumOfRandomVariables = false; - - var mleParams = fitCR.MLE(sample); - fitCR.SetParameters(mleParams); - - Console.WriteLine($"True: N(40,6), N(70,8), N(100,10)"); - Console.WriteLine($"Fitted: N({mleParams[0]:F1},{mleParams[1]:F1}), " + - $"N({mleParams[2]:F1},{mleParams[3]:F1}), " + - $"N({mleParams[4]:F1},{mleParams[5]:F1})"); - - Assert.IsFalse(mleParams.Any(p => double.IsNaN(p) || double.IsInfinity(p)), - "All parameters should be finite"); - - // Verify overall fit - double ksStatistic = ComputeKSStatistic(sample, fitCR); - Console.WriteLine($"KS statistic: {ksStatistic:F4}"); - Assert.IsLessThan(0.06, ksStatistic, "KS statistic should indicate reasonable fit"); + int parameterCount = parameters.Length; + var steps = new double[parameterCount]; + for (int parameterIndex = 0; parameterIndex < parameterCount; parameterIndex++) + { + double nominal = 1E-4 * (Math.Abs(parameters[parameterIndex]) + 1d); + double leftRoom = parameters[parameterIndex] - lowerBounds[parameterIndex]; + double rightRoom = upperBounds[parameterIndex] - parameters[parameterIndex]; + steps[parameterIndex] = Math.Min(nominal, 0.25d * Math.Min(leftRoom, rightRoom)); + Assert.IsGreaterThan(0d, steps[parameterIndex], + $"Hessian step for coordinate {parameterIndex + 1} must be positive."); + } - // Note: Individual parameter recovery is not asserted for 3 same-family components - // under max-rule due to inherent identifiability limitations. The lowest component - // has minimal influence on the maximum and is difficult to recover. - // KS statistic and convergence checks above are sufficient. + var hessian = new Matrix(parameterCount, parameterCount); + double centerValue = function((double[])parameters.Clone()); + for (int row = 0; row < parameterCount; row++) + { + double[] forward = (double[])parameters.Clone(); + double[] backward = (double[])parameters.Clone(); + forward[row] += steps[row]; + backward[row] -= steps[row]; + hessian[row, row] = + (function(forward) - 2d * centerValue + function(backward)) / + (steps[row] * steps[row]); + + for (int column = row + 1; column < parameterCount; column++) + { + double[] plusPlus = (double[])parameters.Clone(); + double[] plusMinus = (double[])parameters.Clone(); + double[] minusPlus = (double[])parameters.Clone(); + double[] minusMinus = (double[])parameters.Clone(); + plusPlus[row] += steps[row]; + plusPlus[column] += steps[column]; + plusMinus[row] += steps[row]; + plusMinus[column] -= steps[column]; + minusPlus[row] -= steps[row]; + minusPlus[column] += steps[column]; + minusMinus[row] -= steps[row]; + minusMinus[column] -= steps[column]; + double mixed = + (function(plusPlus) - function(plusMinus) - + function(minusPlus) + function(minusMinus)) / + (4d * steps[row] * steps[column]); + hessian[row, column] = mixed; + hessian[column, row] = mixed; + } + } + return hessian; } /// - /// Tests MLE for maximum of Exponential, Gamma, and LogNormal - three different families. + /// Evaluates the complete sample likelihood on a clone so finite differences cannot leave + /// the fitted test object in a perturbed coordinate state. /// - /// - /// - /// Configuration Rationale: - /// Three different distribution families with distinct shapes: - /// - /// Exponential(λ=0.05): Monotonically decreasing density, mean=20 - /// Gamma(k=3, θ=15): Unimodal with mode at 30, mean=45 - /// LogNormal(μ=4.2, σ=0.4): Right-skewed, median≈67, mean≈72 - /// - /// - /// - /// Why This Is Identifiable: - /// Using three different families provides maximum structural diversity. - /// The Exponential is memoryless, the Gamma has a characteristic shape controlled - /// by its shape parameter, and the LogNormal has a distinctive heavy right tail. - /// For the maximum, these combine to create a complex but estimable distribution. - /// - /// - [TestMethod] - public void Test_MLE_MaxRule_3Dist_DifferentFamilies() + /// The fitted distribution template. + /// The observed composite sample. + /// The flattened component coordinates. + /// The complete competing-risk log likelihood. + private static double EvaluateLogLikelihood( + CompetingRisks template, + IList sample, + double[] parameters) { - var trueDist1 = new Exponential(0.05); // Mean = 20 - var trueDist2 = new GammaDistribution(3.0, 15.0); // Mean = 45 - var trueDist3 = new LogNormal(4.2, 0.4) { Base = Math.E }; // Median ≈ 67 - - var trueCR = new CompetingRisks(new UnivariateDistributionBase[] { trueDist1, trueDist2, trueDist3 }); - trueCR.MinimumOfRandomVariables = false; // Maximum rule - - int n = 1500; - var sample = trueCR.GenerateRandomValues(n, RANDOM_SEED); + var candidate = (CompetingRisks)template.Clone(); + candidate.SetParameters(parameters); + return candidate.LogLikelihood(sample); + } - Console.WriteLine($"Sample: n={n}, mean={sample.Average():F2}, min={sample.Min():F2}, max={sample.Max():F2}"); - Console.WriteLine($"True distribution means: Exp={1 / 0.05:F0}, Gamma={3 * 15:F0}, LogN≈{Math.Exp(4.2 + 0.4 * 0.4 / 2):F0}"); + /// + /// Applies the predeclared increasing-Weibull-shape label rule to a point and covariance. + /// + /// The generating distribution declaring the component families. + /// The raw flattened coordinates. + /// The raw coordinate covariance. + /// The canonically ordered coordinates and covariance. + private static (double[] Parameters, Matrix Covariance) CanonicalizeRecoveryCoordinates( + CompetingRisks parent, + double[] parameters, + Matrix covariance) + { + int[] order = GetRecoveryCoordinateOrder(parent, parameters); + var orderedParameters = new double[parameters.Length]; + var orderedCovariance = new Matrix(parameters.Length, parameters.Length); + for (int row = 0; row < parameters.Length; row++) + { + orderedParameters[row] = parameters[order[row]]; + for (int column = 0; column < parameters.Length; column++) + orderedCovariance[row, column] = covariance[order[row], order[column]]; + } + return (orderedParameters, orderedCovariance); + } - // Fit model - var fitDist1 = new Exponential(); - var fitDist2 = new GammaDistribution(); - var fitDist3 = new LogNormal(); - var fitCR = new CompetingRisks(new UnivariateDistributionBase[] { fitDist1, fitDist2, fitDist3 }); - fitCR.MinimumOfRandomVariables = false; + /// + /// Gets the scientific component-coordinate order, sorting same-family Weibulls by shape. + /// + /// The generating distribution declaring the component families. + /// The raw flattened coordinates. + /// For each canonical coordinate, the matching raw coordinate index. + private static int[] GetRecoveryCoordinateOrder( + CompetingRisks parent, + IReadOnlyList parameters) + { + if (!parent.Distributions.All(distribution => distribution is Weibull) || + parent.Distributions.Count < 2) + { + return Enumerable.Range(0, parameters.Count).ToArray(); + } - var mleParams = fitCR.MLE(sample); - fitCR.SetParameters(mleParams); + const int WeibullParameterCount = 2; + Assert.HasCount( + WeibullParameterCount * parent.Distributions.Count, + parameters); + return Enumerable.Range(0, parent.Distributions.Count) + .OrderBy(componentIndex => parameters[WeibullParameterCount * componentIndex + 1]) + .SelectMany(componentIndex => new[] + { + WeibullParameterCount * componentIndex, + WeibullParameterCount * componentIndex + 1 + }) + .ToArray(); + } - Console.WriteLine($"True: Exp(0.05), Gamma(3, 15), LogNormal(4.2, 0.4)"); - Console.WriteLine($"Fitted: Exp({mleParams[0]:F4}), Gamma({mleParams[1]:F2}, {mleParams[2]:F2}), " + - $"LogNormal({mleParams[3]:F2}, {mleParams[4]:F2})"); + /// + /// Requires balanced theoretical and realized cause shares plus visible dog-leg crossovers + /// before the Numerics MLE is called. + /// + /// The generating competing-risk distribution. + /// The production generated sample. + /// The scientific fixture label. + private static void AssertIdentifiableRecoveryDesign( + CompetingRisks parent, + IReadOnlyList sample, + string label) + { + const int probabilityCount = 1000; + int componentCount = parent.Distributions.Count; + (double[] labeledSample, int[] hardWinnerCounts) = GenerateLabeledRecoverySample(parent); + CollectionAssert.AreEqual(sample.ToArray(), labeledSample, + $"{label}: verification-only labels must reproduce production generation exactly."); + + var theoreticalShares = new double[componentCount]; + var softCounts = new double[componentCount]; + var dominanceCounts = new int[componentCount]; + var dominantSequence = new int[probabilityCount]; + foreach (double observation in sample) + { + double[] responsibilities = ComputeRecoveryResponsibilities(parent, observation, label); + for (int componentIndex = 0; componentIndex < componentCount; componentIndex++) + softCounts[componentIndex] += responsibilities[componentIndex]; + } - Assert.IsFalse(mleParams.Any(p => double.IsNaN(p) || double.IsInfinity(p)), - "All parameters should be finite"); + for (int probabilityIndex = 0; probabilityIndex < probabilityCount; probabilityIndex++) + { + double probability = (probabilityIndex + 0.5d) / probabilityCount; + double[] responsibilities = ComputeRecoveryResponsibilities( + parent, + parent.InverseCDF(probability), + label); + dominantSequence[probabilityIndex] = IndexOfLargest(responsibilities); + for (int componentIndex = 0; componentIndex < componentCount; componentIndex++) + { + theoreticalShares[componentIndex] += responsibilities[componentIndex] / probabilityCount; + if (responsibilities[componentIndex] >= 0.5d) + dominanceCounts[componentIndex]++; + } + } - // Verify overall fit - double ksStatistic = ComputeKSStatistic(sample, fitCR); - Console.WriteLine($"KS statistic: {ksStatistic:F4}"); - Assert.IsLessThan(0.06, ksStatistic, "KS statistic should indicate reasonable fit"); + double[] crossovers = FindRecoveryCrossovers(dominantSequence, probabilityCount); + double[] interiorCrossovers = crossovers + .Where(probability => probability >= 0.10d && probability <= 0.90d) + .ToArray(); + for (int componentIndex = 0; componentIndex < componentCount; componentIndex++) + { + double dominanceMass = dominanceCounts[componentIndex] / (double)probabilityCount; + Assert.IsGreaterThanOrEqualTo(0.15d, theoreticalShares[componentIndex], + $"{label}: component {componentIndex + 1} theoretical share " + + $"{theoreticalShares[componentIndex]:P2} is below 15%."); + Assert.IsGreaterThanOrEqualTo(100, hardWinnerCounts[componentIndex], + $"{label}: component {componentIndex + 1} has only " + + $"{hardWinnerCounts[componentIndex]} hard wins."); + Assert.IsGreaterThanOrEqualTo(100d, softCounts[componentIndex], + $"{label}: component {componentIndex + 1} soft event count " + + $"{softCounts[componentIndex]:F1} is below 100."); + Assert.IsGreaterThanOrEqualTo(0.10d, dominanceMass, + $"{label}: component {componentIndex + 1} owns only {dominanceMass:P2} " + + "of the composite probability scale."); + } + Assert.HasCount(componentCount - 1, interiorCrossovers, + $"{label}: expected {componentCount - 1} interior dog-leg crossovers, found " + + $"{interiorCrossovers.Length} inside [0.10, 0.90]; all crossovers are " + + $"[{string.Join(", ", crossovers.Select(value => value.ToString("F3")))}]."); + + Console.WriteLine( + $"{label}: theoretical shares [{string.Join(", ", theoreticalShares.Select(value => value.ToString("P1")))}], " + + $"hard wins [{string.Join(", ", hardWinnerCounts)}], soft counts " + + $"[{string.Join(", ", softCounts.Select(value => value.ToString("F1")))}], " + + $"crossovers [{string.Join(", ", crossovers.Select(value => value.ToString("F3")))}]."); } - #endregion - - #region Seed and Serialization - /// - /// Test that reseeding invalidates the lazily built multivariate-normal and - /// empirical-CDF caches, that the seed survives cloning and the XML round-trip, and - /// that undefined dependency ordinals are rejected on deserialization. + /// Reproduces production random-number ordering while retaining the latent winning cause. /// - [TestMethod] - public void Test_PRNGSeed_InvalidatesCachesAndRoundTrips() + /// The generating competing-risk distribution. + /// The composite sample and component hard winner counts. + private static (double[] Sample, int[] HardWinnerCounts) GenerateLabeledRecoverySample( + CompetingRisks parent) { - var distribution = new CompetingRisks(new UnivariateDistributionBase[] + int componentCount = parent.Distributions.Count; + var sample = new double[RECOVERY_SAMPLE_SIZE]; + var counts = new int[componentCount]; + if (parent.Dependency == Probability.DependencyType.Independent) { - new Normal(0d, 1d), - new Exponential(2d), - }) { PRNGSeed = 2468 }; - - FieldInfo mvnCreated = typeof(CompetingRisks).GetField("_mvnCreated", BindingFlags.Instance | BindingFlags.NonPublic)!; - FieldInfo empiricalCreated = typeof(CompetingRisks).GetField("_empiricalCDFCreated", BindingFlags.Instance | BindingFlags.NonPublic)!; - mvnCreated.SetValue(distribution, true); - empiricalCreated.SetValue(distribution, true); - distribution.PRNGSeed = 1357; - Assert.IsFalse((bool)mvnCreated.GetValue(distribution)!); - Assert.IsFalse((bool)empiricalCreated.GetValue(distribution)!); - - var clone = (CompetingRisks)distribution.Clone(); - Assert.AreEqual(1357, clone.PRNGSeed); - var restored = (CompetingRisks)UnivariateDistributionFactory.CreateDistribution(distribution.ToXElement()); - Assert.AreEqual(1357, restored.PRNGSeed); + var random = new MersenneTwister(RECOVERY_SEED); + for (int observationIndex = 0; observationIndex < RECOVERY_SAMPLE_SIZE; observationIndex++) + { + var values = new double[componentCount]; + for (int componentIndex = 0; componentIndex < componentCount; componentIndex++) + { + values[componentIndex] = parent.Distributions[componentIndex] + .InverseCDF(random.NextDouble()); + } + RecordRecoveryWinner(parent, values, sample, counts, observationIndex); + } + } + else + { + Assert.AreEqual(Probability.DependencyType.CorrelationMatrix, parent.Dependency); + var multivariateNormal = new MultivariateNormal( + new double[componentCount], + parent.CorrelationMatrix); + double[,] normals = multivariateNormal.GenerateRandomValues( + RECOVERY_SAMPLE_SIZE, + RECOVERY_SEED); + for (int observationIndex = 0; observationIndex < RECOVERY_SAMPLE_SIZE; observationIndex++) + { + var values = new double[componentCount]; + for (int componentIndex = 0; componentIndex < componentCount; componentIndex++) + { + values[componentIndex] = parent.Distributions[componentIndex].InverseCDF( + Normal.StandardCDF(normals[observationIndex, componentIndex])); + } + RecordRecoveryWinner(parent, values, sample, counts, observationIndex); + } + } + return (sample, counts); + } - XElement malformed = distribution.ToXElement(); - malformed.SetAttributeValue(nameof(CompetingRisks.Dependency), "999"); - Assert.Throws(() => CompetingRisks.FromXElement(malformed)); + /// + /// Records one generated composite observation and hard winning component. + /// + /// The generating competing-risk distribution. + /// The latent component values. + /// The composite sample under construction. + /// The hard winner counts. + /// The observation being recorded. + private static void RecordRecoveryWinner( + CompetingRisks parent, + IReadOnlyList values, + double[] sample, + int[] counts, + int observationIndex) + { + int winner = 0; + for (int componentIndex = 1; componentIndex < values.Count; componentIndex++) + { + bool replace = parent.MinimumOfRandomVariables + ? values[componentIndex] < values[winner] + : values[componentIndex] > values[winner]; + if (replace) + winner = componentIndex; + } + sample[observationIndex] = values[winner]; + counts[winner]++; } - #endregion + /// + /// Computes component cause responsibilities at one observed composite value. + /// + /// The generating competing-risk distribution. + /// The observed composite value. + /// The scientific fixture label. + /// The normalized cause responsibility vector. + private static double[] ComputeRecoveryResponsibilities( + CompetingRisks parent, + double location, + string label) + { + int componentCount = parent.Distributions.Count; + var contributions = new double[componentCount]; + if (parent.Dependency == Probability.DependencyType.Independent) + { + for (int componentIndex = 0; componentIndex < componentCount; componentIndex++) + { + double contribution = parent.Distributions[componentIndex].PDF(location); + for (int otherIndex = 0; otherIndex < componentCount; otherIndex++) + { + if (componentIndex == otherIndex) + continue; + contribution *= parent.MinimumOfRandomVariables + ? parent.Distributions[otherIndex].CCDF(location) + : parent.Distributions[otherIndex].CDF(location); + } + contributions[componentIndex] = contribution; + } + } + else + { + Assert.AreEqual(2, componentCount, + $"{label}: correlated responsibilities require two components."); + Assert.IsTrue(parent.MinimumOfRandomVariables, + $"{label}: only the approved correlated minimum is supported."); + double correlation = parent.CorrelationMatrix[0, 1]; + double conditionalScale = Math.Sqrt(1d - correlation * correlation); + double[] probabilities = parent.Distributions + .Select(distribution => Tools.Clamp( + distribution.CDF(location), + 1E-14, + 1d - 1E-14)) + .ToArray(); + double[] normals = probabilities.Select(Normal.StandardZ).ToArray(); + for (int componentIndex = 0; componentIndex < componentCount; componentIndex++) + { + int otherIndex = 1 - componentIndex; + double threshold = (normals[otherIndex] - correlation * normals[componentIndex]) / + conditionalScale; + contributions[componentIndex] = parent.Distributions[componentIndex].PDF(location) * + (1d - Normal.StandardCDF(threshold)); + } + } - #region Helper Methods + double total = contributions.Sum(); + Assert.IsTrue(Tools.IsFinite(total) && total > 0d, + $"{label}: cause contributions are invalid at x={location:G17}."); + for (int componentIndex = 0; componentIndex < componentCount; componentIndex++) + contributions[componentIndex] /= total; + return contributions; + } /// - /// Checks if two values are close within a relative tolerance. + /// Returns the first index containing the largest value. /// - private static bool IsCloseRelative(double actual, double expected, double tolerance) + /// The values to compare. + /// The largest-value index. + private static int IndexOfLargest(IReadOnlyList values) { - if (expected == 0) return Math.Abs(actual) < tolerance; - return Math.Abs(actual - expected) / Math.Abs(expected) < tolerance; + int largest = 0; + for (int index = 1; index < values.Count; index++) + { + if (values[index] > values[largest]) + largest = index; + } + return largest; } /// - /// Computes the Kolmogorov-Smirnov statistic for goodness of fit. + /// Finds midpoint-grid probabilities where the dominant cause changes. /// - private static double ComputeKSStatistic(double[] sample, CompetingRisks distribution) + /// The dominant component over the probability grid. + /// The number of grid midpoints. + /// The ordered crossover probabilities. + private static double[] FindRecoveryCrossovers( + IReadOnlyList dominantSequence, + int probabilityCount) { - var sorted = sample.OrderBy(x => x).ToArray(); - int n = sorted.Length; - double maxDiff = 0; - - for (int i = 0; i < n; i++) + var crossovers = new List(); + int previous = dominantSequence[0]; + for (int index = 1; index < dominantSequence.Count; index++) { - double empiricalCDF = (i + 1.0) / n; - double theoreticalCDF = distribution.CDF(sorted[i]); - double diff = Math.Abs(empiricalCDF - theoreticalCDF); - if (diff > maxDiff) maxDiff = diff; + if (dominantSequence[index] == previous) + continue; + crossovers.Add(index / (double)probabilityCount); + previous = dominantSequence[index]; } - - return maxDiff; + return crossovers.ToArray(); } /// diff --git a/Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs b/Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs index 7ea33eb3..7f4576df 100644 --- a/Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs +++ b/Test_Numerics/Machine Learning/Unsupervised/Test_GMM.cs @@ -1,6 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.MachineLearning; using System.Collections.Generic; +using System.Reflection; using Numerics.Mathematics.LinearAlgebra; using Numerics.Sampling; @@ -119,20 +120,15 @@ public void Test_GMM_SingularInitialComponentReportsContextualFailure() } /// - /// Verify that the M-step's symmetric positive-definite repair is actually applied to the - /// stored covariance matrices. + /// Verifies that the M-step does not ridge covariance matrices that are already positive definite. /// /// - /// MatrixRegularization.MakeSymmetricPositiveDefinite is pure — it returns a symmetrized copy - /// with a trace-scaled ridge — so the M-step must store that return value for the repair to - /// reach the covariance the next E-step's Cholesky factorization consumes. This test recomputes - /// the M-step covariance externally from the public responsibilities after a single EM iteration - /// and asserts the stored matrices carry the repair's base ridge (1E-10 of the mean diagonal) - /// exactly; a covariance stored WITHOUT the ridge would miss these assertions by exactly that - /// amount. + /// The helper returns the symmetrized input unchanged when Cholesky accepts it. This test + /// recomputes each M-step covariance externally from the public responsibilities after one EM + /// iteration and requires exact agreement after applying only the established diagonal floor. /// [TestMethod] - public void Test_GMM_MStep_PositiveDefiniteRepairIsApplied() + public void Test_GMM_MStep_WellConditionedCovarianceIsNotRidged() { var data = new double[,] { @@ -178,24 +174,55 @@ public void Test_GMM_MStep_PositiveDefiniteRepairIsApplied() expected[d, j] = sum / wgt; } } - // Apply the diagonal floor, then the repair's base ridge (the first Cholesky attempt - // succeeds for these well-separated clusters, so exactly one base ridge is added). - double trace = 0; + // Apply the established diagonal floor. No ridge is needed for these covariances. for (int d = 0; d < dims; d++) - { expected[d, d] = System.Math.Max(expected[d, d], 1E-6 * colVar[d]); - trace += expected[d, d]; - } - double baseRidge = 1e-10 * trace / dims; - for (int d = 0; d < dims; d++) - expected[d, d] += baseRidge; for (int d = 0; d < dims; d++) for (int j = 0; j < dims; j++) Assert.AreEqual(expected[d, j], gmm.Sigmas[k][d, j], 0d, - $"Sigma[{k}][{d},{j}] must carry the positive-definite repair."); + $"Sigma[{k}][{d},{j}] must preserve the un-ridged covariance."); } } + /// + /// Verifies that a ridge required by a rank-deficient M-step covariance is stored for the next E-step. + /// + /// + /// The full fixture has a usable initial covariance. The test then assigns responsibility only to + /// the first three collinear observations and invokes one M-step, producing the exactly rank-one + /// covariance [[2/3, 2/3], [2/3, 2/3]]. The returned trace-scaled ridge must be assigned to + /// ; discarding the pure helper's return value leaves the + /// stored covariance singular. + /// + [TestMethod] + public void Test_GMM_MStep_RequiredPositiveDefiniteRepairIsStored() + { + var data = new double[,] + { + { 0d, 0d }, { 1d, 1d }, { 2d, 2d }, + { 0d, 2d }, { 1d, 0d }, { 2d, 0d } + }; + var gmm = new GaussianMixtureModel(data, 1) { MaxIterations = 1 }; + gmm.Train(seed: 42); + + for (int i = 0; i < data.GetLength(0); i++) + gmm.LikelihoodMatrix[i, 0] = i < 3 ? 1d : 0d; + + MethodInfo mStep = typeof(GaussianMixtureModel).GetMethod( + "MStep", + BindingFlags.Instance | BindingFlags.NonPublic); + Assert.IsNotNull(mStep); + mStep.Invoke(gmm, null); + + double rawVariance = 2d / 3d; + double baseRidge = 1E-10d * rawVariance; + Assert.AreEqual(rawVariance + baseRidge, gmm.Sigmas[0][0, 0], 0d); + Assert.AreEqual(rawVariance + baseRidge, gmm.Sigmas[0][1, 1], 0d); + Assert.AreEqual(rawVariance, gmm.Sigmas[0][0, 1], 0d); + Assert.AreEqual(rawVariance, gmm.Sigmas[0][1, 0], 0d); + Assert.IsTrue(new CholeskyDecomposition(gmm.Sigmas[0]).IsPositiveDefinite); + } + } } diff --git a/Test_Numerics/Mathematics/Linear Algebra/Test_MatrixRegularization.cs b/Test_Numerics/Mathematics/Linear Algebra/Test_MatrixRegularization.cs index de9dd02f..d2a24ad9 100644 --- a/Test_Numerics/Mathematics/Linear Algebra/Test_MatrixRegularization.cs +++ b/Test_Numerics/Mathematics/Linear Algebra/Test_MatrixRegularization.cs @@ -16,12 +16,11 @@ namespace Mathematics.LinearAlgebra /// /// /// - /// symmetrizes its input, then adds a - /// trace-scaled ridge of 1E-10 * trace / p and retries with the ridge multiplied by ten each time - /// the factorization is rejected, up to eight attempts. Tightening the Cholesky pivot test could in - /// principle make the loop reject a matrix the absolute test accepts, escalate to a larger ridge, and - /// return a different matrix to every downstream consumer. These tests pin the returned matrix so that - /// any such escalation shows up as a failure rather than as a silent change in a fitted result. + /// symmetrizes its input and first tests + /// that un-ridged candidate. It returns the candidate unchanged when Cholesky accepts it. Only a rejected + /// candidate enters the trace-scaled ridge loop, beginning at 1E-10 * trace / p and multiplying the + /// ridge by ten after each rejection, up to eight attempts. These tests pin both the no-ridge and fallback + /// paths so that a conditioning-policy change cannot silently alter downstream fitted results. /// /// /// The loop is structurally immune to the scale-relative pivot test at any realistic dimension. For a @@ -74,13 +73,28 @@ private static Matrix FirstRidgeCandidate(Matrix M) } /// - /// Verifies that a well-conditioned symmetric matrix is returned with the base ridge only. + /// Verifies that a well-conditioned symmetric matrix is returned without a ridge. /// [TestMethod] - public void Test_MakeSymmetricPositiveDefinite_WellConditionedTakesTheBaseRidge() + public void Test_MakeSymmetricPositiveDefinite_WellConditionedReturnsWithoutRidge() { var M = new Matrix(new[,] { { 4d, 1d, 0.5d }, { 1d, 3d, 0.25d }, { 0.5d, 0.25d, 2d } }); - AssertMatricesEqual(FirstRidgeCandidate(M), MatrixRegularization.MakeSymmetricPositiveDefinite(M), 0d); + AssertMatricesEqual(M, MatrixRegularization.MakeSymmetricPositiveDefinite(M), 0d); + } + + /// + /// Verifies that a positive-definite matrix with widely separated coordinate scales is not changed. + /// + /// + /// The diagonal scales mirror real-space moment covariances. An unconditional trace-scaled ridge is + /// dominated by the largest coordinate and materially changes the smallest coordinate even though + /// each Cholesky pivot is healthy relative to its own diagonal. + /// + [TestMethod] + public void Test_MakeSymmetricPositiveDefinite_ScaleSeparatedReturnsWithoutRidge() + { + var M = new Matrix(new[,] { { 1d, 0d, 0d }, { 0d, 1E4d, 0d }, { 0d, 0d, 1E8d } }); + AssertMatricesEqual(M, MatrixRegularization.MakeSymmetricPositiveDefinite(M), 0d); } /// @@ -108,14 +122,15 @@ public void Test_MakeSymmetricPositiveDefinite_RankDeficientTakesTheBaseRidge() } /// - /// Verifies that an asymmetric input is symmetrized before the ridge is applied. + /// Verifies that an asymmetric input whose symmetric part is positive definite is only symmetrized. /// [TestMethod] public void Test_MakeSymmetricPositiveDefinite_SymmetrizesFirst() { var M = new Matrix(new[,] { { 2d, 0.8d }, { 0.2d, 2d } }); var regularized = MatrixRegularization.MakeSymmetricPositiveDefinite(M); - AssertMatricesEqual(FirstRidgeCandidate(M), regularized, 0d); + var expected = new Matrix(new[,] { { 2d, 0.5d }, { 0.5d, 2d } }); + AssertMatricesEqual(expected, regularized, 0d); Assert.AreEqual(0.5d, regularized[0, 1], 0d); Assert.AreEqual(0.5d, regularized[1, 0], 0d); } From 2b57771e2afa584b640ff8976fbb40f13143cbc9 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Tue, 1 Sep 2026 19:41:38 -0600 Subject: [PATCH 196/222] Improve Differential Evolution boundary repair --- .../Global/DifferentialEvolution.cs | 7 ++- .../Global/Test_DifferentialEvolution.cs | 46 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/Numerics/Mathematics/Optimization/Global/DifferentialEvolution.cs b/Numerics/Mathematics/Optimization/Global/DifferentialEvolution.cs index d0e682cc..662f9bdf 100644 --- a/Numerics/Mathematics/Optimization/Global/DifferentialEvolution.cs +++ b/Numerics/Mathematics/Optimization/Global/DifferentialEvolution.cs @@ -161,7 +161,12 @@ protected override void Optimize() if (rr <= CrossoverProbability || j == jRand) { u[j] = Xp[r0].Values[j] + G * (Xp[r1].Values[j] - Xp[r2].Values[j]); - u[j] = RepairParameter(u[j], LowerBounds[j], UpperBounds[j]); + // Repair halfway toward the target so infeasible trials neither accumulate exactly + // on a bound nor consume an extra random draw that shifts the seeded DE trajectory. + if (u[j] < LowerBounds[j]) + u[j] = 0.5d * Xp[i].Values[j] + 0.5d * LowerBounds[j]; + else if (u[j] > UpperBounds[j]) + u[j] = 0.5d * Xp[i].Values[j] + 0.5d * UpperBounds[j]; } else { diff --git a/Test_Numerics/Mathematics/Optimization/Global/Test_DifferentialEvolution.cs b/Test_Numerics/Mathematics/Optimization/Global/Test_DifferentialEvolution.cs index bf9cef2e..412f3288 100644 --- a/Test_Numerics/Mathematics/Optimization/Global/Test_DifferentialEvolution.cs +++ b/Test_Numerics/Mathematics/Optimization/Global/Test_DifferentialEvolution.cs @@ -318,5 +318,51 @@ public void Test_TP2() bool match2 = Math.Abs(x - validY) < 1E-4 && Math.Abs(y - validX) < 1E-4; Assert.IsTrue(match1 || match2); } + + /// + /// Verifies infeasible trial coordinates are repaired halfway toward the target instead of clamped to a boundary. + /// + /// The deterministic pseudo-random seed used for the differential-evolution run. + /// The independently derived midpoint repair for the first trial vector. + [TestMethod] + [DataRow(1, 0.8196490542613901d)] + [DataRow(2, 0.7595016034028959d)] + [DataRow(3, 0.7520687940414064d)] + [DataRow(4, 0.48277057497762144d)] + [DataRow(5, 0.8296836465888191d)] + [DataRow(12345, 0.1893519861914683d)] + public void Test_InfeasibleTrialCoordinates_AreRepairedHalfwayToTarget(int seed, double expectedFirstTrial) + { + var evaluatedCoordinates = new List(); + double Objective(double[] values) + { + evaluatedCoordinates.Add(values[0]); + double difference = values[0] - 0.5d; + return difference * difference; + } + + var solver = new DifferentialEvolution(Objective, 1, new double[] { 0d }, new double[] { 1d }) + { + PopulationSize = 4, + PRNGSeed = seed, + Mutation = 2d, + DitherRate = 0d, + CrossoverProbability = 1d, + MaxIterations = 11, + ComputeHessian = false, + ReportFailure = false + }; + + solver.Minimize(); + + Assert.IsGreaterThan(solver.PopulationSize, evaluatedCoordinates.Count); + Assert.AreEqual(expectedFirstTrial, evaluatedCoordinates[solver.PopulationSize], 1E-15); + foreach (double coordinate in evaluatedCoordinates.Skip(solver.PopulationSize)) + { + Assert.IsTrue( + coordinate > 0d && coordinate < 1d, + $"Expected an interior repaired coordinate, but evaluated {coordinate:R}."); + } + } } } From 7a3f0e43bed11f7ea585223cdff71e6ddd3e5da8 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 5 Sep 2026 13:53:58 -0600 Subject: [PATCH 197/222] Support legacy competing-risks matrix placeholders --- .../Univariate/CompetingRisks.cs | 32 ++++-- .../Univariate/Test_LegacyDistributionXml.cs | 99 +++++++++++++++++++ 2 files changed, 124 insertions(+), 7 deletions(-) diff --git a/Numerics/Distributions/Univariate/CompetingRisks.cs b/Numerics/Distributions/Univariate/CompetingRisks.cs index 65e50d31..a28b9f7e 100644 --- a/Numerics/Distributions/Univariate/CompetingRisks.cs +++ b/Numerics/Distributions/Univariate/CompetingRisks.cs @@ -1350,9 +1350,16 @@ public override XElement ToXElement() /// Creates a competing-risks distribution from its serialized representation. /// /// The element to deserialize. - /// A validated competing-risks distribution, or when the element identifies another distribution type. + /// A deserialized competing-risks distribution, or when the element identifies another distribution type. /// Thrown when is null. /// Thrown when serialized configuration, parameters, or correlation data is malformed. + /// + /// Deserialization preserves the saved dependency mode even when optional correlation data + /// is absent. Empty correlation elements and complete component-sized all-zero matrices + /// written by earlier applications are treated as an unconfigured matrix. This permits + /// legacy import without declaring the configuration numerically ready: correlation-based + /// evaluation still calls the strict matrix validator and fails until a valid matrix is set. + /// public static CompetingRisks? FromXElement(XElement xElement) { if (xElement == null) throw new ArgumentNullException(nameof(xElement)); @@ -1451,14 +1458,24 @@ public override XElement ToXElement() throw new ArgumentException("The serialized competing-risks parameters are invalid.", nameof(xElement)); var correlationElement = xElement.Element(nameof(CorrelationMatrix)); - var correlationRows = correlationElement?.Elements("Correlation_Row").ToArray() ?? Array.Empty(); - if (correlationRows.Length > 0) + if (correlationElement != null) { + var correlationRows = correlationElement.Elements("Correlation_Row").ToArray(); + bool containsUnsupportedContent = correlationElement.Elements().Count() != correlationRows.Length + || correlationRows.Any(row => row.HasElements) + || correlationElement.Nodes().OfType().Any(text => !string.IsNullOrWhiteSpace(text.Value)); + if (containsUnsupportedContent) + throw new ArgumentException("The serialized correlation matrix contains unsupported content.", nameof(xElement)); + + if (correlationRows.Length == 0) + return competingRisks; + int dimension = distributions.Length; if (correlationRows.Length != dimension) throw new ArgumentException("The serialized correlation matrix has an invalid row count.", nameof(xElement)); var correlation = new double[dimension, dimension]; + bool allZero = true; for (int i = 0; i < dimension; i++) { string[] entries = correlationRows[i].Value.Split('|'); @@ -1471,9 +1488,14 @@ public override XElement ToXElement() || correlation[i, j] < -1d || correlation[i, j] > 1d) throw new ArgumentException("The serialized correlation matrix contains an invalid value.", nameof(xElement)); + if (correlation[i, j] != 0d) + allZero = false; } } + if (allZero) + return competingRisks; + for (int i = 0; i < dimension; i++) { if (Math.Abs(correlation[i, i] - 1d) > 1E-12) @@ -1486,10 +1508,6 @@ public override XElement ToXElement() } competingRisks.CorrelationMatrix = correlation; } - else if (competingRisks.Dependency == Probability.DependencyType.CorrelationMatrix) - { - throw new ArgumentException("A correlation-matrix dependency requires serialized correlation data.", nameof(xElement)); - } return competingRisks; } diff --git a/Test_Numerics/Distributions/Univariate/Test_LegacyDistributionXml.cs b/Test_Numerics/Distributions/Univariate/Test_LegacyDistributionXml.cs index e07d0baa..daa947c4 100644 --- a/Test_Numerics/Distributions/Univariate/Test_LegacyDistributionXml.cs +++ b/Test_Numerics/Distributions/Univariate/Test_LegacyDistributionXml.cs @@ -1,5 +1,6 @@ using System.Xml.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Data; using Numerics.Data.Statistics; using Numerics.Distributions; using System; @@ -46,6 +47,11 @@ public class Test_LegacyDistributionXml "Parameters=\"10|2|14|3\">1|0.5" + "0.5|1"; + private const string CompetingRisksCompatibilityTemplate = + "{1}"; + /// /// A well-formed 2.1.4 scalar payload loads with its exact parameter values. /// @@ -111,6 +117,15 @@ public void LegacyCompetingRisksXml_WellFormed_Loads() Assert.AreEqual(0.5, risks.CorrelationMatrix[0, 1], 0d); Assert.AreEqual(10d, ((Normal)risks.Distributions[0]).Mu, 0d); Assert.AreEqual(3d, ((Normal)risks.Distributions[1]).Sigma, 0d); + + var roundTrip = (CompetingRisks)UnivariateDistributionFactory.CreateDistribution(risks.ToXElement()); + Assert.AreEqual(Probability.DependencyType.CorrelationMatrix, roundTrip.Dependency); + Assert.IsTrue(roundTrip.MinimumOfRandomVariables); + Assert.AreEqual(1d, roundTrip.CorrelationMatrix[0, 0], 0d); + Assert.AreEqual(0.5d, roundTrip.CorrelationMatrix[0, 1], 0d); + Assert.AreEqual(0.5d, roundTrip.CorrelationMatrix[1, 0], 0d); + Assert.AreEqual(1d, roundTrip.CorrelationMatrix[1, 1], 0d); + CollectionAssert.AreEqual(new[] { 10d, 2d, 14d, 3d }, roundTrip.GetParameters); } /// @@ -129,6 +144,90 @@ public void LegacyCompetingRisksXml_DamagedCorrelationMatrix_Rejected() Assert.Throws(() => UnivariateDistributionFactory.CreateDistribution(corrupted)); } + /// + /// A version-2 competing-risks payload may omit its optional correlation matrix or carry + /// the empty/all-zero placeholders written before dependency-aware matrix persistence. + /// Import preserves every other field and leaves the matrix unconfigured. + /// + /// The dependency value stored by the legacy file. + /// The legacy optional matrix representation. + [TestMethod] + [DataRow("Independent", "")] + [DataRow("Independent", "")] + [DataRow("Independent", " \r\n ")] + [DataRow("Independent", "0|00|0")] + [DataRow("CorrelationMatrix", "")] + [DataRow("CorrelationMatrix", "")] + [DataRow("CorrelationMatrix", " \r\n ")] + [DataRow("CorrelationMatrix", "0|00|0")] + public void Version2CompetingRisksXml_MissingOrPlaceholderMatrix_LoadsWithoutChangingConfiguration( + string dependency, + string matrixXml) + { + string xml = string.Format( + System.Globalization.CultureInfo.InvariantCulture, + CompetingRisksCompatibilityTemplate, + dependency, + matrixXml); + + var risks = (CompetingRisks)UnivariateDistributionFactory.CreateDistribution(XElement.Parse(xml)); + + Assert.AreEqual((Probability.DependencyType)Enum.Parse(typeof(Probability.DependencyType), dependency), risks.Dependency); + Assert.IsNull(risks.CorrelationMatrix); + Assert.AreEqual(Transform.Logarithmic, risks.XTransform); + Assert.AreEqual(Transform.NormalZ, risks.ProbabilityTransform); + Assert.IsFalse(risks.MinimumOfRandomVariables); + Assert.AreEqual(24680, risks.PRNGSeed); + Assert.AreEqual(UnivariateDistributionType.Normal, risks.Distributions[0].Type); + Assert.AreEqual(UnivariateDistributionType.Gumbel, risks.Distributions[1].Type); + CollectionAssert.AreEqual(new[] { 10d, 2d, 14d, 3d }, risks.GetParameters); + } + + /// + /// A correlation-dependent legacy import without configured correlation data remains + /// unusable for Gaussian-copula simulation until the caller supplies a valid matrix. + /// + [TestMethod] + public void Version2CompetingRisksXml_CorrelationDependencyWithoutMatrix_FailsWhenNumericallyUsed() + { + string xml = string.Format( + System.Globalization.CultureInfo.InvariantCulture, + CompetingRisksCompatibilityTemplate, + "CorrelationMatrix", + ""); + var risks = (CompetingRisks)UnivariateDistributionFactory.CreateDistribution(XElement.Parse(xml)); + + ArgumentException exception = Assert.Throws(() => risks.GenerateRandomValues(1, 12345)); + StringAssert.Contains(exception.Message, "requires a correlation matrix"); + } + + /// + /// Populated correlation matrices that are truncated, out of range, asymmetric, or lack + /// a unit diagonal remain invalid and cannot be mistaken for legacy placeholders. + /// + /// The malformed populated matrix. + [TestMethod] + [DataRow("1|0.20.2")] + [DataRow("1|1.21.2|1")] + [DataRow("1|0.20.3|1")] + [DataRow("0.9|0.20.2|1")] + [DataRow("0|0")] + [DataRow("00")] + [DataRow("1|NaNNaN|1")] + [DataRow("not a matrix")] + [DataRow("0|0")] + [DataRow("0|00|0")] + public void Version2CompetingRisksXml_PopulatedMalformedMatrix_RemainsRejected(string matrixXml) + { + string xml = string.Format( + System.Globalization.CultureInfo.InvariantCulture, + CompetingRisksCompatibilityTemplate, + "Independent", + matrixXml); + + Assert.Throws(() => UnivariateDistributionFactory.CreateDistribution(XElement.Parse(xml))); + } + /// /// Tableless empirical and kernel-density elements are rejected: the table attributes are /// required, and no legacy writer ever produced a valid tableless payload. From 43a6927941530184abd5e82e331478407aebb260 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sat, 5 Sep 2026 17:59:20 -0600 Subject: [PATCH 198/222] Pin the 2-D Gauss-Kronrod recorder's exhaustion-flush and mass contracts The recorder must flush the frozen composite rule on budget and depth exhaustion, not only on success, with weights summing to the domain area and weighted values reproducing the result on every non-throwing outcome, and report nothing when the integrand throws. Consumers adopting the recorded mass as an exhaustive partition rely on exactly these behaviors. --- .../Test_AdaptiveGaussKronrod2D.cs | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/Test_Numerics/Mathematics/Integration/Test_AdaptiveGaussKronrod2D.cs b/Test_Numerics/Mathematics/Integration/Test_AdaptiveGaussKronrod2D.cs index 9e5b042c..a2dcd13d 100644 --- a/Test_Numerics/Mathematics/Integration/Test_AdaptiveGaussKronrod2D.cs +++ b/Test_Numerics/Mathematics/Integration/Test_AdaptiveGaussKronrod2D.cs @@ -402,5 +402,90 @@ public void Test_Determinism_RepeatedRunsBitEqual() Assert.AreEqual(first.FunctionEvaluations, second.FunctionEvaluations); } + /// + /// The recorder flushes the frozen composite rule on budget and depth exhaustion, not + /// only on success: a consumer adopting the recorded mass (nodes in whole regions, + /// weights summing to the domain area, weighted values reproducing the result) can rely + /// on a complete flush for every non-throwing outcome. + /// + [TestMethod] + public void Test_Recorder_FlushesOnBudgetAndDepthExhaustion() + { + double sumW = 0, sumWF = 0; + int count = 0; + var budget = new AdaptiveGaussKronrod2D((x, y) => Integrands.GenzProductPeak(x, y, 50, 50, 0.4, 0.6), 0, 1, 0, 1) + { + MaxFunctionEvaluations = 2000, + Recorder = (x, y, w, f) => { sumW += w; sumWF += w * f; count++; } + }; + budget.Integrate(); + Assert.AreEqual(IntegrationStatus.MaximumFunctionEvaluationsReached, budget.Status); + Assert.IsGreaterThanOrEqualTo(441, count); + Assert.AreEqual(0, count % 441); + Assert.AreEqual(1d, sumW, 1E-12); + Assert.AreEqual(budget.Result, sumWF, 1E-12 * Math.Abs(budget.Result)); + + sumW = 0; sumWF = 0; count = 0; + var depth = new AdaptiveGaussKronrod2D((x, y) => Integrands.GenzGaussian(x, y, 10, 10, 0.4, 0.6), 0, 1, 0, 1) + { + MaxDepth = 0, + Recorder = (x, y, w, f) => { sumW += w; sumWF += w * f; count++; } + }; + depth.Integrate(); + Assert.AreEqual(IntegrationStatus.MaximumIterationsReached, depth.Status); + Assert.AreEqual(441, count); + Assert.AreEqual(1d, sumW, 1E-12); + Assert.AreEqual(depth.Result, sumWF, 1E-12 * Math.Abs(depth.Result)); + } + + /// + /// The recorded weights sum to the domain area on every non-Failure status — success, + /// budget exhaustion, and depth exhaustion — on a non-unit-area domain, and a throwing + /// integrand reports nothing at all. + /// + [TestMethod] + public void Test_Recorder_MassCompleteOnEveryNonFailureStatus() + { + const double Area = 2d * 3d; + double sumW = 0; + var success = new AdaptiveGaussKronrod2D((x, y) => x * y, 0, 2, 0, 3) + { + Recorder = (x, y, w, f) => sumW += w + }; + success.Integrate(); + Assert.AreEqual(IntegrationStatus.Success, success.Status); + Assert.AreEqual(Area, sumW, 1E-12 * Area); + + sumW = 0; + var budget = new AdaptiveGaussKronrod2D((x, y) => Integrands.GenzProductPeak(x / 2d, y / 3d, 50, 50, 0.4, 0.6), 0, 2, 0, 3) + { + MaxFunctionEvaluations = 2000, + Recorder = (x, y, w, f) => sumW += w + }; + budget.Integrate(); + Assert.AreEqual(IntegrationStatus.MaximumFunctionEvaluationsReached, budget.Status); + Assert.AreEqual(Area, sumW, 1E-12 * Area); + + sumW = 0; + var depth = new AdaptiveGaussKronrod2D((x, y) => Integrands.GenzGaussian(x / 2d, y / 3d, 10, 10, 0.4, 0.6), 0, 2, 0, 3) + { + MaxDepth = 0, + Recorder = (x, y, w, f) => sumW += w + }; + depth.Integrate(); + Assert.AreEqual(IntegrationStatus.MaximumIterationsReached, depth.Status); + Assert.AreEqual(Area, sumW, 1E-12 * Area); + + int failureCount = 0; + var failing = new AdaptiveGaussKronrod2D((x, y) => throw new InvalidOperationException("Integrand failure."), 0, 2, 0, 3) + { + ReportFailure = false, + Recorder = (x, y, w, f) => failureCount++ + }; + failing.Integrate(); + Assert.AreEqual(IntegrationStatus.Failure, failing.Status); + Assert.AreEqual(0, failureCount); + } + } } From 94d1713e03a4c416dd472d6fbef0ce907881e9d1 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Sun, 6 Sep 2026 05:44:55 -0600 Subject: [PATCH 199/222] Fix the AdaptiveGaussKronrod source file name spelling --- .../{AdaptiveGuassKronrod.cs => AdaptiveGaussKronrod.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Numerics/Mathematics/Integration/{AdaptiveGuassKronrod.cs => AdaptiveGaussKronrod.cs} (100%) diff --git a/Numerics/Mathematics/Integration/AdaptiveGuassKronrod.cs b/Numerics/Mathematics/Integration/AdaptiveGaussKronrod.cs similarity index 100% rename from Numerics/Mathematics/Integration/AdaptiveGuassKronrod.cs rename to Numerics/Mathematics/Integration/AdaptiveGaussKronrod.cs From c0d67b9c52d62dc49bdbfb744ec47b9a205cc86b Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Tue, 8 Sep 2026 11:26:22 -0600 Subject: [PATCH 200/222] Repair KappaFour numerical evaluation and fitting Stabilize probabilities, support, L-moments, moments, modes, and quantile derivatives. Reject unsuccessful MLE results and add independent oracle regressions. Release build has zero warnings and errors; all 32 K4 tests pass on each target framework. Haden approved committing with the documented unrelated BOM HTTP 500 test failure. --- .../Distributions/Univariate/KappaFour.cs | 751 ++++++-- .../Univariate/KappaFourBoundary.cs | 236 +++ .../Fixtures/generate_kappa_four_oracle.py | 76 + .../Fixtures/kappa-four-boundaries.csv | 46 + .../Fixtures/kappa-four-probabilities.csv | 1547 +++++++++++++++++ .../Univariate/Test_KappaFourRegression.cs | 338 ++++ Test_Numerics/Test_Numerics.csproj | 5 + docs/distributions/kappa-four-repair.md | 96 + 8 files changed, 2924 insertions(+), 171 deletions(-) create mode 100644 Numerics/Distributions/Univariate/KappaFourBoundary.cs create mode 100644 Test_Numerics/Distributions/Univariate/Fixtures/generate_kappa_four_oracle.py create mode 100644 Test_Numerics/Distributions/Univariate/Fixtures/kappa-four-boundaries.csv create mode 100644 Test_Numerics/Distributions/Univariate/Fixtures/kappa-four-probabilities.csv create mode 100644 Test_Numerics/Distributions/Univariate/Test_KappaFourRegression.cs create mode 100644 docs/distributions/kappa-four-repair.md diff --git a/Numerics/Distributions/Univariate/KappaFour.cs b/Numerics/Distributions/Univariate/KappaFour.cs index 59b8a1b8..e54b6f5d 100644 --- a/Numerics/Distributions/Univariate/KappaFour.cs +++ b/Numerics/Distributions/Univariate/KappaFour.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using Numerics.Data.Statistics; +using Numerics.Mathematics; using Numerics.Mathematics.LinearAlgebra; +using Numerics.Mathematics.Integration; using Numerics.Mathematics.Optimization; using Numerics.Mathematics.SpecialFunctions; @@ -31,6 +33,17 @@ namespace Numerics.Distributions /// /// /// + /// Finite shapes and a positive finite scale define the distribution. Moment existence and + /// estimation restrictions are separate: an absolute moment of order r requires rκ > -1, + /// and additionally rκh > -1 when h < 0. Moment properties use standardized adaptive + /// quantile integration with relative tolerance 1e-10 and absolute tolerance 1e-12, returning + /// NaN when the required moment does not exist or quadrature fails. + /// + /// + /// The unrestricted likelihood can be unbounded at a sample-dependent support endpoint. + /// A successful numerical fit does not establish the existence of a finite global maximum. + /// + /// /// References: /// /// @@ -192,7 +205,7 @@ public override double Mean { if (!_momentsComputed) { - u = CentralMoments(1000); + u = ComputeMoments(); _momentsComputed = true; } return u[0]; @@ -206,13 +219,28 @@ public override double Median } /// + /// Returns the unique density mode, including one-sided endpoint maxima, or NaN when no unique mode exists. public override double Mode { get { - var brent = new BrentSearch(PDF, InverseCDF(0.001), InverseCDF(0.999)); - brent.Maximize(); - return brent.BestParameterSet.Values[0]; + EnsureValidParameters(); + double lower = Minimum, upper = Maximum; + double lowerDensity = Tools.IsFinite(lower) ? LowerEndpointDensity() : 0d; + double upperDensity = Tools.IsFinite(upper) ? PDF(upper) : 0d; + if (double.IsPositiveInfinity(lowerDensity) && double.IsPositiveInfinity(upperDensity)) return double.NaN; + if (double.IsPositiveInfinity(lowerDensity)) return lower; + if (double.IsPositiveInfinity(upperDensity)) return upper; + double denominator = 1d - Kappa * Hondo; + double t = (1d - Kappa) / denominator; + if (denominator > 0d && t > 0d && (Hondo <= 0d || Hondo * t < 1d)) + { + double logT = Tools.Log1p(-Kappa) - Tools.Log1p(-Kappa * Hondo); + return AffineQuantile(Xi, Alpha, QuantileFromLogT(logT, Kappa)); + } + if (lowerDensity > upperDensity) return lower; + if (upperDensity > lowerDensity) return upper; + return double.NaN; } } @@ -223,7 +251,7 @@ public override double StandardDeviation { if (!_momentsComputed) { - u = CentralMoments(1000); + u = ComputeMoments(); _momentsComputed = true; } return u[1]; @@ -237,7 +265,7 @@ public override double Skewness { if (!_momentsComputed) { - u = CentralMoments(1000); + u = ComputeMoments(); _momentsComputed = true; } return u[2]; @@ -251,7 +279,7 @@ public override double Kurtosis { if (!_momentsComputed) { - u = CentralMoments(1000); + u = ComputeMoments(); _momentsComputed = true; } return u[3]; @@ -265,15 +293,16 @@ public override double Minimum { if (Hondo <= 0d && Kappa < 0d) { - return Xi + Alpha / Kappa; + return LocationPlusScaleOverShape(); } else if (Hondo > 0d && Kappa != 0d) { - return Xi + Alpha / Kappa * (1d - Math.Pow(Hondo, -Kappa)); + double logH = Math.Log(Hondo); + return AffineQuantile(Xi, Alpha, QuantileFromLogT(-logH, Kappa)); } else if (Hondo > 0d && Kappa == 0d) { - return Xi + Alpha * Math.Log(Hondo); + return AffineQuantile(Xi, Alpha, Math.Log(Hondo)); } else if (Hondo <= 0d && Kappa >= 0d) { @@ -294,7 +323,7 @@ public override double Maximum } else { - return Xi + Alpha / Kappa; + return LocationPlusScaleOverShape(); } } } @@ -312,8 +341,11 @@ public override double[] MaximumOfParameters } /// + /// Parameters are installed only after estimation returns successfully. + /// The selected estimator cannot produce a successful valid fit. public void Estimate(IList sample, ParameterEstimationMethod estimationMethod) { + ValidateFittingSample(sample); if (estimationMethod == ParameterEstimationMethod.MethodOfLinearMoments) { SetParameters(ParametersFromLinearMoments(Statistics.LinearMoments(sample))); @@ -329,8 +361,11 @@ public void Estimate(IList sample, ParameterEstimationMethod estimationM } /// + /// Uses the existing seeded random generator and propagates estimation failure without returning a fitted distribution. + /// The resampled data cannot be fitted successfully. public IUnivariateDistribution Bootstrap(ParameterEstimationMethod estimationMethod, int sampleSize, int seed = -1) { + if (sampleSize < 4) throw new ArgumentOutOfRangeException(nameof(sampleSize), "At least four observations are required for Kappa Four estimation."); var newDistribution = new KappaFour(Xi, Alpha, Kappa, Hondo); var sample = newDistribution.GenerateRandomValues(sampleSize, seed); newDistribution.Estimate(sample, estimationMethod); @@ -378,7 +413,21 @@ public override void SetParameters(IList parameters) return null!; } - /// + /// + /// Estimates ξ, α, κ, and h from the first two L-moments and the L-skewness and L-kurtosis. + /// + /// The values L1, L2, τ3, and τ4, in that order. + /// The fitted location, scale, kappa, and hondo parameters. + /// The L-moments are null. + /// There are not four L-moments, or their ratios are outside the estimation region. + /// An L-moment is nonfinite. + /// The Newton iteration fails, or the resulting location and scale are not finite and valid. + /// + /// Uses Hosking's Newton iteration with tolerance 1e-6, at most 20 iterations, and at most ten + /// step reductions. This estimator retains its h > -1 estimation region. Continuous + /// quantile limits and their derivatives are integrated when gamma-ratio differences would + /// lose accuracy near a zero shape; the actual shape values are retained. + /// public double[] ParametersFromLinearMoments(IList moments) { // This routine is taken and converted directly from Fortran code @@ -397,13 +446,18 @@ public double[] ParametersFromLinearMoments(IList moments) //* * //*********************************************************************** + if (moments is null) throw new ArgumentNullException(nameof(moments)); + if (moments.Count != 4) throw new ArgumentException("Exactly four L-moments are required.", nameof(moments)); + for (int index = 0; index < 4; index++) + if (!Tools.IsFinite(moments[index])) + throw new ArgumentOutOfRangeException(nameof(moments), "L-moments must be finite."); + double L1 = moments[0]; double L2 = moments[1]; double T3 = moments[2]; double T4 = moments[3]; double eps = 1E-6; int maxit = 20, maxsr = 10; - double xi = 0, alpha = 0, kappa = 0, hondo = 0; // TEST FOR FEASIBILITY @@ -435,7 +489,8 @@ public double[] ParametersFromLinearMoments(IList moments) // START OF NEWTON-RAPHSON ITERATION - for (int i = 1; i < maxit; i++) + bool converged = false; + for (int i = 1; i <= maxit; i++) { // REDUCE STEPLENGTH UNTIL WE ARE NEARER TO THE REQUIRED @@ -443,26 +498,38 @@ public double[] ParametersFromLinearMoments(IList moments) for (int j = 1; j <= maxsr; j++) { - if (G > 53d) throw new Exception("Iteration encountered numerical difficulties - overflow would have been likely to occur."); + if (!Tools.IsFinite(G) || !Tools.IsFinite(H) || G > 53d) + throw new InvalidOperationException("L-moment iteration encountered a nonfinite shape or an unsupported gamma calculation."); - if (H <= 0) + if (KappaLinearMomentsNeedIntegration(G, H)) { - U1 = Math.Exp(Gamma.LogGamma(-1d / H - G) - Gamma.LogGamma(-1d / H + 1d)); - U2 = Math.Exp(Gamma.LogGamma(-2d / H - G) - Gamma.LogGamma(-2d / H + 1d)); - U3 = Math.Exp(Gamma.LogGamma(-3d / H - G) - Gamma.LogGamma(-3d / H + 1d)); - U4 = Math.Exp(Gamma.LogGamma(-4d / H - G) - Gamma.LogGamma(-4d / H + 1d)); + double[] linearMoments = KappaStandardLinearMoments(G, H); + ALAM2 = linearMoments[1]; + ALAM3 = linearMoments[2]; + ALAM4 = linearMoments[3]; } else { - U1 = Math.Exp(Gamma.LogGamma(1d / H) - Gamma.LogGamma(1d / H + 1d + G)); - U2 = Math.Exp(Gamma.LogGamma(2d / H) - Gamma.LogGamma(2d / H + 1d + G)); - U3 = Math.Exp(Gamma.LogGamma(3d / H) - Gamma.LogGamma(3d / H + 1d + G)); - U4 = Math.Exp(Gamma.LogGamma(4d / H) - Gamma.LogGamma(4d / H + 1d + G)); + if (H < 0) + { + U1 = Math.Exp(Gamma.LogGamma(-1d / H - G) - Gamma.LogGamma(-1d / H + 1d)); + U2 = Math.Exp(Gamma.LogGamma(-2d / H - G) - Gamma.LogGamma(-2d / H + 1d)); + U3 = Math.Exp(Gamma.LogGamma(-3d / H - G) - Gamma.LogGamma(-3d / H + 1d)); + U4 = Math.Exp(Gamma.LogGamma(-4d / H - G) - Gamma.LogGamma(-4d / H + 1d)); + } + else + { + U1 = Math.Exp(Gamma.LogGamma(1d / H) - Gamma.LogGamma(1d / H + 1d + G)); + U2 = Math.Exp(Gamma.LogGamma(2d / H) - Gamma.LogGamma(2d / H + 1d + G)); + U3 = Math.Exp(Gamma.LogGamma(3d / H) - Gamma.LogGamma(3d / H + 1d + G)); + U4 = Math.Exp(Gamma.LogGamma(4d / H) - Gamma.LogGamma(4d / H + 1d + G)); + } + ALAM2 = U1 - 2d * U2; + ALAM3 = -U1 + 6d * U2 - 6d * U3; + ALAM4 = U1 - 12d * U2 + 30d * U3 - 20d * U4; } - ALAM2 = U1 - 2d * U2; - ALAM3 = -U1 + 6d * U2 - 6d * U3; - ALAM4 = U1 - 12d * U2 + 30d * U3 - 20d * U4; - if (ALAM2 == 0d) throw new Exception("Iteration encountered numerical difficulties - overflow would have been likely to occur."); + if (ALAM2 == 0d || !Tools.IsFinite(ALAM2) || !Tools.IsFinite(ALAM3) || !Tools.IsFinite(ALAM4)) + throw new InvalidOperationException("L-moment iteration could not evaluate finite L-moment ratios."); TAU3 = ALAM3 / ALAM2; TAU4 = ALAM4 / ALAM2; E1 = TAU3 - T3; @@ -477,13 +544,19 @@ public double[] ParametersFromLinearMoments(IList moments) DEL2 *= 0.5; G = XG - DEL1; H = XH - DEL2; + Z = G + H * 0.725d; // TOO MANY STEPLENGTH REDUCTIONS - if (j == maxsr) throw new Exception("Iteration encountered numerical difficulties - overflow would have been likely to occur."); + if (j == maxsr) throw new InvalidOperationException("L-moment iteration failed after ten step reductions."); } // TEST FOR CONVERGENCE - if (DIST < eps) break; + if (DIST < eps) + { + converged = true; + break; + } + if (i == maxit) break; // NOT CONVERGED: CALCULATE NEXT STEP // NOTATION: @@ -497,42 +570,56 @@ public double[] ParametersFromLinearMoments(IList moments) XH = H; XZ = Z; XDIST = DIST; - RHH = 1d / (H * H); - - if (H > 0) + if (KappaLinearMomentsNeedIntegration(G, H)) { - U1G = -U1 * Gamma.Digamma(1d / H + 1d + G); - U2G = -U2 * Gamma.Digamma(2d / H + 1d + G); - U3G = -U3 * Gamma.Digamma(3d / H + 1d + G); - U4G = -U4 * Gamma.Digamma(4d / H + 1d + G); - U1H = RHH * (-U1G - U1 * Gamma.Digamma(1d / H)); - U2H = 2d * RHH * (-U2G - U2 * Gamma.Digamma(2d / H)); - U3H = 3d * RHH * (-U3G - U3 * Gamma.Digamma(3d / H)); - U4H = 4d * RHH * (-U4G - U4 * Gamma.Digamma(4d / H)); + DL2G = KappaLinearMomentDerivative(G, H, 1, 2); + DL2H = KappaLinearMomentDerivative(G, H, 1, 3); + DL3G = KappaLinearMomentDerivative(G, H, 2, 2); + DL3H = KappaLinearMomentDerivative(G, H, 2, 3); + DL4G = KappaLinearMomentDerivative(G, H, 3, 2); + DL4H = KappaLinearMomentDerivative(G, H, 3, 3); } else { - U1G = -U1 * Gamma.Digamma(-1d / H - G); - U2G = -U2 * Gamma.Digamma(-2d / H - G); - U3G = -U3 * Gamma.Digamma(-3d / H - G); - U4G = -U4 * Gamma.Digamma(-4d/ H - G); - U1H = RHH * (-U1G - U1 * Gamma.Digamma(-1d / H + 1d)); - U2H = 2d * RHH * (-U2G - U2 * Gamma.Digamma(-2d / H + 1d)); - U3H = 3d * RHH * (-U3G - U3 * Gamma.Digamma(-3d / H + 1d)); - U4H = 4d * RHH * (-U4G - U4 * Gamma.Digamma(-4d / H + 1d)); - } + RHH = 1d / (H * H); - DL2G = U1G - 2d * U2G; - DL2H = U1H - 2d * U2H; - DL3G = -U1G + 6d * U2G - 6d * U3G; - DL3H = -U1H + 6d * U2H - 6d * U3H; - DL4G = U1G - 12d * U2G + 30d * U3G - 20d * U4G; - DL4H = U1H - 12d * U2H + 30d * U3H - 20d * U4H; + if (H > 0) + { + U1G = -U1 * Gamma.Digamma(1d / H + 1d + G); + U2G = -U2 * Gamma.Digamma(2d / H + 1d + G); + U3G = -U3 * Gamma.Digamma(3d / H + 1d + G); + U4G = -U4 * Gamma.Digamma(4d / H + 1d + G); + U1H = RHH * (-U1G - U1 * Gamma.Digamma(1d / H)); + U2H = 2d * RHH * (-U2G - U2 * Gamma.Digamma(2d / H)); + U3H = 3d * RHH * (-U3G - U3 * Gamma.Digamma(3d / H)); + U4H = 4d * RHH * (-U4G - U4 * Gamma.Digamma(4d / H)); + } + else + { + U1G = -U1 * Gamma.Digamma(-1d / H - G); + U2G = -U2 * Gamma.Digamma(-2d / H - G); + U3G = -U3 * Gamma.Digamma(-3d / H - G); + U4G = -U4 * Gamma.Digamma(-4d / H - G); + U1H = RHH * (-U1G - U1 * Gamma.Digamma(-1d / H + 1d)); + U2H = 2d * RHH * (-U2G - U2 * Gamma.Digamma(-2d / H + 1d)); + U3H = 3d * RHH * (-U3G - U3 * Gamma.Digamma(-3d / H + 1d)); + U4H = 4d * RHH * (-U4G - U4 * Gamma.Digamma(-4d / H + 1d)); + } + + DL2G = U1G - 2d * U2G; + DL2H = U1H - 2d * U2H; + DL3G = -U1G + 6d * U2G - 6d * U3G; + DL3H = -U1H + 6d * U2H - 6d * U3H; + DL4G = U1G - 12d * U2G + 30d * U3G - 20d * U4G; + DL4H = U1H - 12d * U2H + 30d * U3H - 20d * U4H; + } D11 = (DL3G - TAU3 * DL2G) / ALAM2; D12 = (DL3H - TAU3 * DL2H) / ALAM2; D21 = (DL4G - TAU4 * DL2G) / ALAM2; D22 = (DL4H - TAU4 * DL2H) / ALAM2; DET = D11 * D22 - D12 * D21; + if (DET == 0d || !Tools.IsFinite(DET)) + throw new InvalidOperationException("The L-moment derivative matrix is singular or nonfinite."); H11 = D22 / DET; H12 = -D12 / DET; H21 = -D21 / DET; @@ -561,80 +648,147 @@ public double[] ParametersFromLinearMoments(IList moments) Z = G + H * 0.725; } - // NOT CONVERGED - if (i == maxit) throw new Exception("Iterations failed to converge."); } - hondo = H; - kappa = G; - var TEMP = Gamma.LogGamma(1d + G); - if (TEMP > 170d) throw new Exception("Iteration for hondo and kappa converged, but overflow would have occurred when calculating xi and alpha."); - var GAM = Math.Exp(TEMP); - TEMP = (1d + G) * Math.Log(Math.Abs(H)); - if (TEMP > 170d) throw new Exception("Iteration for hondo and kappa converged, but overflow would have occurred when calculating xi and alpha."); - var HH = Math.Exp(TEMP); - alpha = L2 * G * HH / (ALAM2 * GAM); - xi = L1 - alpha / G * (1d - GAM * U1 / HH); + if (!converged) + throw new InvalidOperationException("L-moment iterations failed to converge after 20 iterations."); - return [xi, alpha, kappa, hondo]; + // Reevaluate at the accepted shapes: step reductions must not leave stale scale factors. + double[] standardized = KappaStandardLinearMoments(G, H); + double alpha = L2 / standardized[1]; + double xi = L1 - alpha * standardized[0]; + if (!Tools.IsFinite(xi) || !Tools.IsFinite(alpha) || alpha <= 0d) + throw new InvalidOperationException("L-moment shapes converged, but finite valid location and scale could not be recovered."); + return [xi, alpha, G, H]; } - /// + /// + /// Calculates the first two L-moments, L-skewness, and L-kurtosis for the given parameters. + /// + /// The location, scale, kappa, and hondo parameters. + /// L1, L2, τ3, and τ4, in that order. + /// The parameters are null. + /// There are not four parameters. + /// Parameters are invalid or the first absolute moment does not exist. + /// Finite L-moments could not be evaluated numerically. + /// + /// L-moments require κ > -1 and, when h < 0, κh > -1. Gamma products are + /// evaluated as log-gamma ratios. Near zero shapes, adaptive quantile integration avoids + /// cancellation without modifying the parameters; the exact Gumbel limit is explicit. + /// public double[] LinearMomentsFromParameters(IList parameters) { - double xi = parameters[0]; - double alpha = parameters[1]; - double kappa = parameters[2]; - double hondo = parameters[3]; - if (((kappa < -1) && (hondo >= 0)) || ((hondo < 0) && ((kappa <= -1) || (kappa >= -1 / hondo)))) - { - throw new ArgumentOutOfRangeException(nameof(parameters), "L-moments can only be defined for hondo (h) >= 0 and kappa (k) > -1, or if h < 0 and -1 < k < -1/h."); - } + if (parameters is null) throw new ArgumentNullException(nameof(parameters)); + if (parameters.Count != 4) throw new ArgumentException("Exactly four parameters are required.", nameof(parameters)); + ValidateParameters(parameters, true); + double kappa = parameters[2], hondo = parameters[3]; + if (kappa <= -1d || (hondo < 0d && kappa >= -1d / hondo)) + throw new ArgumentOutOfRangeException(nameof(parameters), "L-moments require kappa > -1 and, for hondo < 0, kappa*hondo > -1."); + + double[] standardized = KappaStandardLinearMoments(kappa, hondo); + if (!Tools.IsFinite(standardized[0]) || !Tools.IsFinite(standardized[1]) || standardized[1] <= 0d || + !Tools.IsFinite(standardized[2]) || !Tools.IsFinite(standardized[3])) + throw new InvalidOperationException("Finite standardized L-moments could not be evaluated."); + double l1 = parameters[0] + parameters[1] * standardized[0]; + double l2 = parameters[1] * standardized[1]; + if (!Tools.IsFinite(l1) || !Tools.IsFinite(l2) || l2 <= 0d) + throw new InvalidOperationException("The L-moments are outside the finite numerical range."); + return [l1, l2, standardized[2] / standardized[1], standardized[3] / standardized[1]]; + } + + /// + /// Identifies shape neighborhoods where gamma-ratio differences or their derivatives + /// cancel; this selects an equivalent numerical representation, not a limiting shape. + /// + private static bool KappaLinearMomentsNeedIntegration(double kappa, double hondo) + { + return Math.Abs(kappa) < 0.001d || Math.Abs(hondo) < 0.001d; + } - double L1; - double L2; - double T3; - double T4; - if (kappa == 0.0d) kappa = Math.Pow(10d, -100); - if (hondo == 0.0d) + /// + /// Evaluates standardized L1 through L4 using log-gamma probability-weighted moments + /// or full-interval adaptive integration of the actual quantile near zero shapes. + /// + private static double[] KappaStandardLinearMoments(double kappa, double hondo) + { + if (kappa == 0d && hondo == 0d) { - L1 = xi + alpha * (1.0d - Gamma.Function(1.0d + kappa)) / kappa; - L2 = alpha * (1.0d - Math.Pow(2.0d, -kappa)) * Gamma.Function(1.0d + kappa) / kappa; - T3 = 2.0d * (1.0d - Math.Pow(3.0d, -kappa)) / (1.0d - Math.Pow(2.0d, -kappa)) - 3.0d; - T4 = (5.0d * (1.0d - Math.Pow(4.0d, -kappa)) - 10.0d * (1.0d - Math.Pow(3.0d, -kappa)) + 6.0d * (1.0d - Math.Pow(2.0d, -kappa))) / (1.0d - Math.Pow(2.0d, -kappa)); + double log2 = Math.Log(2d), log3 = Math.Log(3d); + return [0.577215664901532860606512090082402431d, log2, 2d * log3 - 3d * log2, 16d * log2 - 10d * log3]; } - else + + // At h=0 the gamma ratio has an exact simpler form; k=0 is evaluated by + // the continuous quantile itself instead of a small artificial replacement. + if (Math.Abs(kappa) < 0.001d || (hondo != 0d && Math.Abs(hondo) < 0.001d)) { - var g = new double[4]; - for (int r = 1; r <= 4; r++) + var integrated = new double[4]; + for (int order = 0; order < 4; order++) { - if (hondo > 0.0d) - { - g[r - 1] = r * Gamma.Function(1d + kappa) * Gamma.Function(r / hondo) / (Math.Pow(hondo, 1d + kappa) * Gamma.Function(1d + kappa + r / hondo)); - } - else - { - g[r - 1] = r * Gamma.Function(1d + kappa) * Gamma.Function(-kappa - r / hondo) / (Math.Pow(-hondo, 1d + kappa) * Gamma.Function(1d - r / hondo)); - } + int polynomialOrder = order; + integrated[order] = IntegrateProbability(logProbability => + StandardQuantile(logProbability, kappa, hondo) * KappaLinearMomentPolynomial(logProbability, polynomialOrder)); } + return integrated; + } - L1 = xi + alpha * (1d - g[0]) / kappa; - L2 = alpha * (g[0] - g[1]) / kappa; - T3 = (-g[0] + 3d * g[1] - 2d * g[2]) / (g[0] - g[1]); - T4 = -(-g[0] + 6d * g[1] - 10d * g[2] + 5d * g[3]) / (g[0] - g[1]); + var beta = new double[4]; + double logGamma = Gamma.LogGamma(1d + kappa); + for (int r = 1; r <= 4; r++) + { + double logG; + if (hondo == 0d) + logG = logGamma - kappa * Math.Log(r); + else if (hondo > 0d) + logG = Math.Log(r) + logGamma + Gamma.LogGamma(r / hondo) - + (1d + kappa) * Math.Log(hondo) - Gamma.LogGamma(1d + kappa + r / hondo); + else + logG = Math.Log(r) + logGamma + Gamma.LogGamma(-kappa - r / hondo) - + (1d + kappa) * Math.Log(-hondo) - Gamma.LogGamma(1d - r / hondo); + beta[r - 1] = -Tools.Expm1(logG) / (kappa * r); } - // - return [L1, L2, T3, T4]; + return [beta[0], 2d * beta[1] - beta[0], + 6d * beta[2] - 6d * beta[1] + beta[0], + 20d * beta[3] - 30d * beta[2] + 12d * beta[1] - beta[0]]; + } + + /// + /// Integrates a continuous shape derivative against the shifted Legendre polynomial + /// needed by Hosking's Newton derivative matrix. + /// + private static double KappaLinearMomentDerivative(double kappa, double hondo, int order, int component) + { + return IntegrateProbability(logProbability => + StandardQuantileGradient(logProbability, kappa, hondo)[component] * KappaLinearMomentPolynomial(logProbability, order)); + } + + /// Evaluates the first four shifted Legendre polynomials on the probability interval. + private static double KappaLinearMomentPolynomial(double logProbability, int order) + { + double probability = Math.Exp(logProbability); + if (order == 0) return 1d; + if (order == 1) return 2d * probability - 1d; + if (order == 2) return (6d * probability - 6d) * probability + 1d; + return ((20d * probability - 30d) * probability + 12d) * probability - 1d; } /// + /// + /// Retains the existing L-moment bounds. An initializer must have finite sample likelihood; + /// the existing GEV initializer is tried within those bounds when the Kappa initializer fails. + /// If L-moment construction fails before bounds are available, the existing GEV bounds are used. + /// + /// The sample has fewer than four observations or contains a nonfinite value. + /// Neither initializer has finite likelihood inside the fitting bounds. public Tuple GetParameterConstraints(IList sample) { + ValidateFittingSample(sample); var initialVals = new double[NumberOfParameters]; var lowerVals = new double[NumberOfParameters]; var upperVals = new double[NumberOfParameters]; + bool haveBounds = false; + Exception? initializationFailure = null; // Get initial values try @@ -657,6 +811,7 @@ public Tuple GetParameterConstraints(IList // Get bounds of shape 2 lowerVals[3] = -2d; upperVals[3] = 2d; + haveBounds = true; // Correct initial value of kappa and hondo if necessary if (initialVals[2] <= lowerVals[2] || initialVals[2] >= upperVals[2]) @@ -669,33 +824,50 @@ public Tuple GetParameterConstraints(IList initialVals[3] = 0d; } + if (IsUsableInitializer(initialVals, lowerVals, upperVals, sample)) + return Tuple.Create(initialVals, lowerVals, upperVals); + initializationFailure = new InvalidOperationException("The Kappa L-moment initializer does not have finite likelihood within the fitting bounds."); } - catch + catch (Exception exception) when (exception is ArgumentException || exception is InvalidOperationException || exception is ArithmeticException) { + initializationFailure = exception; + } - // Get constraints from GEV + try + { + // Use the existing GEV initializer, preserving already established Kappa bounds. var gev = new GeneralizedExtremeValue(); var parms = gev.GetParameterConstraints(sample); for (int i = 0; i < 3; i++) { initialVals[i] = parms.Item1[i]; - lowerVals[i] = parms.Item2[i]; - upperVals[i] = parms.Item3[i]; - - // Get bounds of shape 2 - initialVals[3] = 0; - lowerVals[3] = -2d; - upperVals[3] = 2d; + if (!haveBounds) + { + lowerVals[i] = parms.Item2[i]; + upperVals[i] = parms.Item3[i]; + } } - + initialVals[3] = 0d; + lowerVals[3] = -2d; + upperVals[3] = 2d; + if (IsUsableInitializer(initialVals, lowerVals, upperVals, sample)) + return Tuple.Create(initialVals, lowerVals, upperVals); } - - - // - return new Tuple(initialVals, lowerVals, upperVals); + catch (Exception exception) when (exception is ArgumentException || exception is InvalidOperationException || exception is ArithmeticException) + { + throw new InvalidOperationException("Neither Kappa nor GEV initialization produced a valid Kappa Four fitting candidate.", + new AggregateException(initializationFailure!, exception)); + } + throw new InvalidOperationException("Neither Kappa nor GEV initialization has finite sample likelihood within the fitting bounds.", initializationFailure); } /// + /// + /// Uses the existing bounded Nelder-Mead optimizer. Returns only after successful solver + /// termination with valid parameters and finite sample likelihood. The unrestricted Kappa + /// likelihood can be unbounded, so numerical success is not proof of a finite global MLE. + /// + /// Initialization fails, the solver does not succeed, or the final fit is invalid or has nonfinite likelihood. public double[] MLE(IList sample) { // Set constraints @@ -704,7 +876,7 @@ public double[] MLE(IList sample) var Lowers = tuple.Item2; var Uppers = tuple.Item3; - // Solve using Powell + // Solve using the existing Nelder-Mead configuration. double logLH(double[] x) { var K4 = new KappaFour(); @@ -714,6 +886,10 @@ double logLH(double[] x) var solver = new NelderMead(logLH, NumberOfParameters, Initials, Lowers, Uppers); solver.ReportFailure = true; solver.Maximize(); + if (solver.Status != OptimizationStatus.Success) + throw new InvalidOperationException($"Kappa Four maximum likelihood estimation failed with optimizer status {solver.Status}."); + if (!IsUsableInitializer(solver.BestParameterSet.Values, Lowers, Uppers, sample)) + throw new InvalidOperationException("Kappa Four maximum likelihood estimation returned invalid parameters or nonfinite sample likelihood."); return solver.BestParameterSet.Values; } @@ -721,46 +897,84 @@ double logLH(double[] x) /// public override double PDF(double x) { - // Validate parameters - if (_parametersValid == false) ValidateParameters([Xi, Alpha, Kappa, Hondo], true); - if (x < Minimum || x > Maximum) return 0.0d; + EnsureValidParameters(); + if (double.IsNaN(x)) return double.NaN; + if (double.IsInfinity(x) || x < Minimum || x > Maximum) return 0d; + if (x == Minimum) return LowerEndpointDensity(); + if (x == Maximum) return Kappa < 1d ? 0d : Kappa == 1d ? 1d / Alpha : double.PositiveInfinity; + return Math.Exp(InteriorLogDensity(x)); + } - double F = CDF(x); - double y = (x - Xi) / Alpha; - if (Kappa == 0d) + /// Validates the observations needed by the four-parameter fitting initializers. + private static void ValidateFittingSample(IList sample) + { + if (sample is null) throw new ArgumentNullException(nameof(sample)); + if (sample.Count < 4) throw new ArgumentOutOfRangeException(nameof(sample), "At least four observations are required for Kappa Four estimation."); + for (int i = 0; i < sample.Count; i++) + if (!Tools.IsFinite(sample[i])) throw new ArgumentOutOfRangeException(nameof(sample), "Observations must be finite."); + } + + /// Checks parameter validity, unchanged fitting bounds, and finite sample likelihood. + private bool IsUsableInitializer(double[] parameters, double[] lower, double[] upper, IList sample) + { + if (ValidateParameters(parameters, false) != null) return false; + for (int i = 0; i < NumberOfParameters; i++) + if (!Tools.IsFinite(lower[i]) || !Tools.IsFinite(upper[i]) || lower[i] >= upper[i] + || parameters[i] < lower[i] || parameters[i] > upper[i]) return false; + var candidate = new KappaFour(parameters[0], parameters[1], parameters[2], parameters[3]); + return Tools.IsFinite(candidate.LogLikelihood(sample)); + } + + /// + /// + /// Evaluates interior densities directly in log space, including finite log densities whose + /// ordinary density underflows or overflows. Infinite endpoint densities retain the base + /// likelihood convention of returning negative infinity. + /// + public override double LogPDF(double x) + { + EnsureValidParameters(); + if (double.IsNaN(x) || double.IsInfinity(x) || x < Minimum || x > Maximum) return double.NegativeInfinity; + if (x == Minimum || x == Maximum) { - return Math.Exp(-y) / Alpha * Math.Pow(F, 1d - Hondo); + double density = PDF(x); + return Tools.IsFinite(density) && density > 0d ? Math.Log(density) : double.NegativeInfinity; } - double yy = 1 - Kappa * y; - return (1 / Alpha) * Math.Pow(yy, 1 / Kappa - 1) * Math.Pow(F, 1 - Hondo); + return InteriorLogDensity(x); } /// public override double CDF(double x) { - // Validate parameters - if (_parametersValid == false) ValidateParameters([Xi, Alpha, this.Kappa, this.Hondo], true); - if (x <= Minimum) return 0d; - if (x >= Maximum) return 1d; + return Math.Exp(LogCDF(x)); + } - double y = (x - Xi) / Alpha; - double yy = 1 - Kappa * y; - if (Kappa !=0 && Hondo != 0) - { - return Math.Pow(1 - Hondo * Math.Pow(yy, 1 / Kappa), 1 / Hondo); - } - else if (Kappa != 0 && Hondo == 0) - { - return Math.Exp(-Math.Pow(yy, 1 / Kappa)); - } - else if (Kappa == 0 && Hondo != 0) - { - return Math.Pow(1 - Hondo * Math.Exp(-y), 1 / Hondo); - } - else - { - return Math.Exp(-Math.Exp(-y)); - } + /// + public override double LogCDF(double x) + { + EnsureValidParameters(); + if (x <= Minimum) return double.NegativeInfinity; + if (x >= Maximum) return 0d; + return InteriorLogProbability(x, LogT(x)); + } + + /// + public override double CCDF(double x) + { + return -Tools.Expm1(LogCDF(x)); + } + + /// + public override double LogCCDF(double x) + { + EnsureValidParameters(); + if (x <= Minimum) return 0d; + if (x >= Maximum) return double.NegativeInfinity; + double logT = LogT(x); + double logF = InteriorLogProbability(x, logT); + // 1-F ~ t when t is too small to represent; retain its logarithm. + if (logF == 0d) return logT; + return logF < -Math.Log(2d) ? Tools.Log1p(-Math.Exp(logF)) : Math.Log(-Tools.Expm1(logF)); } /// @@ -775,22 +989,213 @@ public override double InverseCDF(double probability) if (_parametersValid == false) ValidateParameters([Xi, Alpha, Kappa, Hondo], true); - if (Kappa != 0 && Hondo != 0) - { - return Xi + Alpha / Kappa * (1 - Math.Pow((1 - Math.Pow(probability, Hondo)) / Hondo, Kappa)); - } - else if (Kappa != 0 && Hondo == 0) + return AffineQuantile(Xi, Alpha, StandardQuantile(Math.Log(probability), Kappa, Hondo)); + } + + /// Validates the current parameters before numerical evaluation. + private void EnsureValidParameters() + { + if (!_parametersValid) ValidateParameters(GetParameters, true); + } + + /// Returns the continuous exponential divided difference (exp(x)-1)/x. + private static double ExponentialRelative(double x) + { + return x == 0d ? 1d : Tools.Expm1(x) / x; + } + + /// Returns log((exp(x)-1)/x) without intermediate overflow or cancellation. + private static double LogExponentialRelative(double x) + { + if (Math.Abs(x) < 1E-4) + return Tools.Log1p(x * (0.5d + x * (1d / 6d + x * (1d / 24d + x * (1d / 120d + x / 720d))))); + return x > 0d ? x + Math.Log(-Tools.Expm1(-x)) - Math.Log(x) + : Math.Log(-Tools.Expm1(x)) - Math.Log(-x); + } + + /// Evaluates the standardized quantile from log probability, retaining nonzero shapes. + private static double StandardQuantile(double logProbability, double k, double h) + { + return QuantileFromLogT(QuantileLogT(logProbability, h), k); + } + + /// Returns the quantile latent log(t), including products outside the finite double range. + private static double QuantileLogT(double logProbability, double h) + { + double s = h * logProbability; + if (double.IsPositiveInfinity(s)) return double.PositiveInfinity; + if (h > 0d && s < -0.5d) return Math.Log(-Tools.Expm1(s)) - Math.Log(h); + return Math.Log(-logProbability) + LogExponentialRelative(s); + } + + /// Converts log(t) to a standardized quantile without indeterminate products at limits. + private static double QuantileFromLogT(double w, double k) + { + if (k == 0d) return -w; + double v = k * w; + return Math.Abs(v) < 0.5d ? -w * ExponentialRelative(v) : -Tools.Expm1(v) / k; + } + + /// Applies location and scale, recovering finite cancellation after product overflow. + private static double AffineQuantile(double location, double scale, double value) + { + double product = scale * value; + if (double.IsInfinity(product) && Tools.IsFinite(value) && Math.Sign(location) != Math.Sign(product)) + return scale * (value + location / scale); + return location + product; + } + + /// Evaluates the finite-support affine endpoint without overflowing an intermediate quotient. + private double LocationPlusScaleOverShape() + { + double shift = Alpha / Kappa; + if (double.IsInfinity(shift) && Math.Sign(Xi) != Math.Sign(shift)) return (Xi * Kappa + Alpha) / Kappa; + return Xi + shift; + } + + /// + /// Integrates a function of log probability over the full unit probability interval. + /// Each half uses p=u^8/2 (or its survival counterpart), regularizing endpoint singularities + /// and avoiding the loss of upper-tail probabilities to subtraction from one. + /// + /// The function evaluated at log(p). + /// The integral, or NaN if quadrature fails its status or error checks. + private static double IntegrateProbability(Func integrand) + { + var integration = new AdaptiveGaussKronrod(u => { - return Xi + (Alpha / Kappa) * (1 - Math.Pow(-Math.Log(probability), Kappa)); - } - else if (Kappa == 0 && Hondo != 0) + double logU = Math.Log(u); + double logTail = 8d * logU - Math.Log(2d); + double jacobian = Math.Exp(Math.Log(4d) + 7d * logU); + // Pair the two tails before estimating the error so relative error is measured + // against the complete integral, including cancellation in a near-zero mean. + double value = integrand(logTail) * jacobian + integrand(Tools.Log1p(-Math.Exp(logTail))) * jacobian; + if (!Tools.IsFinite(value)) throw new ArithmeticException("The Kappa Four quantile integrand is nonfinite."); + return value; + }, 0d, 1d) { - return Xi - Alpha * Math.Log((1 - Math.Pow(probability, Hondo)) / Hondo); - } - else + RelativeTolerance = 1E-10, + AbsoluteTolerance = 1E-12, + ReportFailure = false + }; + integration.Integrate(); + if (integration.Status != IntegrationStatus.Success || !Tools.IsFinite(integration.Result) + || !Tools.IsFinite(integration.StandardError)) return double.NaN; + return integration.StandardError <= Math.Max(1E-12, 1E-10 * Math.Abs(integration.Result)) ? integration.Result : double.NaN; + } + + /// Tests absolute existence of an ordinary moment of the given positive order. + private bool MomentExists(int order) + { + return order * Kappa > -1d && (Hondo >= 0d || order * Kappa * Hondo > -1d); + } + + /// + /// Computes moments in standardized coordinates, centering before higher powers and then + /// applying location and scale. Nonexistent or numerically unresolved moments are NaN. + /// + private double[] ComputeMoments() + { + EnsureValidParameters(); + double[] result = [double.NaN, double.NaN, double.NaN, double.NaN]; + if (!MomentExists(1)) return result; + double mean = IntegrateProbability(logP => StandardQuantile(logP, Kappa, Hondo)); + if (!Tools.IsFinite(mean)) return result; + result[0] = AffineQuantile(Xi, Alpha, mean); + if (!MomentExists(2)) return result; + double variance = IntegrateProbability(logP => Math.Pow(StandardQuantile(logP, Kappa, Hondo) - mean, 2d)); + if (!(variance > 0d) || !Tools.IsFinite(variance)) return result; + double sd = Math.Sqrt(variance); + result[1] = Alpha * sd; + if (MomentExists(3)) result[2] = IntegrateProbability(logP => Math.Pow((StandardQuantile(logP, Kappa, Hondo) - mean) / sd, 3d)); + if (MomentExists(4)) result[3] = IntegrateProbability(logP => Math.Pow((StandardQuantile(logP, Kappa, Hondo) - mean) / sd, 4d)); + return result; + } + + /// Returns the derivative of the exponential divided difference. + private static double ExponentialRelativeDerivative(double x) + { + if (Math.Abs(x) < 1E-3) + return 0.5d + x * (1d / 3d + x * (1d / 8d + x * (1d / 30d + x * (1d / 144d + x / 840d)))); + if (x > 50d) return Math.Exp(x + Math.Log(x - 1d) - 2d * Math.Log(x)); + return (x * Math.Exp(x) - Tools.Expm1(x)) / (x * x); + } + + /// Returns the derivative of log(exprel(x)), including its removable singularity. + private static double LogExponentialRelativeDerivative(double x) + { + if (Math.Abs(x) < 1E-3) return 0.5d + x * (1d / 12d + x * x * (-1d / 720d + x * x / 30240d)); + return x > 0d ? 1d / -Tools.Expm1(-x) - 1d / x : Math.Exp(x) / Tools.Expm1(x) - 1d / x; + } + + /// Returns the unit-scale quantile gradient in the order xi, alpha, kappa, hondo. + private static double[] StandardQuantileGradient(double logProbability, double k, double h) + { + double s = h * logProbability; + double w = QuantileLogT(logProbability, h); + double v = k * w; + double dh = h > 0d && s < -0.5d ? logProbability * (Math.Exp(s) / Tools.Expm1(s)) - 1d / h + : logProbability * LogExponentialRelativeDerivative(s); + double dk = v < -50d ? -1d / k / k + : v > 50d ? -Math.Exp(v + Math.Log(v - 1d) - 2d * Math.Log(Math.Abs(k))) + : -w * w * ExponentialRelativeDerivative(v); + return [1d, QuantileFromLogT(w, k), dk, -Math.Exp(v) * dh]; + } + + /// Returns log(t), where t=(1-k*y)^(1/k), with its k=0 limit. + private double LogT(double x) + { + double y = (x - Xi) / Alpha; + if (double.IsInfinity(y) && Tools.IsFinite(x)) y = x / Alpha - Xi / Alpha; + if (Kappa == 0d) return -y; + double product = -Kappa * y; + if (product < -0.9d) return KappaFourBoundary.LogT(x, Xi, Alpha, Kappa); + double logBase = double.IsPositiveInfinity(product) ? Math.Log(Math.Abs(Kappa)) + Math.Log(Math.Abs(y)) : Tools.Log1p(product); + return logBase / Kappa; + } + + /// Evaluates log F from log(t) without exponentiating a large t. + private double LogProbabilityFromLogT(double w) + { + if (Hondo == 0d) return -Math.Exp(w); + double z = Math.Log(Math.Abs(Hondo)) + w; + if (z < -36d) return -Math.Exp(w); // correction is smaller than a double rounding unit + if (Hondo < 0d) + return (z > 0d ? z + Tools.Log1p(Math.Exp(-z)) : Tools.Log1p(Math.Exp(z))) / Hondo; + return (z < -Math.Log(2d) ? Tools.Log1p(-Math.Exp(z)) : Math.Log(-Tools.Expm1(z))) / Hondo; + } + + /// Retains the small lower-support residual when ordinary log terms nearly cancel. + private double InteriorLogProbability(double x, double w) + { + if (Hondo > 0d && Math.Abs(Math.Log(Hondo) + w) < 1E-5) + return KappaFourBoundary.LogProbability(x, Xi, Alpha, Kappa, Hondo); + return LogProbabilityFromLogT(w); + } + + /// Evaluates the interior log density using the latent transform. + private double InteriorLogDensity(double x) + { + double w = LogT(x); + if (Hondo < 0d && Math.Log(-Hondo) + w > 0d) { - return Xi - Alpha * Math.Log(-Math.Log(probability)); + // Combine the linear terms before multiplication when t is large. + double z = Math.Log(-Hondo) + w; + double correction = Math.Log(-Hondo) + Tools.Log1p(Math.Exp(-z)); + double result = (1d / Hondo - Kappa) * w + (1d / Hondo - 1d) * correction - Math.Log(Alpha); + return double.IsNaN(result) ? (w + correction) / Hondo - Kappa * w - correction - Math.Log(Alpha) : result; } + return (1d - Kappa) * w + (1d - Hondo) * InteriorLogProbability(x, w) - Math.Log(Alpha); + } + + /// Returns the one-sided density limit at the finite lower support endpoint. + private double LowerEndpointDensity() + { + if (Hondo > 0d) return Hondo < 1d ? 0d : Hondo == 1d ? 1d / Alpha : double.PositiveInfinity; + if (Hondo == 0d) return 0d; + double exponent = 1d / Hondo - Kappa; + return exponent < 0d ? 0d : exponent > 0d ? double.PositiveInfinity + : Math.Exp((1d / Hondo - 1d) * Math.Log(-Hondo) - Math.Log(Alpha)); } /// @@ -802,27 +1207,31 @@ public override UnivariateDistributionBase Clone() } /// + /// Parameter covariance has not been implemented for Kappa Four. public double[,] ParameterCovariance(int sampleSize, ParameterEstimationMethod estimationMethod) { throw new NotImplementedException(); } /// + /// Quantile variance has not been implemented for Kappa Four. public double QuantileVariance(double probability, int sampleSize, ParameterEstimationMethod estimationMethod) { throw new NotImplementedException(); } /// + /// Uses continuous zero-shape limits in the parameter order xi, alpha, kappa, hondo. + /// The probability is not strictly between zero and one, or distribution parameters are invalid. public double[] QuantileGradient(double probability) { - double a = Alpha, k = Kappa, h = Hondo, F = probability; - double dxi = 1d; - double da = (1d - Math.Pow((1d - Math.Pow(F, h)) / h, k)) / k; - double dk = a * (-(1d - Math.Pow((1d - Math.Pow(F, h)) / h, k)) / (k * k) - Math.Pow((1d - Math.Pow(F, h)) / h, k) * Math.Log((1d - Math.Pow(F, h)) / h) / k); - double x = 1d - Math.Pow(F, h); - double dh = -(a * (Math.Pow(F, h) - h * Math.Log(F) * Math.Pow(F, h) - 1d) * Math.Sign(x) * Math.Pow(Math.Abs(x), k - 1d)) / (Math.Sign(h) * Math.Pow(Math.Abs(h), k + 1d)); - return [dxi, da, dk, dh]; + EnsureValidParameters(); + if (double.IsNaN(probability) || probability <= 0d || probability >= 1d) + throw new ArgumentOutOfRangeException(nameof(probability), "Quantile gradients require a probability strictly between zero and one."); + double[] gradient = StandardQuantileGradient(Math.Log(probability), Kappa, Hondo); + gradient[2] *= Alpha; + gradient[3] *= Alpha; + return gradient; } /// @@ -852,4 +1261,4 @@ public double[] QuantileGradient(double probability) return jacobian.ToArray(); } } -} \ No newline at end of file +} diff --git a/Numerics/Distributions/Univariate/KappaFourBoundary.cs b/Numerics/Distributions/Univariate/KappaFourBoundary.cs new file mode 100644 index 00000000..550f3f06 --- /dev/null +++ b/Numerics/Distributions/Univariate/KappaFourBoundary.cs @@ -0,0 +1,236 @@ +using System; + +namespace Numerics.Distributions +{ + /// + /// Evaluates the Kappa Four lower-support residual with compensated arithmetic when + /// ordinary logarithms lose the distance from a positive-hondo boundary. + /// + internal static class KappaFourBoundary + { + private static readonly Pair LogTwo = new Pair(0.6931471805599453d, 2.3190468138462996E-17d); + + /// Returns log F close to the lower support endpoint for positive hondo. + /// The value being evaluated. + /// The finite location parameter. + /// The positive finite scale parameter. + /// The finite kappa shape. + /// The positive finite hondo shape. + /// The log probability, including negative infinity at or below the endpoint. + /// + /// Retains the low components of the standardized subtraction and logarithms. The + /// logarithm uses an atanh series after binary range reduction; its truncation error + /// is below the precision of a compensated pair. No endpoint clipping is applied. + /// + internal static double LogProbability(double x, double xi, double alpha, double k, double h) + { + Pair y = StandardizedDifference(x, xi, alpha); + if (y.High == 0d && h == 1d && x > xi) + { + // The standardized distance can underflow although its logarithm is finite. + Pair difference = Subtract(new Pair(x), new Pair(xi)); + return Subtract(Log(difference), Log(new Pair(alpha))).High; + } + + Pair logH = Log(new Pair(h)); + Pair z; + if (k == 0d) + { + z = Subtract(logH, y); + } + else + { + Pair product = Multiply(new Pair(-k), y); + if (Math.Abs(product.High) < 0.125d) + { + // log(1-k*y)/k = -y*log1p(-k*y)/(-k*y), including k -> 0. + z = Subtract(logH, Multiply(y, LogOnePlusRelative(product))); + } + else + { + Pair logBase; + if (double.IsPositiveInfinity(product.High)) + { + // Here the reciprocal-product correction is below pair precision. + logBase = Add(Log(new Pair(Math.Abs(k))), Log(Abs(y))); + } + else + { + Pair basis = Add(new Pair(1d), product); + if (basis.High <= 0d) return 0d; + logBase = Log(basis); + } + z = Divide(Add(logBase, Multiply(new Pair(k), logH)), new Pair(k)); + } + } + + if (z.High >= 0d) return double.NegativeInfinity; + return Math.Log(-Tools.Expm1(z.High)) / h; + } + + /// Evaluates the latent logarithm close to an endpoint where kappa times the standardized value approaches one. + /// The value being evaluated. + /// The finite location parameter. + /// The positive finite scale parameter. + /// The finite kappa shape. + /// Logarithm of the latent transform, with its continuous zero-kappa limit. + /// Retains subtraction and product corrections in the small positive base 1-k*(x-xi)/alpha. + internal static double LogT(double x, double xi, double alpha, double k) + { + Pair y = StandardizedDifference(x, xi, alpha); + if (k == 0d) return -y.High; + Pair basis = Subtract(new Pair(1d), Multiply(new Pair(k), y)); + if (basis.High <= 0d) return Math.Log(basis.High) / k; + return Divide(Log(basis), new Pair(k)).High; + } + + /// Forms the standardized difference in pairs, halving before a subtraction that would overflow. + private static Pair StandardizedDifference(double x, double xi, double alpha) + { + Pair difference = Subtract(new Pair(x), new Pair(xi)); + if (double.IsInfinity(difference.High)) + { + difference = Subtract(new Pair(x * 0.5d), new Pair(xi * 0.5d)); + return Multiply(Divide(difference, new Pair(alpha)), new Pair(2d)); + } + return Divide(difference, new Pair(alpha)); + } + + /// Stores a leading double and its nonoverlapping rounding correction. + private readonly struct Pair + { + internal readonly double High; + internal readonly double Low; + + internal Pair(double high, double low = 0d) + { + High = high; + Low = low; + } + } + + /// Renormalizes two components using an error-free finite sum. + private static Pair Normalize(double high, double low) + { + double sum = high + low; + if (double.IsInfinity(sum) || double.IsNaN(sum)) return new Pair(sum); + double part = sum - high; + return new Pair(sum, (high - (sum - part)) + (low - part)); + } + + /// Adds compensated pairs, retaining the leading addition error. + private static Pair Add(Pair first, Pair second) + { + double sum = first.High + second.High; + if (double.IsInfinity(sum) || double.IsNaN(sum)) return new Pair(sum); + double part = sum - first.High; + double error = (first.High - (sum - part)) + (second.High - part); + return Normalize(sum, error + first.Low + second.Low); + } + + /// Subtracts compensated pairs. + private static Pair Subtract(Pair first, Pair second) + { + return Add(first, new Pair(-second.High, -second.Low)); + } + + /// Returns the absolute value of a compensated pair. + private static Pair Abs(Pair value) + { + return value.High < 0d ? new Pair(-value.High, -value.Low) : value; + } + + /// + /// Multiplies compensated pairs using Dekker's product correction. Splitting the + /// significand with a bit mask avoids overflowing a large splitter multiplication. + /// + private static Pair Multiply(Pair first, Pair second) + { + double product = first.High * second.High; + if (double.IsInfinity(product) || double.IsNaN(product)) return new Pair(product); + const long mask = ~((1L << 27) - 1L); + double firstHigh = BitConverter.Int64BitsToDouble(BitConverter.DoubleToInt64Bits(first.High) & mask); + double secondHigh = BitConverter.Int64BitsToDouble(BitConverter.DoubleToInt64Bits(second.High) & mask); + double firstLow = first.High - firstHigh; + double secondLow = second.High - secondHigh; + double error = ((firstHigh * secondHigh - product) + firstHigh * secondLow + firstLow * secondHigh) + + firstLow * secondLow; + error += first.High * second.Low + first.Low * second.High + first.Low * second.Low; + return Normalize(product, error); + } + + /// Divides compensated pairs using two residual corrections. + private static Pair Divide(Pair numerator, Pair denominator) + { + double quotient = numerator.High / denominator.High; + if (double.IsInfinity(quotient) || double.IsNaN(quotient)) return new Pair(quotient); + Pair result = new Pair(quotient); + Pair residual = Subtract(numerator, Multiply(denominator, result)); + double correction = residual.High / denominator.High; + result = Add(result, new Pair(correction)); + residual = Subtract(residual, Multiply(denominator, new Pair(correction))); + return Add(result, new Pair(residual.High / denominator.High)); + } + + /// Scales a pair by a power of two without overflowing a scaling factor. + private static Pair Scale(Pair value, int exponent) + { + while (exponent > 512) + { + value = new Pair(value.High * 1.3407807929942597E154d, value.Low * 1.3407807929942597E154d); + exponent -= 512; + } + while (exponent < -512) + { + value = new Pair(value.High * 7.458340731200207E-155d, value.Low * 7.458340731200207E-155d); + exponent += 512; + } + double factor = Math.Pow(2d, exponent); + return new Pair(value.High * factor, value.Low * factor); + } + + /// Evaluates a positive pair's logarithm by binary reduction and an atanh series. + private static Pair Log(Pair value) + { + if (!(value.High > 0d) || double.IsInfinity(value.High)) return new Pair(Math.Log(value.High)); + double leading = value.High; + int correction = 0; + if (leading < 2.2250738585072014E-308d) + { + leading *= 18014398509481984d; + correction = -54; + } + int exponent = (int)((BitConverter.DoubleToInt64Bits(leading) >> 52) & 0x7ffL) - 1023 + correction; + Pair reduced = Scale(value, -exponent); + if (reduced.High > 1.4142135623730951d) + { + reduced = Scale(reduced, -1); + exponent++; + } + Pair ratio = Divide(Subtract(reduced, new Pair(1d)), Add(reduced, new Pair(1d))); + Pair square = Multiply(ratio, ratio); + Pair term = ratio; + Pair sum = ratio; + for (int index = 1; index <= 24; index++) + { + term = Multiply(term, square); + sum = Add(sum, Divide(term, new Pair(2d * index + 1d))); + } + return Add(Multiply(sum, new Pair(2d)), Multiply(LogTwo, new Pair(exponent))); + } + + /// Evaluates log(1+v)/v continuously near zero without dividing by a tiny v. + private static Pair LogOnePlusRelative(Pair value) + { + Pair sum = new Pair(1d); + Pair term = new Pair(1d); + Pair negative = new Pair(-value.High, -value.Low); + for (int index = 1; index <= 40; index++) + { + term = Multiply(term, negative); + sum = Add(sum, Divide(term, new Pair(index + 1d))); + } + return sum; + } + } +} diff --git a/Test_Numerics/Distributions/Univariate/Fixtures/generate_kappa_four_oracle.py b/Test_Numerics/Distributions/Univariate/Fixtures/generate_kappa_four_oracle.py new file mode 100644 index 00000000..e2792816 --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Fixtures/generate_kappa_four_oracle.py @@ -0,0 +1,76 @@ +"""Regenerate the frozen Kappa Four probability oracle using Python's 90-digit Decimal. +Formula provenance: SciPy scipy.stats.kappa4 and Hosking's Kappa quantile. +No Numerics code, NumPy, SciPy installation, or runtime network is used. +The represented binary64 shapes/probabilities/arguments are converted exactly to Decimal. +Rows whose rounded quantile is outside the mathematical open support are omitted. +""" +from decimal import Decimal as D, localcontext +from pathlib import Path +import csv +import math + +target = Path(__file__).with_name("kappa-four-probabilities.csv") +shapes = [-2., -1., -.5, -.2, -1e-8, -1e-12, -1e-16, 0., 1e-16, 1e-12, 1e-8, .2, .5, 1., 2.] +probabilities = [1e-12, .01, .1, .5, .9, .99, .999999999999] +rows = [] + +def quantile(k, h, p): + a = -p.ln() if not h else (1 - (h*p.ln()).exp()) / h + return -a.ln() if not k else (1 - (k*a.ln()).exp()) / k + +with localcontext() as context: + context.prec = 90 + for kf in shapes: + for hf in shapes: + k, h = D.from_float(kf), D.from_float(hf) + for pf in probabilities: + p = D.from_float(pf) + q = quantile(k, h, p) + x = D.from_float(float(q)) + base = 1 - k*x + if base <= 0: + continue + logt = -x if not k else base.ln()/k + t = logt.exp() + b = 1 - h*t + if b <= 0: + continue + logf = -t if not h else b.ln()/h + logpdf = (1-k)*logt + (1-h)*logf + # High-precision symmetric differences independently check production derivatives. + step = D('1e-25') + dk = (quantile(k+step, h, p) - quantile(k-step, h, p))/(2*step) + dh = (quantile(k, h+step, p) - quantile(k, h-step, p))/(2*step) + rows.append([kf, hf, pf, float(x), float(q), float(logf.exp()), float(logpdf.exp()), float(dk), float(dh)]) +with target.open("w", newline="", encoding="ascii") as output: + writer = csv.writer(output, lineterminator="\n") + writer.writerow(["kappa", "hondo", "probability", "x", "quantile", "cdf", "pdf", "gradient_kappa", "gradient_hondo"]) + writer.writerows(rows) +print(f"Wrote {len(rows)} cases to {target.name}") + +boundary_rows = [] +with localcontext() as context: + # Extra digits retain the subnormal distance above the exact zero boundary at h=1. + context.prec = 400 + for kf in [-2., -.5, 0., .2, 2., 10.]: + for hf in [.2, .5, 1., 2., 10.]: + k, h = D.from_float(kf), D.from_float(hf) + lower = h.ln() if not k else (1-(-k*h.ln()).exp())/k + endpoints = [(lower, math.inf)] + if k > 0: + endpoints.append((1/k, -math.inf)) + for endpoint, direction in endpoints: + xf = float(endpoint) + for unused in range(4): + xf = math.nextafter(xf, direction) + x = D.from_float(xf) + logt = -x if not k else (1-k*x).ln()/k + logcdf = (1-h*logt.exp()).ln()/h + logpdf = (1-k)*logt+(1-h)*logcdf + boundary_rows.append([kf, hf, xf, float(logcdf), float(logpdf)]) +boundary_target = target.with_name("kappa-four-boundaries.csv") +with boundary_target.open("w", newline="", encoding="ascii") as output: + writer = csv.writer(output, lineterminator="\n") + writer.writerow(["kappa", "hondo", "x", "logcdf", "logpdf"]) + writer.writerows(boundary_rows) +print(f"Wrote {len(boundary_rows)} boundary cases to {boundary_target.name}") diff --git a/Test_Numerics/Distributions/Univariate/Fixtures/kappa-four-boundaries.csv b/Test_Numerics/Distributions/Univariate/Fixtures/kappa-four-boundaries.csv new file mode 100644 index 00000000..52461318 --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Fixtures/kappa-four-boundaries.csv @@ -0,0 +1,46 @@ +kappa,hondo,x,logcdf,logpdf +-2.0,0.2,-0.47999999999999976,-163.78559457887573,-126.20016192579831 +-2.0,0.5,-0.3749999999999998,-69.31471805599453,-32.57791748631743 +-2.0,1.0,2e-323,-743.0537775602613,-6e-323 +-2.0,2.0,1.5000000000000009,-18.021826694558577,15.942385152878742 +-2.0,10.0,49.50000000000003,-3.5796793311185633,25.30935870108493 +-0.5,0.2,-1.1055728090000831,-168.92034240505183,-132.7221170553903 +-0.5,0.5,-0.5857864376269045,-70.07355616013324,-33.9970573092267 +-0.5,1.0,2e-323,-743.0537775602613,-3e-323 +-0.5,2.0,0.8284271247461905,-17.881943145931114,16.842222375091197 +-0.5,10.0,4.324555320336763,-3.4320338503208725,27.434427013396782 +0.0,0.2,-1.6094379124340994,-173.0811601306311,-136.85549019207076 +0.0,0.5,-0.6931471805599448,-70.59920742655979,-34.60645653271995 +0.0,1.0,2e-323,-743.0537775602613,-2e-323 +0.0,2.0,0.6931471805599457,-17.70206969381971,17.008922513259762 +0.0,10.0,2.3025850929940477,-3.3848918386883615,28.161441455201206 +0.2,0.2,-1.8986483073060731,-174.52369952316343,-138.33140928858347 +0.2,0.2,4.9999999999999964,-1.2058997559173439e-76,-139.8473928750617 +0.2,0.5,-0.7434917749851746,-71.02330714775353,-34.95713582942881 +0.2,0.5,4.9999999999999964,-1.2058997559173439e-76,-139.8473928750617 +0.2,1.0,2e-323,-743.0537775602613,-1.5e-323 +0.2,1.0,4.9999999999999964,-1.2058997559173439e-76,-139.8473928750617 +0.2,2.0,0.6472471835193797,-17.648649063939587,17.09413131949163 +0.2,2.0,4.9999999999999964,-1.2058997559173439e-76,-139.8473928750617 +0.2,10.0,1.8452132775990346,-3.4173418432126295,28.91400851451843 +0.2,10.0,4.9999999999999964,-1.2058997559173439e-76,-139.8473928750617 +2.0,0.2,-11.999999999999991,-178.7177439833341,-144.58363309910135 +2.0,0.2,0.4999999999999998,-2.1073424299855936e-08,17.675253087419865 +2.0,0.5,-1.4999999999999991,-72.0873067782343,-36.7368005696771 +2.0,0.5,0.4999999999999998,-2.107342436646932e-08,17.675253093741894 +2.0,1.0,2e-323,-743.0537775602613,2e-323 +2.0,1.0,0.4999999999999998,-2.1073424477491626e-08,17.675253104278607 +2.0,2.0,0.3750000000000002,-17.328679513998633,18.02182669455858 +2.0,2.0,0.4999999999999998,-2.1073424699536237e-08,17.67525312535203 +2.0,10.0,0.4950000000000002,-3.1458685910446573,30.615402412395984 +2.0,10.0,0.4999999999999998,-2.1073426475893378e-08,17.675253293939445 +10.0,0.2,-976562.399999999,-187.733612414409,-164.6718311434341 +10.0,0.2,0.09999999999999995,-0.02959011977956636,31.68577875978709 +10.0,0.5,-102.29999999999994,-74.76231517213523,-43.61948221110712 +10.0,0.5,0.09999999999999995,-0.02972250148505874,31.694589604868217 +10.0,1.0,2e-323,-743.0537775602613,1.8e-322 +10.0,1.0,0.09999999999999995,-0.029946694231720975,31.709450855610744 +10.0,2.0,0.09990234375000005,-15.27488461951245,21.513209244552446 +10.0,2.0,0.09999999999999995,-0.030408975900199503,31.739859831510945 +10.0,10.0,0.09999999999000006,-1.4322426734989497,33.613455319590926 +10.0,10.0,0.09999999999999995,-0.034959626738983646,32.0240874962616 diff --git a/Test_Numerics/Distributions/Univariate/Fixtures/kappa-four-probabilities.csv b/Test_Numerics/Distributions/Univariate/Fixtures/kappa-four-probabilities.csv new file mode 100644 index 00000000..80de8b87 --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Fixtures/kappa-four-probabilities.csv @@ -0,0 +1,1547 @@ +kappa,hondo,probability,x,quantile,cdf,pdf,gradient_kappa,gradient_hondo +-2.0,-2.0,0.01,-0.4999999799959994,-0.4999999799959994,0.010000000002818024,124962.50364420966,-0.2499998196220625,1.642580799359263e-07 +-2.0,-2.0,0.1,-0.4997959391898786,-0.4997959391898786,0.1000000000000005,121.28737499999815,-0.24910172989091425,0.0007451662190702231 +-2.0,-2.0,0.5,-0.2777777777777778,-0.2777777777777778,0.5,0.421875,-0.04878553153151902,0.18853166255404166 +-2.0,-2.0,0.9,35.849030470914144,35.849030470914144,0.9,0.0011760973936899856,-59.97717680414772,3.9641547299022237 +-2.0,-2.0,0.99,4850.872490593663,4850.872490593663,0.99,1.0152281667815824e-06,-19842.76157125678,48.9212662811184 +-2.0,-2.0,0.999999999999,5.0002212245269885e+23,5.0002212245269885e+23,0.999999999999,9.999336363092361e-37,-1.356612182416663e+25,500011061103.6681 +-2.0,-1.0,0.01,-0.49994898479746963,-0.49994898479746963,0.010000000000001447,97.02989999998555,-0.24974007142892896,0.0003725831095351115 +-2.0,-1.0,0.1,-0.49382716049382713,-0.49382716049382713,0.1000000000000002,7.2899999999999805,-0.23335046557199865,0.019239850384006114 +-2.0,-1.0,0.5,-0.0,-0.0,0.5,0.25,0.0,0.38629436111989063 +-2.0,-1.0,0.9,40.00000000000002,40.00000000000002,0.9,0.0011111111111111102,-68.98759538211692,4.342017682839305 +-2.0,-1.0,0.99,4899.999999999991,4899.999999999991,0.99,1.0101010101010128e-06,-20068.38482558452,49.33417001676246 +-2.0,-1.0,0.999999999999,5.000221224531989e+23,5.000221224531989e+23,0.999999999999,9.999336363087361e-37,-1.3566121824180447e+25,500011061104.0848 +-2.0,-0.5,1e-12,-0.499999999999875,-0.499999999999875,1.0000869003186338e-12,7.9999759989812205,-0.24999999999812392,6.4077750022879205e-12 +-2.0,-0.5,0.01,-0.4984567901234568,-0.4984567901234568,0.01000000000000004,5.831999999999996,-0.24476794481806147,0.009619925192003057 +-2.0,-0.5,0.1,-0.4732645751229371,-0.4732645751229371,0.09999999999999994,2.5575514027824204,-0.19748335576680698,0.07311998687576479 +-2.0,-0.5,0.5,0.22855339059327376,0.22855339059327376,0.5,0.20101012677666932,-0.02285629132869072,0.5341061400967259 +-2.0,-0.5,0.9,42.22037420613658,42.22037420613658,0.9,0.0010811008239694517,-73.8961472550621,4.540558158038851 +-2.0,-0.5,0.99,4924.718906838875,4924.718906838875,0.99,1.0075566910448782e-06,-20182.002284325128,49.54156187093501 +-2.0,-0.5,0.999999999999,5.0002212245344886e+23,5.0002212245344886e+23,0.999999999999,9.999336363084862e-37,-1.3566121824187353e+25,500011061104.2931 +-2.0,-0.2,1e-12,-0.4999996804823804,-0.4999996804823804,9.999999999684999e-13,7.793145567040145e-06,-0.2499975615523621,1.4532595522036878e-05 +-2.0,-0.2,0.01,-0.49125033033966536,-0.49125033033966536,0.009999999999999988,1.7197587246496182,-0.22792636771518046,0.04639331133734136 +-2.0,-0.2,0.1,-0.4415375700777839,-0.4415375700777839,0.10000000000000002,1.57811887057558,-0.1580320565449921,0.14491044493631738 +-2.0,-0.2,0.5,0.4045189449482034,0.4045189449482034,0.5,0.17889265535406873,-0.06583774998786997,0.6414460804681947 +-2.0,-0.2,0.9,43.60081964242441,43.60081964242441,0.9,0.0010638392907026484,-76.97716937619528,4.662803513225958 +-2.0,-0.2,0.99,4939.600080192439,4939.600080192439,0.99,1.0060362254396973e-06,-20250.431725844708,49.66629814831739 +-2.0,-0.2,0.999999999999,5.000221224535989e+23,5.000221224535989e+23,0.999999999999,9.99933636308336e-37,-1.35661218241915e+25,500011061104.4181 +-2.0,-1e-08,1e-12,-0.49934509845522124,-0.49934509845522124,1.000000000000124e-12,2.1095550559359106e-08,-0.24749897079676697,0.018095599245969173 +-2.0,-1e-08,0.01,-0.47642353895928446,-0.47642353895928446,0.009999999999999978,0.9766457467889672,-0.2022062779867396,0.10857361630914635 +-2.0,-1e-08,0.1,-0.4056941536656654,-0.4056941536656654,0.10000000000000002,1.2208071694311475,-0.1241929401276935,0.21714723678495929 +-2.0,-1e-08,0.5,0.5406844832893287,0.5406844832893287,0.5,0.1665123265715525,-0.11108206412299018,0.721347516277815 +-2.0,-1e-08,0.9,44.5416435026478,44.5416435026478,0.9,0.0010526310395014289,-79.08942113176155,4.745610786348286 +-2.0,-1e-08,0.99,4949.541665958727,4949.541665958727,0.99,1.0050251256359224e-06,-20296.159508943776,49.74958123254437 +-2.0,-1e-08,0.999999999999,5.000221224536989e+23,5.000221224536989e+23,0.999999999999,9.999336363082361e-37,-1.3566121824194262e+25,500011061104.5014 +-2.0,-1e-12,1e-12,-0.4993450982742833,-0.4993450982742833,1.0000000000004111e-12,2.1095547645197937e-08,-0.24749897019624503,0.018095603412218827 +-2.0,-1e-12,0.01,-0.4764235378736568,-0.4764235378736568,0.01000000000000001,0.9766457243031177,-0.2022062763287912,0.10857362047539629 +-2.0,-1e-12,0.1,-0.4056941514944102,-0.4056941514944102,0.1,1.2208071553774913,-0.12419293831679616,0.21714724095120924 +-2.0,-1e-12,0.5,0.5406844905020826,0.5406844905020826,0.5,0.16651232599452245,-0.11108206676655763,0.721347520444065 +-2.0,-1e-12,0.9,44.54164355009916,44.54164355009916,0.9,0.0010526310389469557,-79.08942123854455,4.745610790514536 +-2.0,-1e-12,0.99,4949.541666456173,4949.541666456173,0.99,1.0050251255854233e-06,-20296.1595112321,49.749581236710625 +-2.0,-1e-12,0.999999999999,5.000221224536989e+23,5.000221224536989e+23,0.999999999999,9.999336363082361e-37,-1.3566121824194262e+25,500011061104.5014 +-2.0,-1e-16,1e-12,-0.4993450982742652,-0.4993450982742652,1.0000000000004711e-12,2.109554764490765e-08,-0.24749897019618497,0.018095603412635452 +-2.0,-1e-16,0.01,-0.47642353787354824,-0.47642353787354824,0.010000000000000028,0.9766457243008697,-0.20220627632862542,0.10857362047581291 +-2.0,-1e-16,0.1,-0.4056941514941931,-0.4056941514941931,0.09999999999999998,1.220807155376086,-0.12419293831661507,0.21714724095162588 +-2.0,-1e-16,0.5,0.5406844905028039,0.5406844905028039,0.5,0.16651232599446475,-0.11108206676682199,0.7213475204444817 +-2.0,-1e-16,0.9,44.541643550103906,44.541643550103906,0.9,0.0010526310389469002,-79.08942123855523,4.745610790514952 +-2.0,-1e-16,0.99,4949.541666456223,4949.541666456223,0.99,1.0050251255854182e-06,-20296.15951123233,49.749581236711045 +-2.0,-1e-16,0.999999999999,5.000221224536989e+23,5.000221224536989e+23,0.999999999999,9.999336363082361e-37,-1.3566121824194262e+25,500011061104.5014 +-2.0,0.0,1e-12,-0.4993450982742652,-0.4993450982742652,1.000000000000433e-12,2.10955476449069e-08,-0.24749897019618497,0.018095603412635494 +-2.0,0.0,0.01,-0.47642353787354824,-0.47642353787354824,0.010000000000000016,0.9766457243008692,-0.2022062763286254,0.10857362047581295 +-2.0,0.0,0.1,-0.405694151494193,-0.405694151494193,0.10000000000000002,1.2208071553760858,-0.12419293831661506,0.2171472409516259 +-2.0,0.0,0.5,0.5406844905028039,0.5406844905028039,0.5,0.16651232599446475,-0.11108206676682202,0.7213475204444817 +-2.0,0.0,0.9,44.541643550103906,44.541643550103906,0.9,0.0010526310389469002,-79.08942123855523,4.745610790514952 +-2.0,0.0,0.99,4949.541666456223,4949.541666456223,0.99,1.0050251255854182e-06,-20296.15951123233,49.749581236711045 +-2.0,0.0,0.999999999999,5.000221224536989e+23,5.000221224536989e+23,0.999999999999,9.999336363082361e-37,-1.3566121824194262e+25,500011061104.5012 +-2.0,1e-16,1e-12,-0.4993450982742652,-0.4993450982742652,1.0000000000003948e-12,2.1095547644906154e-08,-0.24749897019618497,0.018095603412635535 +-2.0,1e-16,0.01,-0.47642353787354824,-0.47642353787354824,0.010000000000000005,0.9766457243008686,-0.2022062763286254,0.108573620475813 +-2.0,1e-16,0.1,-0.405694151494193,-0.405694151494193,0.09999999999999999,1.2208071553760855,-0.12419293831661503,0.21714724095162596 +-2.0,1e-16,0.5,0.540684490502804,0.540684490502804,0.5,0.16651232599446472,-0.11108206676682204,0.7213475204444817 +-2.0,1e-16,0.9,44.541643550103906,44.541643550103906,0.9,0.0010526310389469002,-79.08942123855523,4.745610790514952 +-2.0,1e-16,0.99,4949.541666456223,4949.541666456223,0.99,1.0050251255854182e-06,-20296.15951123233,49.749581236711045 +-2.0,1e-16,0.999999999999,5.000221224536989e+23,5.000221224536989e+23,0.999999999999,9.999336363082361e-37,-1.3566121824194262e+25,500011061104.5014 +-2.0,1e-12,1e-12,-0.4993450982742471,-0.4993450982742471,1.0000000000004546e-12,2.1095547644615866e-08,-0.2474989701961249,0.01809560341305216 +-2.0,1e-12,0.01,-0.47642353787343966,-0.47642353787343966,0.010000000000000023,0.9766457242986205,-0.20220627632845958,0.10857362047622962 +-2.0,1e-12,0.1,-0.40569415149397586,-0.40569415149397586,0.10000000000000003,1.2208071553746802,-0.12419293831643395,0.21714724095204258 +-2.0,1e-12,0.5,0.5406844905035253,0.5406844905035253,0.5,0.16651232599440702,-0.1110820667670864,0.7213475204448984 +-2.0,1e-12,0.9,44.54164355010865,44.54164355010865,0.9,0.001052631038946845,-79.0894212385659,4.74561079051537 +-2.0,1e-12,0.99,4949.541666456273,4949.541666456273,0.99,1.005025125585413e-06,-20296.15951123256,49.74958123671146 +-2.0,1e-12,0.999999999999,5.000221224536989e+23,5.000221224536989e+23,0.999999999999,9.999336363082361e-37,-1.3566121824194262e+25,500011061104.5014 +-2.0,1e-08,1e-12,-0.4993450980933092,-0.4993450980933092,9.999999999998629e-13,2.1095544730438974e-08,-0.24749896959560286,0.018095607579302505 +-2.0,1e-08,0.01,-0.476423536787812,-0.476423536787812,0.010000000000000014,0.9766457018127708,-0.20220627467051117,0.10857362464247969 +-2.0,1e-08,0.1,-0.4056941493227206,-0.4056941493227206,0.10000000000000003,1.220807141321024,-0.1241929365055366,0.21714724511829261 +-2.0,1e-08,0.5,0.5406844977162791,0.5406844977162791,0.5,0.16651232541737698,-0.1110820694106539,0.7213475246111484 +-2.0,1e-08,0.9,44.54164359756002,44.54164359756002,0.9,0.0010526310383923714,-79.0894213453489,4.745610794681619 +-2.0,1e-08,0.99,4949.541666953719,4949.541666953719,0.99,1.005025125534914e-06,-20296.159513520885,49.74958124087771 +-2.0,1e-08,0.999999999999,5.000221224536989e+23,5.000221224536989e+23,0.999999999999,9.999336363082361e-37,-1.3566121824194262e+25,500011061104.5014 +-2.0,0.2,1e-12,-0.4798398011229649,-0.4798398011229649,1.0000000000004941e-12,3.102507131465507e-08,-0.20755373154798556,0.1971489738464352 +-2.0,0.2,0.01,-0.44479331666036337,-0.44479331666036337,0.01000000000000001,0.6846482799049509,-0.16157203034197606,0.2157503391354425 +-2.0,0.2,0.1,-0.35314901552530564,-0.35314901552530564,0.10000000000000002,0.9957256918524497,-0.08661434825011166,0.31227519506631063 +-2.0,0.2,0.5,0.6935199033031064,0.6935199033031064,0.5,0.15573510188802395,-0.17245344542660423,0.808176734321404 +-2.0,0.2,0.9,45.49913419845435,45.49913419845435,0.9,0.0010416564993067615,-81.24917979043238,4.829471659965273 +-2.0,0.2,0.99,4959.499919387346,4959.499919387346,0.99,1.004016056068658e-06,-20341.97396578048,49.83296482845195 +-2.0,0.2,0.999999999999,5.000221224537989e+23,5.000221224537989e+23,0.999999999999,9.99933636308136e-37,-1.3566121824197026e+25,500011061104.5848 +-2.0,0.5,1e-12,-0.374999749999625,-0.374999749999625,1.0000000000981681e-12,7.99997600041667e-06,-0.10085642914307677,0.49999409222549773 +-2.0,0.5,0.01,-0.345679012345679,-0.345679012345679,0.01000000000000001,0.5832000000000002,-0.08213168751510509,0.459356303635525 +-2.0,0.5,0.1,-0.232645751229371,-0.232645751229371,0.1,0.8087687665751145,-0.03263651155200575,0.5000119467780961 +-2.0,0.5,0.5,0.9571067811865476,0.9571067811865476,0.5,0.14213562373095048,-0.30070731123449773,0.9517666341150135 +-2.0,0.5,0.9,46.96708245126287,46.96708245126287,0.9,0.0010256222952084674,-84.57963064754337,4.957247947632056 +-2.0,0.5,0.99,4974.468592766541,4974.468592766541,0.99,1.0025062497623697e-06,-20410.858420614142,49.9582287480373 +-2.0,0.5,0.999999999999,5.000221224539489e+23,5.000221224539489e+23,0.999999999999,9.999336363079862e-37,-1.356612182420117e+25,500011061104.7098 +-2.0,1.0,1e-12,1.0000000000015e-12,1.0000000000015e-12,1e-12,0.999999999997,-5.000000000011666e-25,0.999999999974369 +-2.0,1.0,0.01,0.010152025303540455,0.010152025303540455,0.01,0.970299,-5.118653887431955e-05,0.972842699147499 +-2.0,1.0,0.1,0.11728395061728396,0.11728395061728396,0.1,0.729,-0.006395380035695249,0.91871260727105 +-2.0,1.0,0.5,1.5,1.5,0.5,0.125,-0.6362943611198906,1.2274112777602189 +-2.0,1.0,0.9,49.50000000000002,49.50000000000002,0.9,0.0009999999999999994,-90.37925464970233,5.17553590795633 +-2.0,1.0,0.99,4999.499999999991,4999.499999999991,0.99,1.0000000000000027e-06,-20526.100929940418,50.167505033573185 +-2.0,1.0,0.999999999999,5.0002212245419894e+23,5.0002212245419894e+23,0.999999999999,9.99933636307736e-37,-1.3566121824208079e+25,500011061104.9181 +-2.0,2.0,0.01,1.500400060008001,1.500400060008001,0.009999999999999606,12.496250374987993,-0.6365716815911587,1.9985574392026408 +-2.0,2.0,0.1,1.540608101214162,1.540608101214162,0.10000000000000012,1.2128737499999984,-0.6646464981398771,1.945685398294998 +-2.0,2.0,0.5,3.0555555555555554,3.0555555555555554,0.5,0.10546875000000001,-1.959615121819471,1.9125400164505 +-2.0,2.0,0.9,54.90166204986152,54.90166204986152,0.9,0.0009526388888888882,-102.95794389925746,5.632297604409213 +-2.0,2.0,0.99,5049.877515719291,5049.877515719291,0.99,9.950251262626288e-07,-20758.224434037664,50.58794641536127 +-2.0,2.0,0.999999999999,5.000221224546989e+23,5.000221224546989e+23,0.999999999999,9.999336363072362e-37,-1.3566121824221894e+25,500011061105.3348 +-1.0,-2.0,0.01,-0.9997999799979999,-0.9997999799979999,0.009999999999998632,24.99500025000342,-0.998096391001817,0.0008212082706396635 +-1.0,-2.0,0.1,-0.9797979797979798,-0.9797979797979798,0.10000000000000005,2.4502499999999987,-0.9009702490995021,0.03688572784397604 +-1.0,-2.0,0.5,-0.3333333333333333,-0.3333333333333333,0.5,0.28125,-0.06302326126122375,0.2827974938310625 +-1.0,-2.0,0.9,7.526315789473687,7.526315789473687,0.9,0.010027777777777773,-10.74692061487805,0.4649317275811249 +-1.0,-2.0,0.99,97.50251256281398,97.50251256281398,0.99,0.00010000252525252544,-354.63210283297957,0.4966499331671549 +-1.0,-2.0,0.999999999999,1000022122207.0028,1000022122207.0028,0.999999999999,9.999557570491275e-25,-26631632375370.7,0.49999999999966666 +-1.0,-1.0,1e-12,-0.999999999999,-0.999999999999,9.999778782788784e-13,0.999999999998,-0.999999999971369,2.663102111598281e-11 +-1.0,-1.0,0.01,-0.98989898989899,-0.98989898989899,0.009999999999999955,0.9801000000000001,-0.9434836378774284,0.036885727843976035 +-1.0,-1.0,0.1,-0.8888888888888888,-0.8888888888888888,0.10000000000000003,0.8099999999999999,-0.6447528247404201,0.17315865345605502 +-1.0,-1.0,0.5,-0.0,-0.0,0.5,0.25,0.0,0.38629436111989063 +-1.0,-1.0,0.9,8.000000000000002,8.000000000000002,0.9,0.009999999999999997,-11.77502119602598,0.4824464092043671 +-1.0,-1.0,0.99,97.99999999999991,97.99999999999991,0.99,0.00010000000000000017,-356.916865163324,0.4983249496642677 +-1.0,-1.0,0.999999999999,1000022122207.5028,1000022122207.5028,0.999999999999,9.999557570491275e-25,-26631632375384.516,0.49999999999983336 +-1.0,-0.5,1e-12,-0.9999994999995,-0.9999994999995,1.0000000000112679e-12,3.9999920000265354e-06,-0.9999922456638765,1.2815537189025837e-05 +-1.0,-0.5,0.01,-0.9444444444444444,-0.9444444444444444,0.010000000000000007,0.3240000000000001,-0.7838682356724352,0.17315865345605502 +-1.0,-0.5,0.1,-0.7687623522128678,-0.7687623522128678,0.1,0.5914021704740869,-0.4301589096566586,0.3162114282665426 +-1.0,-0.5,0.5,0.20710678118654752,0.20710678118654752,0.5,0.24264068711928516,-0.02010259044920825,0.4424680139496164 +-1.0,-0.5,0.9,8.24341649025257,8.24341649025257,0.9,0.009993065183904873,-12.313124364163565,0.49122076916332735 +-1.0,-0.5,0.99,98.2493718553309,98.2493718553309,0.99,9.999936869483987e-05,-358.0630725347801,0.49916247271719155 +-1.0,-0.5,0.999999999999,1000022122207.7528,1000022122207.7528,0.999999999999,9.999557570491275e-25,-26631632375391.42,0.4999999999999167 +-1.0,-0.2,1e-12,-0.9992006032029227,-0.9992006032029227,9.999999999997924e-13,6.229815605565557e-09,-0.9934995825422444,0.01817945177560048 +-1.0,-0.2,0.01,-0.8677149316034901,-0.8677149316034901,0.01000000000000001,0.22749840051576947,-0.6001292143462404,0.3507070896186299 +-1.0,-0.2,0.1,-0.658057227237609,-0.658057227237609,0.09999999999999998,0.5396263423532668,-0.2911143731039772,0.4237856637988153 +-1.0,-0.2,0.5,0.3450047917745151,0.3450047917745151,0.5,0.24061147866448934,-0.05365136781917817,0.47690988492458153 +-1.0,-0.2,0.9,8.391572780149703,8.391572780149703,0.9,0.009991124125016761,-12.643791904706191,0.49648803479236114 +-1.0,-0.2,0.99,98.39919597453934,98.39919597453934,0.99,9.999919192996633e-05,-358.7520222659103,0.49966498885000227 +-1.0,-0.2,0.999999999999,1000022122207.9028,1000022122207.9028,0.999999999999,9.999557570491275e-25,-26631632375395.566,0.4999999999999667 +-1.0,-1e-08,1e-12,-0.9638087981747288,-0.9638087981747288,1.0000000000000232e-12,7.634733279089107e-10,-0.8436923985405,0.49999995394829816 +-1.0,-1e-08,0.01,-0.782852764048374,-0.782852764048374,0.010000000000000009,0.21207592441913606,-0.45122992450255,0.49999999232471637 +-1.0,-1e-08,0.1,-0.5657055230967482,-0.5657055230967482,0.1,0.5301898110478398,-0.20348983356744718,0.4999999961623582 +-1.0,-1e-08,0.5,0.4426950358889634,0.4426950358889634,0.5,0.24022650695910072,-0.08607133022336962,0.4999999988447547 +-1.0,-1e-08,0.9,8.491221576029906,8.491221576029906,0.9,0.00999075443371475,-12.86751334995073,0.49999999982439913 +-1.0,-1e-08,0.99,98.49916246842209,98.49916246842209,0.99,9.999915826049495e-05,-359.21183282060696,0.49999999998324945 +-1.0,-1e-08,0.999999999999,1000022122208.0028,1000022122208.0028,0.999999999999,9.999557570491275e-25,-26631632375398.332,0.5 +-1.0,-1e-12,1e-12,-0.963808793175229,-0.963808793175229,9.999999999999725e-13,7.634733279088698e-10,-0.8436923819474644,0.49999999999539485 +-1.0,-1e-12,0.01,-0.7828527590488741,-0.7828527590488741,0.010000000000000002,0.21207592441913595,-0.4512299168674155,0.49999999999923245 +-1.0,-1e-12,0.1,-0.5657055180972481,-0.5657055180972481,0.10000000000000003,0.5301898110478398,-0.20348982939770197,0.49999999999961625 +-1.0,-1e-12,0.5,0.4426950408884634,0.4426950408884634,0.5,0.24022650695910072,-0.08607133205575095,0.4999999999998845 +-1.0,-1e-12,0.9,8.491221581029405,8.491221581029405,0.9,0.009990754433714751,-12.867513361201441,0.49999999999998246 +-1.0,-1e-12,0.99,98.49916247342158,98.49916247342158,0.99,9.999915826049496e-05,-359.2118328436054,0.49999999999999833 +-1.0,-1e-12,0.999999999999,1000022122208.0028,1000022122208.0028,0.999999999999,9.999557570491275e-25,-26631632375398.332,0.5 +-1.0,-1e-16,1e-12,-0.9638087931747291,-0.9638087931747291,9.999999999999598e-13,7.634733279088608e-10,-0.8436923819458052,0.49999999999999956 +-1.0,-1e-16,0.01,-0.7828527590483741,-0.7828527590483741,0.009999999999999998,0.2120759244191359,-0.451229916866652,0.49999999999999994 +-1.0,-1e-16,0.1,-0.5657055180967482,-0.5657055180967482,0.10000000000000002,0.5301898110478398,-0.203489829397285,0.49999999999999994 +-1.0,-1e-16,0.5,0.44269504088896333,0.44269504088896333,0.5,0.24022650695910072,-0.08607133205593419,0.5 +-1.0,-1e-16,0.9,8.491221581029905,8.491221581029905,0.9,0.009990754433714753,-12.867513361202565,0.5 +-1.0,-1e-16,0.99,98.49916247342209,98.49916247342209,0.99,9.999915826049495e-05,-359.21183284360774,0.5 +-1.0,-1e-16,0.999999999999,1000022122208.0028,1000022122208.0028,0.999999999999,9.999557570491275e-25,-26631632375398.332,0.5 +-1.0,0.0,1e-12,-0.963808793174729,-0.963808793174729,1.0000000000000064e-12,7.634733279088938e-10,-0.843692381945805,0.5 +-1.0,0.0,0.01,-0.782852759048374,-0.782852759048374,0.01000000000000001,0.21207592441913606,-0.45122991686665187,0.5 +-1.0,0.0,0.1,-0.5657055180967482,-0.5657055180967482,0.09999999999999999,0.5301898110478398,-0.20348982939728497,0.5 +-1.0,0.0,0.5,0.4426950408889634,0.4426950408889634,0.5,0.24022650695910072,-0.0860713320559342,0.5 +-1.0,0.0,0.9,8.491221581029905,8.491221581029905,0.9,0.009990754433714753,-12.867513361202565,0.5 +-1.0,0.0,0.99,98.49916247342209,98.49916247342209,0.99,9.999915826049495e-05,-359.21183284360774,0.5 +-1.0,0.0,0.999999999999,1000022122208.0028,1000022122208.0028,0.999999999999,9.999557570491275e-25,-26631632375398.332,0.4999999999999998 +-1.0,1e-16,1e-12,-0.963808793174729,-0.963808793174729,9.999999999999683e-13,7.634733279088668e-10,-0.8436923819458049,0.5000000000000004 +-1.0,1e-16,0.01,-0.782852759048374,-0.782852759048374,0.01,0.21207592441913592,-0.4512299168666518,0.5000000000000001 +-1.0,1e-16,0.1,-0.5657055180967481,-0.5657055180967481,0.10000000000000003,0.5301898110478398,-0.2034898293972849,0.5 +-1.0,1e-16,0.5,0.44269504088896344,0.44269504088896344,0.5,0.24022650695910072,-0.08607133205593423,0.5 +-1.0,1e-16,0.9,8.491221581029905,8.491221581029905,0.9,0.009990754433714753,-12.867513361202567,0.5 +-1.0,1e-16,0.99,98.49916247342209,98.49916247342209,0.99,9.999915826049495e-05,-359.21183284360774,0.5 +-1.0,1e-16,0.999999999999,1000022122208.0028,1000022122208.0028,0.999999999999,9.999557570491275e-25,-26631632375398.332,0.5 +-1.0,1e-12,1e-12,-0.963808793174229,-0.963808793174229,1.0000000000000404e-12,7.634733279089178e-10,-0.8436923819441455,0.5000000000046052 +-1.0,1e-12,0.01,-0.7828527590478741,-0.7828527590478741,0.009999999999999997,0.2120759244191359,-0.4512299168658883,0.5000000000007675 +-1.0,1e-12,0.1,-0.5657055180962481,-0.5657055180962481,0.10000000000000002,0.5301898110478398,-0.20348982939686794,0.5000000000003838 +-1.0,1e-12,0.5,0.44269504088946343,0.44269504088946343,0.5,0.2402265069591007,-0.08607133205611746,0.5000000000001156 +-1.0,1e-12,0.9,8.491221581030405,8.491221581030405,0.9,0.009990754433714751,-12.867513361203692,0.5000000000000175 +-1.0,1e-12,0.99,98.49916247342259,98.49916247342259,0.99,9.999915826049495e-05,-359.21183284361,0.5000000000000017 +-1.0,1e-12,0.999999999999,1000022122208.0028,1000022122208.0028,0.999999999999,9.999557570491275e-25,-26631632375398.332,0.5 +-1.0,1e-08,1e-12,-0.9638087881747288,-0.9638087881747288,9.99999999999977e-13,7.634733279088778e-10,-0.8436923653511091,0.5000000460517019 +-1.0,1e-08,0.01,-0.7828527540483741,-0.7828527540483741,0.009999999999999997,0.21207592441913592,-0.45122990923075373,0.5000000076752836 +-1.0,1e-08,0.1,-0.5657055130967481,-0.5657055130967481,0.10000000000000003,0.5301898110478398,-0.20348982522712275,0.5000000038376419 +-1.0,1e-08,0.5,0.4426950458889634,0.4426950458889634,0.5,0.24022650695910072,-0.08607133388849882,0.5000000011552453 +-1.0,1e-08,0.9,8.491221586029905,8.491221586029905,0.9,0.009990754433714751,-12.867513372454402,0.5000000001756009 +-1.0,1e-08,0.99,98.49916247842208,98.49916247842208,0.99,9.999915826049496e-05,-359.21183286660846,0.5000000000167506 +-1.0,1e-08,0.999999999999,1000022122208.0028,1000022122208.0028,0.999999999999,9.999557570491275e-25,-26631632375398.332,0.5 +-1.0,0.2,1e-12,-0.7992006032029226,-0.7992006032029226,1.0000000000001381e-12,6.229815605567277e-09,-0.4768274334647155,0.9818205482243996 +-1.0,0.2,0.01,-0.6677149316034902,-0.6677149316034902,0.009999999999999998,0.2274984005157693,-0.3016158572049699,0.64929291038137 +-1.0,0.2,0.1,-0.4580572272376089,-0.4580572272376089,0.10000000000000002,0.5396263423532669,-0.12606586566267292,0.5762143362011847 +-1.0,0.2,0.5,0.5450047917745151,0.5450047917745151,0.5,0.24061147866448934,-0.12711402603421793,0.5230901150754185 +-1.0,0.2,0.9,8.591572780149702,8.591572780149702,0.9,0.009991124125016761,-13.093869070407026,0.5035119652076389 +-1.0,0.2,0.99,98.59919597453934,98.59919597453934,0.99,9.999919192996633e-05,-359.6720521449354,0.5003350111499978 +-1.0,0.2,0.999999999999,1000022122208.1028,1000022122208.1028,0.999999999999,9.999557570491277e-25,-26631632375401.094,0.5000000000000333 +-1.0,0.5,1e-12,-0.4999994999995,-0.4999994999995,1.0000000000112679e-12,3.9999920000265354e-06,-0.1534260631463405,0.999987184462811 +-1.0,0.5,0.01,-0.4444444444444444,-0.4444444444444444,0.010000000000000007,0.3240000000000001,-0.11789629727660055,0.8268413465439449 +-1.0,0.5,0.1,-0.2687623522128678,-0.2687623522128678,0.1,0.5914021704740869,-0.03987270377660082,0.6837885717334574 +-1.0,0.5,0.5,0.7071067811865476,0.7071067811865476,0.5,0.24264068711928513,-0.20585391982611656,0.5575319860503837 +-1.0,0.5,0.9,8.74341649025257,8.74341649025257,0.9,0.009993065183904873,-13.43836584067467,0.5087792308366726 +-1.0,0.5,0.99,98.7493718553309,98.7493718553309,0.99,9.999936869483987e-05,-360.3631476742579,0.5008375272828085 +-1.0,0.5,0.999999999999,1000022122208.2528,1000022122208.2528,0.999999999999,9.999557570491275e-25,-26631632375405.24,0.5000000000000834 +-1.0,1.0,1e-12,1.000000000001e-12,1.000000000001e-12,1e-12,0.999999999998,-5.000000000008333e-25,0.999999999973369 +-1.0,1.0,0.01,0.010101010101010102,0.010101010101010102,0.01,0.9801,-5.0844296466102206e-05,0.963114272156024 +-1.0,1.0,0.1,0.11111111111111112,0.11111111111111112,0.1,0.8099999999999999,-0.005956128508695891,0.8268413465439449 +-1.0,1.0,0.5,1.0,1.0,0.5,0.25,-0.38629436111989063,0.6137056388801094 +-1.0,1.0,0.9,9.000000000000002,9.000000000000002,0.9,0.009999999999999997,-14.025850929940463,0.5175535907956329 +-1.0,1.0,0.99,98.99999999999991,98.99999999999991,0.99,0.00010000000000000017,-361.5170185988087,0.5016750503357322 +-1.0,1.0,0.999999999999,1000022122208.5028,1000022122208.5028,0.999999999999,9.999557570491275e-25,-26631632375412.145,0.5000000000001666 +-1.0,2.0,0.01,1.0002000200020003,1.0002000200020003,0.010000000000001405,24.995000249996483,-0.3864330144219995,0.9991787917293603 +-1.0,2.0,0.1,1.02020202020202,1.02020202020202,0.09999999999999977,2.450250000000006,-0.40039902305746816,0.963114272156024 +-1.0,2.0,0.5,1.6666666666666667,1.6666666666666667,0.5,0.28125,-0.94887800803127,0.7172025061689375 +-1.0,2.0,0.9,9.526315789473687,9.526315789473687,0.9,0.010027777777777773,-15.25135144612207,0.5350682724188751 +-1.0,2.0,0.99,99.50251256281398,99.50251256281398,0.99,0.00010000252525252544,-363.83243495594286,0.5033500668328451 +-1.0,2.0,0.999999999999,1000022122209.0028,1000022122209.0028,0.999999999999,9.999557570491275e-25,-26631632375425.96,0.5000000000003333 +-0.5,-2.0,1e-12,-1.9999999999971716,-1.9999999999971716,9.999918869356964e-13,0.3535533905932738,-3.999999999839999,3.836905802317698e-11 +-0.5,-2.0,0.01,-1.9717143144329008,-1.9717143144329008,0.010000000000000026,0.3535003589105321,-3.702516809049159,0.05806529021130481 +-0.5,-2.0,0.1,-1.7157323781925193,-1.7157323781925193,0.10000000000000003,0.34826337016688963,-2.322260265247268,0.25951409878791465 +-0.5,-2.0,0.5,-0.36700683814454793,-0.36700683814454793,0.5,0.22963966338592295,-0.07189192737748182,0.34635478021198834 +-0.5,-2.0,0.9,3.839971160707452,3.839971160707452,0.9,0.02928096651410263,-4.836034830868241,0.15922398066253574 +-0.5,-2.0,0.99,17.84968640183658,17.84968640183658,0.99,0.0009925093828271863,-55.41231656870104,0.050041086102116224 +-0.5,-2.0,0.999999999999,2000020.1220856563,2000020.1220856563,0.999999999999,9.999668176025823e-19,-51262657.487917684,4.999944695394253e-07 +-0.5,-1.0,1e-12,-1.999998,-1.999998,1.0000000000565114e-12,1.0000000000267556e-06,-3.9999407379577683,2.6631021115969494e-05 +-0.5,-1.0,0.01,-1.7989924369481576,-1.7989924369481576,0.01,0.09850375627355537,-2.674331030889614,0.36700835813290006 +-0.5,-1.0,0.1,-1.3333333333333333,-1.3333333333333333,0.10000000000000002,0.27,-1.2018502817758536,0.5194759603681651 +-0.5,-1.0,0.5,-0.0,-0.0,0.5,0.25,0.0,0.38629436111989063 +-0.5,-1.0,0.9,4.000000000000001,4.000000000000001,0.9,0.02999999999999999,-5.183347464017318,0.16081546973478902 +-0.5,-1.0,0.99,17.89974874213239,17.89974874213239,0.99,0.000994987437106621,-55.642232973398585,0.05008354186997327 +-0.5,-1.0,0.999999999999,2000020.1220861562,2000020.1220861562,0.999999999999,9.999668176028325e-19,-51262657.4879315,4.99994469539467e-07 +-0.5,-0.5,1e-12,-1.9985857857305196,-1.9985857857305196,1.0000000000000018e-12,2.8284228821065673e-09,-3.976653222070426,0.01812389743986159 +-0.5,-0.5,0.01,-1.5285954792089682,-1.5285954792089682,0.010000000000000009,0.07636753236814717,-1.6946566449789633,0.7346499484794474 +-0.5,-0.5,0.1,-1.0382564836982113,-1.0382564836982113,0.10000000000000002,0.2843886014901291,-0.6682229739967366,0.6575795373853449 +-0.5,-0.5,0.5,0.19736822693561992,0.19736822693561992,0.5,0.2665854682188721,-0.018866271153349688,0.40272541354315317 +-0.5,-0.5,0.9,4.0805975003292465,4.0805975003292465,0.9,0.0303819035889396,-5.3615161166638154,0.16156990135812446 +-0.5,-0.5,0.99,17.924795793717024,17.924795793717024,0.99,0.0009962335003726517,-55.757358689868575,0.05010465129831792 +-0.5,-0.5,0.999999999999,2000020.1220864062,2000020.1220864062,0.999999999999,9.999668176029573e-19,-51262657.487938404,4.999944695394878e-07 +-0.5,-0.2,1e-12,-1.943452787970499,-1.943452787970499,1.0000000000000042e-12,1.7613935197633456e-10,-3.4836304749366165,0.6429831329656419 +-0.5,-0.2,0.01,-1.2725797132427228,-1.2725797132427228,0.010000000000000004,0.08274347587000143,-1.073736524877332,0.9642488558630274 +-0.5,-0.2,0.1,-0.8304825392284371,-0.8304825392284371,0.09999999999999999,0.3155512148372194,-0.4059419892195424,0.7247188315071965 +-0.5,-0.2,0.5,0.3194868327063339,0.3194868327063339,0.5,0.279047578280142,-0.04851660868136318,0.41122017008230394 +-0.5,-0.2,0.9,4.129134614331685,4.129134614331685,0.9,0.030618472355362303,-5.469844776644423,0.162009179446452 +-0.5,-0.2,0.99,17.939829083975553,17.939829083975553,0.99,0.000996983397809598,-55.826487619995426,0.05011727901434753 +-0.5,-0.2,0.999999999999,2000020.1220865562,2000020.1220865562,0.999999999999,9.999668176030324e-19,-51262657.48794255,4.999944695395003e-07 +-0.5,-1e-08,1e-12,-1.6195202931809833,-1.6195202931809833,1.0000000000000018e-12,1.4524305398345776e-10,-1.9762515599672985,2.6282608243605163 +-0.5,-1e-08,0.01,-1.068018807160518,-1.068018807160518,0.010000000000000007,0.09882538650634112,-0.7127349035207743,1.072983009026949 +-0.5,-1e-08,0.1,-0.681979549622614,-0.681979549622614,0.10000000000000002,0.3494005067713875,-0.26468726495589356,0.7587135632367377 +-0.5,-1e-08,0.5,0.4022448134101265,0.4022448134101265,0.5,0.28854144019306566,-0.07596412736930157,0.4162773053383977 +-0.5,-1e-08,0.9,4.16156524789924,4.16156524789924,0.9,0.030779342659536024,-5.5426546199318505,0.16229642297300093 +-0.5,-1e-08,0.99,17.949853379754156,17.949853379754156,0.99,0.000997484272697853,-55.87259583867138,0.050125681674499704 +-0.5,-1e-08,0.999999999999,2000020.122086656,2000020.122086656,0.999999999999,9.999668176030825e-19,-51262657.48794531,4.999944695395087e-07 +-0.5,-1e-12,1e-12,-1.6195202669010031,-1.6195202669010031,9.999999999999883e-13,1.4524306401548698e-10,-1.9762514727456426,2.628260884872414 +-0.5,-1e-12,0.01,-1.068018796431761,-1.068018796431761,0.009999999999999993,0.09882538764399657,-0.712734887136035,1.072983013144262 +-0.5,-1e-12,0.1,-0.6819795420362371,-0.6819795420362371,0.09999999999999999,0.34940050878249734,-0.26468725862860903,0.7587135646924276 +-0.5,-1e-12,0.5,0.4022448175724833,0.4022448175724833,0.5,0.2885414406930199,-0.07596412889485911,0.4162773055788248 +-0.5,-1e-12,0.9,4.161565249522042,4.161565249522042,0.9,0.03077934266764254,-5.542654623583751,0.1622964229872492 +-0.5,-1e-12,0.99,17.949853380255362,17.949853380255362,0.99,0.0009974842727229134,-55.872595840977006,0.05012568167491948 +-0.5,-1e-12,0.999999999999,2000020.122086656,2000020.122086656,0.999999999999,9.999668176030825e-19,-51262657.48794531,4.999944695395087e-07 +-0.5,-1e-16,1e-12,-1.619520266898375,-1.619520266898375,1.000000000000006e-12,1.452430640164926e-10,-1.9762514727369205,2.628260884878465 +-0.5,-1e-16,0.01,-1.068018796430688,-1.068018796430688,0.009999999999999998,0.09882538764411036,-0.7127348871343964,1.0729830131446736 +-0.5,-1e-16,0.1,-0.6819795420354784,-0.6819795420354784,0.10000000000000002,0.3494005087826985,-0.2646872586279763,0.7587135646925731 +-0.5,-1e-16,0.5,0.40224481757289954,0.40224481757289954,0.5,0.28854144069306986,-0.07596412889501167,0.4162773055788489 +-0.5,-1e-16,0.9,4.161565249522204,4.161565249522204,0.9,0.030779342667643345,-5.542654623584116,0.1622964229872506 +-0.5,-1e-16,0.99,17.949853380255412,17.949853380255412,0.99,0.0009974842727229157,-55.87259584097723,0.05012568167491952 +-0.5,-1e-16,0.999999999999,2000020.122086656,2000020.122086656,0.999999999999,9.999668176030825e-19,-51262657.48794531,4.999944695395087e-07 +-0.5,0.0,1e-12,-1.6195202668983748,-1.6195202668983748,1.0000000000000002e-12,1.452430640164919e-10,-1.9762514727369196,2.628260884878466 +-0.5,0.0,0.01,-1.0680187964306878,-1.0680187964306878,0.010000000000000009,0.09882538764411045,-0.7127348871343963,1.0729830131446736 +-0.5,0.0,0.1,-0.6819795420354784,-0.6819795420354784,0.09999999999999999,0.3494005087826985,-0.2646872586279762,0.7587135646925731 +-0.5,0.0,0.5,0.4022448175728996,0.4022448175728996,0.5,0.2885414406930699,-0.07596412889501168,0.4162773055788489 +-0.5,0.0,0.9,4.161565249522204,4.161565249522204,0.9,0.030779342667643345,-5.542654623584116,0.1622964229872506 +-0.5,0.0,0.99,17.949853380255412,17.949853380255412,0.99,0.0009974842727229157,-55.87259584097723,0.05012568167491952 +-0.5,0.0,0.999999999999,2000020.122086656,2000020.122086656,0.999999999999,9.999668176030825e-19,-51262657.48794531,4.999944695395085e-07 +-0.5,1e-16,1e-12,-1.6195202668983746,-1.6195202668983746,9.999999999999941e-13,1.4524306401649118e-10,-1.9762514727369187,2.6282608848784665 +-0.5,1e-16,0.01,-1.0680187964306878,-1.0680187964306878,0.009999999999999998,0.09882538764411039,-0.7127348871343961,1.0729830131446736 +-0.5,1e-16,0.1,-0.6819795420354783,-0.6819795420354783,0.1,0.3494005087826985,-0.26468725862797615,0.7587135646925732 +-0.5,1e-16,0.5,0.40224481757289965,0.40224481757289965,0.5,0.2885414406930699,-0.0759641288950117,0.4162773055788489 +-0.5,1e-16,0.9,4.161565249522204,4.161565249522204,0.9,0.030779342667643345,-5.542654623584116,0.1622964229872506 +-0.5,1e-16,0.99,17.949853380255412,17.949853380255412,0.99,0.0009974842727229157,-55.87259584097723,0.05012568167491952 +-0.5,1e-16,0.999999999999,2000020.122086656,2000020.122086656,0.999999999999,9.999668176030825e-19,-51262657.48794531,4.999944695395087e-07 +-0.5,1e-12,1e-12,-1.6195202668957465,-1.6195202668957465,1.0000000000000119e-12,1.4524306401749683e-10,-1.9762514727281966,2.6282608848845177 +-0.5,1e-12,0.01,-1.0680187964296148,-1.0680187964296148,0.010000000000000004,0.09882538764422419,-0.7127348871327576,1.0729830131450855 +-0.5,1e-12,0.1,-0.6819795420347197,-0.6819795420347197,0.1,0.34940050878289963,-0.26468725862734344,0.7587135646927188 +-0.5,1e-12,0.5,0.40224481757331587,0.40224481757331587,0.5,0.2885414406931199,-0.07596412889516425,0.41627730557887294 +-0.5,1e-12,0.9,4.161565249522367,4.161565249522367,0.9,0.030779342667644153,-5.542654623584481,0.16229642298725203 +-0.5,1e-12,0.99,17.949853380255462,17.949853380255462,0.99,0.0009974842727229183,-55.87259584097747,0.05012568167491956 +-0.5,1e-12,0.999999999999,2000020.122086656,2000020.122086656,0.999999999999,9.999668176030825e-19,-51262657.48794531,4.999944695395087e-07 +-0.5,1e-08,1e-12,-1.6195202406157656,-1.6195202406157656,1.0000000000000073e-12,1.4524307404952934e-10,-1.9762513855065424,2.628260945396403 +-0.5,1e-08,0.01,-1.0680187857008576,-1.0680187857008576,0.010000000000000007,0.09882538878187978,-0.7127348707480183,1.072983017262398 +-0.5,1e-08,0.1,-0.6819795344483427,-0.6819795344483427,0.1,0.3494005107940095,-0.26468725230005896,0.7587135661484086 +-0.5,1e-08,0.5,0.40224482173567266,0.40224482173567266,0.5,0.2885414411930741,-0.0759641304207218,0.41627730581930006 +-0.5,1e-08,0.9,4.161565251145168,4.161565251145168,0.9,0.03077934267575067,-5.542654627236382,0.1622964230015003 +-0.5,1e-08,0.99,17.94985338075667,17.94985338075667,0.99,0.000997484272747978,-55.872595843283094,0.05012568167533934 +-0.5,1e-08,0.999999999999,2000020.122086656,2000020.122086656,0.999999999999,9.999668176030825e-19,-51262657.48794531,4.999944695395087e-07 +-0.5,0.2,1e-12,-1.1037870860178876,-1.1037870860178876,9.999999999998483e-13,2.791620598717698e-09,-0.7687501394481732,2.1910430722581524 +-0.5,0.2,0.01,-0.8471165394602803,-0.8471165394602803,0.01,0.13113957162693565,-0.4240298470097417,1.1263808227024181 +-0.5,0.2,0.1,-0.5276647490976912,-0.5276647490976912,0.10000000000000002,0.39725544308109617,-0.15338447888954776,0.7827216469184669 +-0.5,0.2,0.5,0.48596443399700723,0.48596443399700723,0.5,0.29907578918567507,-0.10953281121123551,0.4208347536447886 +-0.5,0.2,0.9,4.194052883258167,4.194052883258167,0.9,0.030942775596775145,-5.615934727984483,0.16257916252816484 +-0.5,0.2,0.99,17.95987935580166,17.95987935580166,0.99,0.0009979859032998918,-55.91872186243296,0.05013407167759932 +-0.5,0.2,0.999999999999,2000020.1220867562,2000020.1220867562,0.999999999999,9.999668176031323e-19,-51262657.487948075,4.999944695395169e-07 +-0.5,0.5,1e-12,-0.5857857305195935,-0.5857857305195935,9.999999998593968e-13,2.8284228819077214e-06,-0.19131424165617694,1.4141947313686973 +-0.5,0.5,0.01,-0.5092880150001402,-0.5092880150001402,0.01,0.2414953415699773,-0.14235540400759517,1.109324074487833 +-0.5,0.5,0.1,-0.2897513072220962,-0.2897513072220962,0.1,0.505722394479661,-0.04416608846915741,0.7996364208561982 +-0.5,0.5,0.5,0.613125929752753,0.613125929752753,0.5,0.3170253355622144,-0.171247879206353,0.4267165081501723 +-0.5,0.5,0.9,4.242889231838916,4.242889231838916,0.9,0.031192799514832045,-5.726732126963125,0.16299479678155554 +-0.5,0.5,0.99,17.974921462206645,17.974921462206645,0.99,0.000998739767974836,-55.98794424501276,0.0501466329397503 +-0.5,0.5,0.999999999999,2000020.122086906,2000020.122086906,0.999999999999,9.999668176032074e-19,-51262657.48795222,4.999944695395294e-07 +-0.5,1.0,1e-12,1.00000000000075e-12,1.00000000000075e-12,1e-12,0.9999999999985,-5.000000000006667e-25,0.9999999999728689 +-0.5,1.0,0.01,0.010075630518424151,0.010075630518424151,0.01,0.9850375627355538,-5.067414080053197e-05,0.95828660129333 +-0.5,1.0,0.1,0.10818510677891956,0.10818510677891956,0.1,0.8538149682454624,-0.005749256394537458,0.7844105756038375 +-0.5,1.0,0.5,0.8284271247461901,0.8284271247461901,0.5,0.3535533905932738,-0.3036620374447142,0.43395541890454786 +-0.5,1.0,0.9,4.324555320336759,4.324555320336759,0.9,0.031622776601683784,-5.913716159750086,0.16366481581129566 +-0.5,1.0,0.99,17.999999999999993,17.999999999999993,0.99,0.001000000000000001,-56.103403719761786,0.05016750503357325 +-0.5,1.0,0.999999999999,2000020.1220871562,2000020.1220871562,0.999999999999,9.999668176033322e-19,-51262657.48795913,4.999944695395503e-07 +-0.5,2.0,0.01,0.8285685567099131,0.8285685567099131,0.010000000000001915,35.35003589104644,-0.30376007768362057,0.7064907720614475 +-0.5,2.0,0.1,0.8426762180748057,0.8426762180748057,0.09999999999999985,3.4826337016689015,-0.3136104203681614,0.6776109540947229 +-0.5,2.0,0.5,1.2659863237109041,1.2659863237109041,0.5,0.4592793267718459,-0.6714022788100718,0.4391950455898004 +-0.5,2.0,0.9,4.488856845230502,4.488856845230502,0.9,0.03253440723789182,-6.296266196340204,0.16491911755216662 +-0.5,2.0,0.99,18.05018828468341,18.05018828468341,0.99,0.0010025347301284712,-56.33465515005183,0.050209011475205 +-0.5,2.0,0.999999999999,2000020.122087656,2000020.122087656,0.999999999999,9.999668176035824e-19,-51262657.487972945,4.99994469539592e-07 +-0.2,-2.0,1e-12,-4.999908971789848,-4.999908971789848,1.0000000000069173e-12,2.7464013582766947e-08,-24.994577550102786,0.0004939376583520418 +-0.2,-2.0,0.01,-4.089699691752511,-4.089699691752511,0.01,0.027460717933980725,-12.695385905795568,0.7474713872844831 +-0.2,-2.0,0.1,-2.708874048703403,-2.708874048703403,0.09999999999999999,0.10802548845467638,-4.604459399004484,0.83664749812839 +-0.2,-2.0,0.5,-0.38946044259136114,-0.38946044259136114,0.5,0.2033384570995685,-0.07788929287514375,0.3911547096132417 +-0.2,-2.0,0.9,2.675775906303258,2.675775906303258,0.9,0.055694695261874684,-3.0715160655912745,0.08371052259286395 +-0.2,-2.0,0.99,7.521589614853103,7.521589614853103,0.99,0.0039334063417616515,-19.867175730777824,0.01262677770051326 +-0.2,-2.0,0.999999999999,1250.948772553031,1250.948772553031,0.999999999999,3.980966023979116e-15,-28448.43097622485,1.2559209888068442e-10 +-0.2,-1.0,1e-12,-4.980094641472321,-4.980094641472321,1.000000000000012e-12,2.511886431506591e-10,-24.3504678255632,0.1060200046542587 +-0.2,-1.0,0.01,-3.005459012516379,-3.005459012516379,0.009999999999999998,0.024817740177127588,-5.862140179088862,1.4566879015519258 +-0.2,-1.0,0.1,-1.7780299251137286,-1.7780299251137286,0.10000000000000002,0.13966610165238239,-1.810757789586709,1.0042415993574207 +-0.2,-1.0,0.5,-0.0,-0.0,0.5,0.25,0.0,0.38629436111989063 +-0.2,-1.0,0.9,2.7592278695767987,2.7592278695767987,0.9,0.05799546134795287,-3.252626828302304,0.08318692497501727 +-0.2,-1.0,0.99,7.534212210670499,7.534212210670499,0.99,0.0039491911552175735,-19.925146281698865,0.012618405391845722 +-0.2,-1.0,0.999999999999,1250.9487725531565,1250.9487725531565,0.999999999999,3.98096602398071e-15,-28448.43097622832,1.2559209888067605e-10 +-0.2,-0.5,1e-12,-4.72535980924541,-4.72535980924541,9.999999999999961e-13,3.6411240366984985e-11,-19.642138791966037,1.4078632234220054 +-0.2,-0.5,0.01,-2.1951121363845014,-2.1951121363845014,0.009999999999999995,0.03208684424338805,-2.8683920168625594,1.7484861800120064 +-0.2,-0.5,0.1,-1.2693739975989697,-1.2693739975989697,0.10000000000000002,0.18328619206081942,-0.884079567009639,1.0203066739664157 +-0.2,-0.5,0.5,0.1918142040958434,0.1918142040958434,0.5,0.2820721305689135,-0.018165510163639955,0.3806144999738317 +-0.2,-0.5,0.9,2.8007526755245618,2.8007526755245618,0.9,0.05920586599219653,-3.344420731436523,0.08291072318043638 +-0.2,-0.5,0.99,7.540520362561379,7.540520362561379,0.99,0.0039571222891684535,-19.954140931172304,0.012614200042416094 +-0.2,-0.5,0.999999999999,1250.9487725532192,1250.9487725532192,0.999999999999,3.980966023981507e-15,-28448.430976230055,1.2559209888067186e-10 +-0.2,-0.2,1e-12,-3.799056751114768,-3.799056751114768,9.999999999999994e-13,2.0734096494963703e-11,-10.430573089696008,5.4622410192694195 +-0.2,-0.2,0.01,-1.6636456554463588,-1.6636456554463588,0.010000000000000005,0.04510108694157815,-1.5694638011400883,1.7690327960638528 +-0.2,-0.2,0.1,-0.9657696701894936,-0.9657696701894936,0.09999999999999999,0.2286945869158772,-0.499667829030774,0.9999620488683694 +-0.2,-0.2,0.5,0.30535892461763214,0.30535892461763214,0.5,0.304996887447171,-0.045700900413859526,0.37623332343445975 +-0.2,-0.2,0.9,2.825600442261835,2.825600442261835,0.9,0.05995218518240886,-3.3998776267602158,0.08274049673257983 +-0.2,-0.2,0.99,7.544304243288285,7.544304243288285,0.99,0.003961893415196987,-19.9715406898841,0.012611670704980722 +-0.2,-0.2,0.999999999999,1250.948772553257,1250.948772553257,0.999999999999,3.980966023981983e-15,-28448.430976231095,1.2559209888066935e-10 +-0.2,-1e-08,1e-12,-2.425513579700373,-2.425513579700373,1.0000000000000087e-12,5.366316380753468e-11,-3.5830039128518134,7.113569191788981 +-0.2,-1e-08,0.01,-1.3159894479984053,-1.3159894479984053,0.010000000000000005,0.06250212933630245,-0.9538012988863946,1.6965495689158265 +-0.2,-1e-08,0.1,-0.7681831293799309,-0.7681831293799309,0.1,0.2720563220103577,-0.31144302573425403,0.9744118479964919 +-0.2,-1e-08,0.5,0.3802804219656994,0.3802804219656994,0.5,0.3220776259764343,-0.07054016252786978,0.37293262094156837 +-0.2,-1e-08,0.9,2.8421370316864176,2.8421370316864176,0.9,0.060458305981487215,-3.437003789850773,0.08262516016629065 +-0.2,-1e-08,0.99,7.5468264084596814,7.5468264084596814,0.99,0.003965079363717736,-19.983141758446862,0.012609981930271342 +-0.2,-1e-08,0.999999999999,1250.948772553282,1250.948772553282,0.999999999999,3.9809660239823036e-15,-28448.43097623179,1.2559209888066767e-10 +-0.2,-1e-12,1e-12,-2.4255135085717954,-2.4255135085717954,1.0000000000000087e-12,5.3663169738013754e-11,-3.5830036767803914,7.113569060765311 +-0.2,-1e-12,0.01,-1.3159894310346063,-1.3159894310346063,0.01,0.06250213048751907,-0.9538012729796261,1.6965495637077477 +-0.2,-1e-12,0.1,-0.7681831196367869,-0.7681831196367869,0.1,0.27205632451583844,-0.3114430176081557,0.9744118465008641 +-0.2,-1e-12,0.5,0.3802804256946527,0.3802804256946527,0.5,0.3220776268693338,-0.07054016389457933,0.37293262076925415 +-0.2,-1e-12,0.9,2.8421370325125865,2.8421370325125865,0.9,0.06045830600696434,-3.4370037917099565,0.08262516016048761 +-0.2,-1e-12,0.99,7.546826408585769,7.546826408585769,0.99,0.003965079363877121,-19.983141759026882,0.01260998193018686 +-0.2,-1e-12,0.999999999999,1250.948772553282,1250.948772553282,0.999999999999,3.9809660239823036e-15,-28448.43097623179,1.2559209888066767e-10 +-0.2,-1e-16,1e-12,-2.425513508564683,-2.425513508564683,9.999999999999913e-13,5.3663169738605905e-11,-3.583003676756784,7.113569060752209 +-0.2,-1e-16,0.01,-1.3159894310329099,-1.3159894310329099,0.010000000000000004,0.0625021304876342,-0.9538012729770354,1.696549563707227 +-0.2,-1e-16,0.1,-0.7681831196358125,-0.7681831196358125,0.1,0.272056324516089,-0.3114430176073431,0.9744118465007144 +-0.2,-1e-16,0.5,0.38028042569502557,0.38028042569502557,0.5,0.3220776268694231,-0.070540163894716,0.37293262076923694 +-0.2,-1e-16,0.9,2.842137032512669,2.842137032512669,0.9,0.06045830600696689,-3.4370037917101426,0.08262516016048704 +-0.2,-1e-16,0.99,7.5468264085857815,7.5468264085857815,0.99,0.0039650793638771374,-19.98314175902694,0.012609981930186852 +-0.2,-1e-16,0.999999999999,1250.948772553282,1250.948772553282,0.999999999999,3.9809660239823036e-15,-28448.43097623179,1.2559209888066767e-10 +-0.2,0.0,1e-12,-2.425513508564682,-2.425513508564682,1.0000000000000008e-12,5.366316973860645e-11,-3.583003676756782,7.113569060752207 +-0.2,0.0,0.01,-1.3159894310329097,-1.3159894310329097,0.010000000000000007,0.06250213048763423,-0.9538012729770351,1.696549563707227 +-0.2,0.0,0.1,-0.7681831196358124,-0.7681831196358124,0.1,0.272056324516089,-0.311443017607343,0.9744118465007144 +-0.2,0.0,0.5,0.3802804256950256,0.3802804256950256,0.5,0.3220776268694231,-0.07054016389471601,0.37293262076923694 +-0.2,0.0,0.9,2.842137032512669,2.842137032512669,0.9,0.060458306006966896,-3.4370037917101426,0.08262516016048704 +-0.2,0.0,0.99,7.5468264085857815,7.5468264085857815,0.99,0.0039650793638771374,-19.98314175902694,0.012609981930186852 +-0.2,0.0,0.999999999999,1250.948772553282,1250.948772553282,0.999999999999,3.9809660239823036e-15,-28448.43097623179,1.2559209888066762e-10 +-0.2,1e-16,1e-12,-2.425513508564681,-2.425513508564681,1.0000000000000103e-12,5.3663169738607e-11,-3.5830036767567797,7.113569060752206 +-0.2,1e-16,0.01,-1.3159894310329097,-1.3159894310329097,0.009999999999999995,0.06250213048763419,-0.9538012729770349,1.6965495637072268 +-0.2,1e-16,0.1,-0.7681831196358123,-0.7681831196358123,0.10000000000000002,0.2720563245160891,-0.31144301760734294,0.9744118465007144 +-0.2,1e-16,0.5,0.3802804256950256,0.3802804256950256,0.5,0.3220776268694231,-0.07054016389471603,0.37293262076923694 +-0.2,1e-16,0.9,2.842137032512669,2.842137032512669,0.9,0.060458306006966896,-3.4370037917101426,0.08262516016048704 +-0.2,1e-16,0.99,7.5468264085857815,7.5468264085857815,0.99,0.0039650793638771374,-19.98314175902694,0.012609981930186852 +-0.2,1e-16,0.999999999999,1250.948772553282,1250.948772553282,0.999999999999,3.9809660239823036e-15,-28448.43097623179,1.2559209888066767e-10 +-0.2,1e-12,1e-12,-2.4255135085575685,-2.4255135085575685,9.99999999999993e-13,5.366316973919915e-11,-3.5830036767331723,7.1135690607391036 +-0.2,1e-12,0.01,-1.3159894310312132,-1.3159894310312132,0.009999999999999998,0.06250213048774932,-0.9538012729744442,1.696549563706706 +-0.2,1e-12,0.1,-0.768183119634838,-0.768183119634838,0.10000000000000002,0.2720563245163396,-0.3114430176065303,0.9744118465005649 +-0.2,1e-12,0.5,0.38028042569539855,0.38028042569539855,0.5,0.3220776268695124,-0.0705401638948527,0.3729326207692197 +-0.2,1e-12,0.9,2.8421370325127517,2.8421370325127517,0.9,0.06045830600696944,-3.4370037917103287,0.08262516016048646 +-0.2,1e-12,0.99,7.546826408585794,7.546826408585794,0.99,0.003965079363877153,-19.983141759026996,0.012609981930186843 +-0.2,1e-12,0.999999999999,1250.948772553282,1250.948772553282,0.999999999999,3.9809660239823036e-15,-28448.43097623179,1.2559209888066767e-10 +-0.2,1e-08,1e-12,-2.425513437428992,-2.425513437428992,9.999999999999917e-13,5.366317566967922e-11,-3.5830034406617646,7.113568929715411 +-0.2,1e-08,0.01,-1.3159894140674142,-1.3159894140674142,0.009999999999999997,0.062502131638966,-0.9538012470676761,1.6965495584986272 +-0.2,1e-08,0.1,-0.768183109891694,-0.768183109891694,0.09999999999999999,0.27205632702182037,-0.31144300948043213,0.974411845004937 +-0.2,1e-08,0.5,0.3802804294243518,0.3802804294243518,0.5,0.3220776277624119,-0.07054016526156226,0.37293262059690546 +-0.2,1e-08,0.9,2.8421370333389206,2.8421370333389206,0.9,0.06045830603244657,-3.437003793569512,0.08262516015468342 +-0.2,1e-08,0.99,7.5468264087118815,7.5468264087118815,0.99,0.003965079364036539,-19.983141759607015,0.012609981930102362 +-0.2,1e-08,0.999999999999,1250.948772553282,1250.948772553282,0.999999999999,3.9809660239823036e-15,-28448.43097623179,1.2559209888066767e-10 +-0.2,0.2,1e-12,-1.3732093694238567,-1.3732093694238567,9.999999999999176e-13,1.7245870291056562e-09,-1.043419836256271,3.5466815359070973 +-0.2,0.2,0.01,-0.9888198302618675,-0.9888198302618675,0.009999999999999995,0.09422952644435081,-0.5247331097558013,1.5675882512815709 +-0.2,0.2,0.1,-0.5765544051432495,-0.5765544051432495,0.09999999999999999,0.33056425128252015,-0.17299195354428562,0.9406353937226334 +-0.2,0.2,0.5,0.4545128657725477,0.4545128657725477,0.5,0.3407690994967847,-0.10029610405209596,0.3693453609759058 +-0.2,0.2,0.9,2.858650408491046,2.858650408491046,0.9,0.06097140680610095,-3.4742518240510725,0.08250835606957427 +-0.2,0.2,0.99,7.549348235924496,7.549348235924496,0.99,0.003968269477858887,-19.994743808199942,0.012608291117433447 +-0.2,0.2,0.999999999999,1250.9487725533072,1250.9487725533072,0.999999999999,3.98096602398262e-15,-28448.430976232485,1.25592098880666e-10 +-0.2,0.5,1e-12,-0.6472463129682937,-0.6472463129682937,9.9999999991211e-13,2.297393953017335e-06,-0.21913697175939797,1.7410774205828206 +-0.2,0.5,0.01,-0.554552319339,-0.554552319339,0.010000000000000002,0.20245430036557707,-0.15978673048241043,1.3232447806566234 +-0.2,0.5,0.1,-0.3034201236772553,-0.3034201236772553,0.09999999999999999,0.46039409892063116,-0.04699234374052392,0.8783649625758071 +-0.2,0.5,0.5,0.5644486863689111,0.5644486863689111,0.5,0.37219640769424606,-0.15362370748304457,0.363463863136947 +-0.2,0.5,0.9,2.883376307062127,2.883376307062127,0.9,0.06175437547856503,-3.5303485200791234,0.08233042563490267 +-0.2,0.5,0.99,7.553130342368692,7.553130342368692,0.99,0.003973062471745882,-20.012148712071806,0.012605751080716666 +-0.2,0.5,0.999999999999,1250.948772553345,1250.948772553345,0.999999999999,3.980966023983096e-15,-28448.430976233525,1.2559209888066349e-10 +-0.2,1.0,1e-12,1.0000000000006e-12,1.0000000000006e-12,1e-12,0.9999999999988,-5.000000000005667e-25,0.9999999999725689 +-0.2,1.0,0.01,0.010060443549826546,0.010060443549826546,0.01,0.9880120321448116,-5.057235488542341e-05,0.9554016220743412 +-0.2,1.0,0.1,0.10647843800067534,0.10647843800067534,0.1,0.8812335261248792,-0.0056290114199458825,0.7600045513993379 +-0.2,1.0,0.5,0.743491774985175,0.743491774985175,0.5,0.43527528164806206,-0.2636262554743347,0.352481328916993 +-0.2,1.0,0.9,2.9244659623055678,2.9244659623055678,0.9,0.0630957344480193,-3.6244273832156786,0.08202671627858032 +-0.2,1.0,0.99,7.559432157547898,7.559432157547898,0.99,0.003981071705534977,-20.04116173714017,0.012601507519652125 +-0.2,1.0,0.999999999999,1250.9487725534077,1250.9487725534077,0.999999999999,3.980966023983893e-15,-28448.43097623526,1.255920988806593e-10 +-0.2,2.0,1e-12,0.743491774985175,0.743491774985175,2.9339727125305087e-09,148356963.1677465,-0.2636262554743347,0.5743491774985175 +-0.2,2.0,0.01,0.7436066517133704,0.7436066517133704,0.010000000000001395,43.522304913654786,-0.26370588769873804,0.573831606543224 +-0.2,2.0,0.1,0.7550481899414013,0.7550481899414013,0.10000000000000002,4.300572155635069,-0.271694644299489,0.5487341358191473 +-0.2,2.0,0.5,1.0836434189320576,1.0836434189320576,0.5,0.6164069360689596,-0.5487983355205464,0.3272403230022176 +-0.2,2.0,0.9,3.0061787706922507,3.0061787706922507,0.9,0.06592130814137986,-3.814677320384593,0.08139319262670854 +-0.2,2.0,0.99,7.572029406710619,7.572029406710619,0.99,0.003997168923714244,-20.099205790804156,0.012592982365763656 +-0.2,2.0,0.999999999999,1250.9487725535332,1250.9487725535332,0.999999999999,3.9809660239854874e-15,-28448.43097623873,1.2559209888065092e-10 +-1e-08,-2.0,1e-12,-54.56888016247832,-54.56888016247832,1.0000000000000006e-12,5.0000027284455e-13,-1488.8816119154924,27.13100631083415 +-1e-08,-2.0,0.01,-8.517092823711533,-8.517092823711533,0.01,0.004999500425812092,-36.27043611358816,4.1056303993826155 +-1e-08,-2.0,0.1,-3.901972593447692,-3.901972593447692,0.09999999999999999,0.04950000193147651,-7.612695159023547,1.8258434570328996 +-1e-08,-2.0,0.5,-0.4054651072861546,-0.4054651072861546,0.5,0.1875000007602471,-0.0822009767243852,0.424196239026626 +-1e-08,-2.0,0.9,2.1431573790315612,2.1431573790315612,0.9,0.08549999816760047,-2.2965617592424046,0.05452903094667608 +-1e-08,-2.0,0.99,4.5900821614489,4.5900821614489,0.99,0.009850499547853983,-10.534426963246085,0.0050420029199467024 +-1e-08,-2.0,0.999999999999,27.63104705526496,27.63104705526496,0.999999999999,9.999776019740978e-13,-381.73734552579305,4.999890772925349e-13 +-1e-08,-1.0,1e-12,-27.63101729856126,-27.63101729856126,9.999999999999992e-13,1.0000002763092485e-12,-381.73659363593214,26.631013757534127 +-1e-08,-1.0,0.01,-4.59511974455896,-4.59511974455896,0.009999999999999997,0.009900000454916872,-10.557562895128685,3.651686888754235 +-1e-08,-1.0,0.1,-2.1972245531972403,-2.1972245531972403,0.1,0.09000000197750214,-2.4138978862659917,1.5584278468623352 +-1e-08,-1.0,0.5,-0.0,-0.0,0.5,0.25,0.0,0.38629436111989063 +-1e-08,-1.0,0.9,2.197224601475199,2.197224601475199,0.9,0.0899999980224979,-2.413897956984337,0.053605157756088685 +-1e-08,-1.0,0.99,4.5951199557102225,4.5951199557102225,0.99,0.009899999545083157,-10.557563541972259,0.0050335855814434075 +-1e-08,-1.0,0.999999999999,27.631047055265462,27.631047055265462,0.999999999999,9.99977601974597e-13,-381.73734552580686,4.999890772924516e-13 +-1e-08,-0.5,1e-12,-14.508655686018168,-14.508655686018168,1.0000000000000016e-12,1.999998290172869e-12,-105.25054999786043,25.631045028256683 +-1e-08,-0.5,0.01,-2.8903717161249207,-2.8903717161249207,0.009999999999999998,0.018000000520266922,-4.177124368932266,3.116855672120273 +-1e-08,-0.5,0.1,-1.4643093082697876,-1.4643093082697876,0.10000000000000002,0.13675444879914056,-1.0721008803757297,1.3674737944373156 +-1e-08,-0.5,0.5,0.18822640663674362,0.18822640663674362,0.5,0.2928932182621501,-0.01771459006657587,0.3665525052783864 +-1e-08,-0.5,0.9,2.223911592069145,2.223911592069145,0.9,0.09237006145484664,-2.4728913663381205,0.0531427725458058 +-1e-08,-0.5,0.99,4.597635696324996,4.597635696324996,0.99,0.009924874072582947,-10.56912683608427,0.005029376874993818 +-1e-08,-0.5,0.999999999999,27.63104705526571,27.63104705526571,0.999999999999,9.999776019748482e-13,-381.73734552581374,4.999890772924099e-13 +-1e-08,-0.2,1e-12,-7.131652864051029,-7.131652864051029,9.999999999999996e-13,4.98009499663541e-12,-25.4302368911957,22.74146024298668 +-1e-08,-0.2,0.01,-2.022796055476454,-2.022796055476454,0.009999999999999995,0.030094642081078357,-2.0458519548200216,2.6511463975158187 +-1e-08,-0.2,0.1,-1.0731118812672171,-1.0731118812672171,0.09999999999999999,0.1845213297400237,-0.5757845569180371,1.239346735793129 +-1e-08,-0.2,0.5,0.29639757614434586,0.29639757614434586,0.5,0.32362359080047715,-0.04392576152872335,0.35457857790151504 +-1e-08,-0.2,0.9,2.2398127995018298,2.2398127995018298,0.9,0.09383236727393506,-2.5083806696784348,0.05286527161479865 +-1e-08,-0.2,0.99,4.599144130603114,4.599144130603114,0.99,0.00993983881879478,-10.576063204894407,0.0050268516453423045 +-1e-08,-0.2,0.999999999999,27.631047055265864,27.631047055265864,0.999999999999,9.999776019749955e-13,-381.7373455258179,4.999890772923849e-13 +-1e-08,-1e-08,1e-12,-3.3189391781142774,-3.3189391781142774,9.999999999999974e-13,2.7631018215618875e-11,-5.507678694943106,13.815510735663633 +-1e-08,-1e-08,0.01,-1.5271796371723638,-1.5271796371723638,0.009999999999999998,0.04605170150279351,-1.1661388280333014,2.3025850755024284 +-1e-08,-1e-08,0.1,-0.8340324532828306,-0.8340324532828306,0.1,0.23025850856888622,-0.3478050675314242,1.1512925413131176 +-1e-08,-1e-08,0.5,0.366512917787587,0.366512917787587,0.5,0.34657358780860315,-0.06716585937052807,0.3465735919505871 +-1e-08,-1e-08,0.9,2.2503673521064087,2.2503673521064087,0.9,0.09482446190819115,-2.5320765907195297,0.05268025902366316 +-1e-08,-1e-08,0.99,4.600149332533194,4.600149332533194,0.99,0.009949832036759304,-10.580686778560331,0.005025168158000129 +-1e-08,-1e-08,0.999999999999,27.631047055265963,27.631047055265963,0.999999999999,9.99977601975096e-13,-381.7373455258207,4.999890772923682e-13 +-1e-08,-1e-12,1e-12,-3.318939039972989,-3.318939039972989,9.999999999999963e-13,2.763102203260349e-11,-5.507678236460573,13.815510099499523 +-1e-08,-1e-12,0.01,-1.5271796141488156,-1.5271796141488156,0.010000000000000004,0.0460517025630671,-1.1661387928722076,2.302585057831203 +-1e-08,-1e-12,0.1,-0.8340324417710565,-0.8340324417710565,0.09999999999999999,0.23025851121957017,-0.34780505793023103,1.1512925368953113 +-1e-08,-1e-12,0.5,0.36651292125297635,0.36651292125297635,0.5,0.34657358900961555,-0.06716586064063804,0.3465735915502497 +-1e-08,-1e-12,0.9,2.2503673526331585,2.2503673526331585,0.9,0.09482446195813993,-2.5320765919049104,0.05268025901441339 +-1e-08,-1e-12,0.99,4.60014933258344,4.60014933258344,0.99,0.009949832037259257,-10.580686778791472,0.005025168157915962 +-1e-08,-1e-12,0.999999999999,27.631047055265963,27.631047055265963,0.999999999999,9.99977601975096e-13,-381.7373455258207,4.999890772923682e-13 +-1e-08,-1e-16,1e-12,-3.3189390399591745,-3.3189390399591745,1.0000000000000006e-12,2.76310220329853e-11,-5.5076782364147245,13.815510099435906 +-1e-08,-1e-16,0.01,-1.5271796141465133,-1.5271796141465133,0.010000000000000004,0.04605170256317313,-1.1661387928686913,2.3025850578294356 +-1e-08,-1e-16,0.1,-0.8340324417699053,-0.8340324417699053,0.1,0.23025851121983523,-0.3478050579292709,1.1512925368948694 +-1e-08,-1e-16,0.5,0.3665129212533229,0.3665129212533229,0.5,0.34657358900973567,-0.06716586064076505,0.34657359155020967 +-1e-08,-1e-16,0.9,2.2503673526332113,2.2503673526332113,0.9,0.09482446195814491,-2.532076591905029,0.052680259014412464 +-1e-08,-1e-16,0.99,4.600149332583445,4.600149332583445,0.99,0.009949832037259304,-10.580686778791495,0.005025168157915954 +-1e-08,-1e-16,0.999999999999,27.631047055265963,27.631047055265963,0.999999999999,9.99977601975096e-13,-381.7373455258207,4.999890772923682e-13 +-1e-08,0.0,1e-12,-3.318939039959173,-3.318939039959173,9.999999999999992e-13,2.7631022032985305e-11,-5.50767823641472,13.815510099435901 +-1e-08,0.0,0.01,-1.527179614146513,-1.527179614146513,0.010000000000000004,0.04605170256317314,-1.166138792868691,2.3025850578294356 +-1e-08,0.0,0.1,-0.8340324417699052,-0.8340324417699052,0.1,0.23025851121983526,-0.3478050579292708,1.1512925368948694 +-1e-08,0.0,0.5,0.3665129212533229,0.3665129212533229,0.5,0.34657358900973567,-0.06716586064076506,0.34657359155020967 +-1e-08,0.0,0.9,2.2503673526332113,2.2503673526332113,0.9,0.09482446195814491,-2.532076591905029,0.052680259014412464 +-1e-08,0.0,0.99,4.600149332583445,4.600149332583445,0.99,0.009949832037259304,-10.580686778791495,0.005025168157915954 +-1e-08,0.0,0.999999999999,27.631047055265963,27.631047055265963,0.999999999999,9.99977601975096e-13,-381.7373455258207,4.99989077292368e-13 +-1e-08,1e-16,1e-12,-3.318939039959172,-3.318939039959172,9.999999999999978e-13,2.7631022032985308e-11,-5.507678236414716,13.815510099435894 +-1e-08,1e-16,0.01,-1.5271796141465128,-1.5271796141465128,0.010000000000000004,0.04605170256317315,-1.1661387928686906,2.3025850578294356 +-1e-08,1e-16,0.1,-0.8340324417699051,-0.8340324417699051,0.1,0.2302585112198353,-0.34780505792927074,1.1512925368948694 +-1e-08,1e-16,0.5,0.36651292125332297,0.36651292125332297,0.5,0.34657358900973567,-0.06716586064076507,0.3465735915502096 +-1e-08,1e-16,0.9,2.2503673526332113,2.2503673526332113,0.9,0.09482446195814491,-2.532076591905029,0.052680259014412464 +-1e-08,1e-16,0.99,4.600149332583445,4.600149332583445,0.99,0.009949832037259304,-10.580686778791495,0.005025168157915954 +-1e-08,1e-16,0.999999999999,27.631047055265963,27.631047055265963,0.999999999999,9.99977601975096e-13,-381.7373455258207,4.999890772923682e-13 +-1e-08,1e-12,1e-12,-3.3189390399453575,-3.3189390399453575,1.0000000000000022e-12,2.763102203336712e-11,-5.507678236368868,13.815510099372277 +-1e-08,1e-12,0.01,-1.5271796141442104,-1.5271796141442104,0.010000000000000004,0.04605170256327918,-1.1661387928651745,2.302585057827668 +-1e-08,1e-12,0.1,-0.8340324417687539,-0.8340324417687539,0.1,0.23025851122010035,-0.3478050579283106,1.1512925368944276 +-1e-08,1e-12,0.5,0.3665129212536695,0.3665129212536695,0.5,0.3465735890098558,-0.06716586064089208,0.3465735915501696 +-1e-08,1e-12,0.9,2.250367352633264,2.250367352633264,0.9,0.09482446195814989,-2.5320765919051476,0.052680259014411535 +-1e-08,1e-12,0.99,4.600149332583451,4.600149332583451,0.99,0.00994983203725935,-10.580686778791518,0.005025168157915945 +-1e-08,1e-12,0.999999999999,27.631047055265963,27.631047055265963,0.999999999999,9.99977601975096e-13,-381.7373455258207,4.999890772923682e-13 +-1e-08,1e-08,1e-12,-3.318938901804075,-3.318938901804075,1.0000000000000052e-12,2.7631025850352542e-11,-5.507677777886374,13.815509463208167 +-1e-08,1e-08,0.01,-1.5271795911206627,-1.5271795911206627,0.009999999999999997,0.04605170362355276,-1.1661387577040816,2.3025850401564427 +-1e-08,1e-08,0.1,-0.8340324302569798,-0.8340324302569798,0.1,0.23025851387078433,-0.3478050483271176,1.1512925324766212 +-1e-08,1e-08,0.5,0.36651292471905883,0.36651292471905883,0.5,0.3465735902108682,-0.06716586191100206,0.34657359114983216 +-1e-08,1e-08,0.9,2.250367353160014,2.250367353160014,0.9,0.09482446200809869,-2.5320765930905282,0.05268025900516176 +-1e-08,1e-08,0.99,4.600149332633697,4.600149332633697,0.99,0.009949832037759303,-10.580686779022662,0.00502516815783178 +-1e-08,1e-08,0.999999999999,27.631047055265963,27.631047055265963,0.999999999999,9.99977601975096e-13,-381.7373455258207,4.999890772923682e-13 +-1e-08,0.2,1e-12,-1.605448882280363,-1.605448882280363,1.000000000000063e-12,1.2509432358381075e-09,-1.2887330637042838,4.889559172600378 +-1e-08,0.2,0.01,-1.1017620326679574,-1.1017620326679574,0.010000000000000002,0.07559432240834857,-0.6069397905433254,1.9540237133162952 +-1e-08,0.2,0.1,-0.6125948665498911,-0.6125948665498911,0.1,0.2924465980220696,-0.18763623564478946,1.063238337387996 +-1e-08,0.2,0.5,0.4350270127633198,0.4350270127633198,0.5,0.3717458858753925,-0.09462425077967512,0.3385686051822575 +-1e-08,0.2,0.9,2.260884903107591,2.260884903107591,0.9,0.0958305920339884,-2.5558002532886763,0.0524952464139678 +-1e-08,0.2,0.99,4.60115419786628,4.60115419786628,0.99,0.009959838656060756,-10.585309813922438,0.005023484670489553 +-1e-08,0.2,0.999999999999,27.631047055266063,27.631047055266063,0.999999999999,9.999776019751964e-13,-381.7373455258234,4.999890772923516e-13 +-1e-08,0.5,1e-12,-0.6931461781571872,-0.6931461781571872,1.0000000000452695e-12,1.999998013908179e-06,-0.24022581270199622,1.999972355088521 +-1e-08,0.5,0.01,-0.5877866631746532,-0.5877866631746532,0.009999999999999995,0.18000000105801595,-0.17274658104145707,1.4883144150309873 +-1e-08,0.5,0.1,-0.31301677200387623,-0.31301677200387623,0.09999999999999999,0.4324555333873342,-0.04898974982897868,0.9351112756056286 +-1e-08,0.5,0.5,0.5347999981696255,0.5347999981696255,0.5,0.41421356015788097,-0.1430055187661846,0.32659467771813583 +-1e-08,0.5,0.9,2.2765918510834964,2.2765918510834964,0.9,0.09736659388438765,-2.5914352085444228,0.05221774548265364 +-1e-08,0.5,0.99,4.602660864482912,4.602660864482912,0.99,0.009974873754130309,-10.59224335421295,0.005020959440837773 +-1e-08,0.5,0.999999999999,27.63104705526621,27.63104705526621,0.999999999999,9.999776019753473e-13,-381.73734552582755,4.999890772923266e-13 +-1e-08,1.0,1e-12,1.0000000000005e-12,1.0000000000005e-12,1e-12,0.999999999999,-5.000000000005e-25,0.999999999972369 +-1e-08,1.0,0.01,0.010050335854006488,0.010050335854006488,0.01,0.9899999999005017,-5.05046253874722e-05,0.953483129530292 +-1e-08,1.0,0.1,0.1053605157133305,0.1053605157133305,0.1,0.8999999990517553,-0.005550419133740165,0.7441572126735984 +-1e-08,1.0,0.5,0.6931471829622103,0.6931471829622103,0.5,0.49999999653426414,-0.2402265080691829,0.3068528215669964 +-1e-08,1.0,0.9,2.3025851195035365,2.3025851195035365,0.9,0.09999999769741492,-2.6509490959327717,0.051755360271274475 +-1e-08,1.0,0.99,4.605170292026054,4.605170292026054,0.99,0.009999999539483002,-10.603796546505372,0.005016750734387231 +-1e-08,1.0,0.999999999999,27.631047055266464,27.631047055266464,0.999999999999,9.99977601975595e-13,-381.7373455258345,4.999890772922849e-13 +-1e-08,2.0,0.01,0.6932471879632369,0.6932471879632369,0.009999999999997343,49.99499965342436,-0.24029583125418644,0.4995394403881368 +-1e-08,2.0,0.1,0.7031975188858804,0.7031975188858804,0.09999999999999977,4.949999965191735,-0.2472433747040927,0.4767415680696667 +-1e-08,2.0,0.5,0.9808292578218564,0.9808292578218564,0.5,0.7499999926437806,-0.481013014927048,0.2689509424513011 +-1e-08,2.0,0.9,2.353878415085314,2.353878415085314,0.9,0.10555555307090614,-2.7703717747652137,0.0508314870763045 +-1e-08,2.0,0.99,4.61018283408056,4.61018283408056,0.99,0.010050504587158424,-10.62689271851914,0.005008333395880129 +-1e-08,2.0,0.999999999999,27.63104705526696,27.63104705526696,0.999999999999,9.999776019760975e-13,-381.7373455258483,4.999890772922016e-13 +-1e-12,-2.0,1e-12,-54.56889504980827,-54.56889504980827,1e-12,5.000000000272844e-13,-1488.882153505577,27.13102111444804 +-1e-12,-2.0,0.01,-8.517093186379634,-8.517093186379634,0.009999999999999997,0.004999500000042579,-36.270438172840166,4.1056307490280295 +-1e-12,-2.0,0.1,-3.901972669567032,-3.901972669567032,0.1,0.04950000000019315,-7.612695357033936,1.8258435282696894 +-1e-12,-2.0,0.5,-0.4054651081080822,-0.4054651081080822,0.5,0.18750000000007602,-0.08220097694656049,0.42419624074642176 +-1e-12,-2.0,0.9,2.1431573560682406,2.1431573560682406,0.9,0.08549999999981674,-2.296561726433065,0.054529029778150016 +-1e-12,-2.0,0.99,4.5900820561151665,4.5900820561151665,0.99,0.009850499999954791,-10.534426640919099,0.00504200268853778 +-1e-12,-2.0,0.999999999999,27.631043238273595,27.631043238273595,0.999999999999,9.999778782507493e-13,-381.73727521415657,4.999889391541712e-13 +-1e-12,-1.0,1e-12,-27.63102111554581,-27.63102111554581,1.0000000000000016e-12,1.0000000000266327e-12,-381.7366639473852,26.63102111522034 +-1e-12,-1.0,0.01,-4.595119850124032,-4.595119850124032,0.010000000000000004,0.009900000000045496,-10.557563218518126,3.6516870565368476 +-1e-12,-1.0,0.1,-2.1972245773338055,-2.1972245773338055,0.1,0.09000000000019776,-2.413897921621628,1.558427881101071 +-1e-12,-1.0,0.5,-0.0,-0.0,0.5,0.25,0.0,0.38629436111989063 +-1e-12,-1.0,0.9,2.1972245773386336,2.1972245773386336,0.9,0.08999999999980222,-2.4138979216287004,0.05360515657838078 +-1e-12,-1.0,0.99,4.595119850145147,4.595119850145147,0.99,0.009899999999954516,-10.557563218582807,0.005033585350167253 +-1e-12,-1.0,0.999999999999,27.631043238274096,27.631043238274096,0.999999999999,9.999778782512485e-13,-381.7372752141704,4.999889391540878e-13 +-1e-12,-0.5,1e-12,-14.50865673841847,-14.50865673841847,9.999999999999984e-13,1.999998000029014e-12,-105.25056017713683,25.631048746605423 +-1e-12,-0.5,0.01,-2.8903717578919874,-2.8903717578919874,0.010000000000000002,0.01800000000005203,-4.177124449413833,3.1168557621999815 +-1e-12,-0.5,0.1,-1.4643093189897245,-1.4643093189897245,0.09999999999999999,0.13675444679683266,-1.0721008908405985,1.3674738144593594 +-1e-12,-0.5,0.5,0.18822640645961544,0.18822640645961544,0.5,0.29289321881339736,-0.01771459004434907,0.3665525045885068 +-1e-12,-0.5,0.9,2.223911567342704,2.223911567342704,0.9,0.09237006350886974,-2.4728913296785087,0.05314277136407573 +-1e-12,-0.5,0.99,4.597635590644299,4.597635590644299,0.99,0.009924874528846864,-10.569126512163377,0.005029376643784525 +-1e-12,-0.5,0.999999999999,27.631043238274344,27.631043238274344,0.999999999999,9.999778782514997e-13,-381.7372752141773,4.999889391540462e-13 +-1e-12,-0.2,1e-12,-7.131653118327973,-7.131653118327973,1.0000000000000012e-12,4.980094641507847e-12,-25.430238100139007,22.74146186466661 +-1e-12,-0.2,0.01,-2.022796075932928,-2.022796075932928,0.009999999999999993,0.030094641472385997,-2.0458519824062047,2.6511464511377416 +-1e-12,-0.2,0.1,-1.073111887024487,-1.073111887024487,0.1,0.18452132776010138,-0.5757845610368335,1.239346749091376 +-1e-12,-0.2,0.5,0.29639757570513214,0.29639757570513214,0.5,0.3236235917595937,-0.04392576144193543,0.3545785768506578 +-1e-12,-0.2,0.9,2.239812774420532,2.239812774420532,0.9,0.09383236937539424,-2.508380632226827,0.05286527043083396 +-1e-12,-0.2,0.99,4.599144024853059,4.599144024853059,0.99,0.009939839275896586,-10.576062880654586,0.005026851414173282 +-1e-12,-0.2,0.999999999999,27.631043238274497,27.631043238274497,0.999999999999,9.99977878251647e-13,-381.7372752141814,4.999889391540211e-13 +-1e-12,-1e-08,1e-12,-3.3189392331855574,-3.3189392331855574,9.999999999999945e-13,2.763101729865382e-11,-5.50767881679526,13.815511194146195 +-1e-12,-1e-08,0.01,-1.527179648832586,-1.527179648832586,0.010000000000000002,0.046051700799571646,-1.166138839904804,2.302585110663523 +-1e-12,-1e-08,0.1,-0.8340324567605335,-0.8340324567605335,0.09999999999999999,0.23025850664864758,-0.34780506946510226,1.151292550914311 +-1e-12,-1e-08,0.5,0.3665129171159956,0.3665129171159956,0.5,0.3465735890787131,-0.06716585920643012,0.3465735906804772 +-1e-12,-1e-08,0.9,2.250367326788175,2.250367326788175,0.9,0.0948244640418765,-2.532076552735979,0.05268025783828239 +-1e-12,-1e-08,0.99,4.600149226736908,4.600149226736908,0.99,0.009949832494420666,-10.580686454107862,0.005025167926858016 +-1e-12,-1e-08,0.999999999999,27.631043238274597,27.631043238274597,0.999999999999,9.999778782517475e-13,-381.7372752141842,4.999889391540044e-13 +-1e-12,-1e-12,1e-12,-3.318939095044264,-3.318939095044264,1.0000000000000018e-12,2.7631021115638567e-11,-5.507678358312712,13.815510557982044 +-1e-12,-1e-12,0.01,-1.5271796258090375,-1.5271796258090375,0.01,0.04605170185984521,-1.1661388047437096,2.3025850929922966 +-1e-12,-1e-12,0.1,-0.8340324452487593,-0.8340324452487593,0.09999999999999999,0.2302585092993315,-0.347805059863909,1.1512925464965045 +-1e-12,-1e-12,0.5,0.3665129205813849,0.3665129205813849,0.5,0.3465735902797255,-0.06716586047654008,0.34657359028013973 +-1e-12,-1e-12,0.9,2.2503673273149247,2.2503673273149247,0.9,0.09482446409182528,-2.53207655392136,0.052680257829032615 +-1e-12,-1e-12,0.99,4.600149226787154,4.600149226787154,0.99,0.009949832494920619,-10.580686454339006,0.00502516792677385 +-1e-12,-1e-12,0.999999999999,27.631043238274597,27.631043238274597,0.999999999999,9.999778782517475e-13,-381.7372752141842,4.999889391540044e-13 +-1e-12,-1e-16,1e-12,-3.3189390950304496,-3.3189390950304496,1.000000000000006e-12,2.763102111602038e-11,-5.507678358266864,13.815510557918428 +-1e-12,-1e-16,0.01,-1.5271796258067352,-1.5271796258067352,0.010000000000000002,0.046051701859951236,-1.1661388047401935,2.3025850929905296 +-1e-12,-1e-16,0.1,-0.8340324452476081,-0.8340324452476081,0.1,0.2302585092995966,-0.3478050598629489,1.1512925464960626 +-1e-12,-1e-16,0.5,0.36651292058173146,0.36651292058173146,0.5,0.34657359027984563,-0.06716586047666709,0.3465735902800997 +-1e-12,-1e-16,0.9,2.2503673273149776,2.2503673273149776,0.9,0.09482446409183026,-2.5320765539214785,0.05268025782903169 +-1e-12,-1e-16,0.99,4.60014922678716,4.60014922678716,0.99,0.009949832494920666,-10.580686454339029,0.005025167926773841 +-1e-12,-1e-16,0.999999999999,27.631043238274597,27.631043238274597,0.999999999999,9.999778782517475e-13,-381.7372752141842,4.999889391540044e-13 +-1e-12,0.0,1e-12,-3.3189390950304483,-3.3189390950304483,1.0000000000000046e-12,2.763102111602038e-11,-5.507678358266859,13.81551055791842 +-1e-12,0.0,0.01,-1.527179625806735,-1.527179625806735,0.010000000000000002,0.04605170185995125,-1.166138804740193,2.302585092990529 +-1e-12,0.0,0.1,-0.834032445247608,-0.834032445247608,0.09999999999999999,0.2302585092995966,-0.3478050598629488,1.1512925464960626 +-1e-12,0.0,0.5,0.3665129205817315,0.3665129205817315,0.5,0.34657359027984563,-0.0671658604766671,0.34657359028009965 +-1e-12,0.0,0.9,2.2503673273149776,2.2503673273149776,0.9,0.09482446409183026,-2.5320765539214785,0.052680257829031685 +-1e-12,0.0,0.99,4.60014922678716,4.60014922678716,0.99,0.009949832494920666,-10.580686454339029,0.005025167926773841 +-1e-12,0.0,0.999999999999,27.631043238274597,27.631043238274597,0.999999999999,9.999778782517475e-13,-381.7372752141842,4.999889391540042e-13 +-1e-12,1e-16,1e-12,-3.318939095030447,-3.318939095030447,1.0000000000000034e-12,2.7631021116020382e-11,-5.507678358266855,13.815510557918415 +-1e-12,1e-16,0.01,-1.5271796258067347,-1.5271796258067347,0.01,0.046051701859951256,-1.1661388047401928,2.302585092990529 +-1e-12,1e-16,0.1,-0.8340324452476079,-0.8340324452476079,0.09999999999999999,0.23025850929959663,-0.3478050598629487,1.1512925464960626 +-1e-12,1e-16,0.5,0.3665129205817315,0.3665129205817315,0.5,0.34657359027984563,-0.06716586047666712,0.34657359028009965 +-1e-12,1e-16,0.9,2.2503673273149776,2.2503673273149776,0.9,0.09482446409183026,-2.5320765539214785,0.052680257829031685 +-1e-12,1e-16,0.99,4.60014922678716,4.60014922678716,0.99,0.009949832494920666,-10.580686454339029,0.005025167926773841 +-1e-12,1e-16,0.999999999999,27.631043238274597,27.631043238274597,0.999999999999,9.999778782517475e-13,-381.7372752141842,4.999889391540044e-13 +-1e-12,1e-12,1e-12,-3.318939095016633,-3.318939095016633,9.999999999999953e-13,2.7631021116401868e-11,-5.507678358221006,13.815510557854799 +-1e-12,1e-12,0.01,-1.5271796258044323,-1.5271796258044323,0.010000000000000002,0.04605170186005729,-1.1661388047366767,2.302585092988762 +-1e-12,1e-12,0.1,-0.8340324452464567,-0.8340324452464567,0.1,0.23025850929986172,-0.34780505986198856,1.1512925464956207 +-1e-12,1e-12,0.5,0.36651292058207807,0.36651292058207807,0.5,0.34657359027996576,-0.06716586047679413,0.34657359028005963 +-1e-12,1e-12,0.9,2.2503673273150304,2.2503673273150304,0.9,0.09482446409183524,-2.532076553921597,0.05268025782903076 +-1e-12,1e-12,0.99,4.600149226787165,4.600149226787165,0.99,0.009949832494920713,-10.580686454339052,0.0050251679267738335 +-1e-12,1e-12,0.999999999999,27.631043238274597,27.631043238274597,0.999999999999,9.999778782517475e-13,-381.7372752141842,4.999889391540044e-13 +-1e-12,1e-08,1e-12,-3.3189389568753462,-3.3189389568753462,9.999999999999943e-13,2.7631024933387096e-11,-5.507677899738498,13.815509921690648 +-1e-12,1e-08,0.01,-1.5271796027808842,-1.5271796027808842,0.009999999999999998,0.046051702920330874,-1.1661387695755832,2.3025850753175354 +-1e-12,1e-08,0.1,-0.8340324337346825,-0.8340324337346825,0.1,0.2302585119505457,-0.3478050502607955,1.1512925420778142 +-1e-12,1e-08,0.5,0.3665129240474674,0.3665129240474674,0.5,0.34657359148097816,-0.0671658617469041,0.34657358987972214 +-1e-12,1e-08,0.9,2.25036732784178,2.25036732784178,0.9,0.09482446414178404,-2.5320765551069777,0.05268025781978099 +-1e-12,1e-08,0.99,4.600149226837411,4.600149226837411,0.99,0.009949832495420663,-10.580686454570193,0.005025167926689667 +-1e-12,1e-08,0.999999999999,27.631043238274597,27.631043238274597,0.999999999999,9.999778782517475e-13,-381.7372752141842,4.999889391540044e-13 +-1e-12,0.2,1e-12,-1.605448895166405,-1.605448895166405,1.0000000000000151e-12,1.2509432157568138e-09,-1.288733077496205,4.889559251091902 +-1e-12,0.2,0.01,-1.1017620387367484,-1.1017620387367484,0.01,0.0755943215755623,-0.606939795000901,1.954023734842834 +-1e-12,0.2,0.1,-0.6125948684260659,-0.6125948684260659,0.09999999999999999,0.2924465962307359,-0.1876362364110128,1.063238343900688 +-1e-12,0.2,0.5,0.43502701181717196,0.43502701181717196,0.5,0.3717458874924258,-0.0946242505052752,0.3385686037095399 +-1e-12,0.2,0.9,2.260884877552144,2.260884877552144,0.9,0.09583059420039113,-2.5558002147700614,0.05249524522722941 +-1e-12,0.2,0.99,4.601154092023769,4.601154092023769,0.99,0.00995983911428246,-10.585309489257302,0.005023484439374401 +-1e-12,0.2,0.999999999999,27.631043238274696,27.631043238274696,0.999999999999,9.999778782518479e-13,-381.737275214187,4.999889391539878e-13 +-1e-12,0.5,1e-12,-0.6931461805592051,-0.6931461805592051,1.000000000022979e-12,1.999998000024365e-06,-0.24022581381196256,1.9999723689498667 +-1e-12,0.5,0.01,-0.5877866649019463,-0.5877866649019463,0.009999999999999992,0.18000000000010574,-0.17274658171831028,1.4883144237782262 +-1e-12,0.5,0.1,-0.3130167724937247,-0.3130167724937247,0.1,0.4324555320338112,-0.04898974993119922,0.935111278532391 +-1e-12,0.5,0.5,0.5347999967397133,0.5347999967397133,0.5,0.41421356237287354,-0.14300551825637323,0.3265946759716822 +-1e-12,0.5,0.9,2.276591825171736,2.276591825171736,0.9,0.0973665961008059,-2.5914351692174216,0.05221774429398761 +-1e-12,0.5,0.99,4.602660758571073,4.602660758571073,0.99,0.009974874213194006,-10.59224302922877,0.0050209592097631576 +-1e-12,0.5,0.999999999999,27.631043238274845,27.631043238274845,0.999999999999,9.999778782519987e-13,-381.73727521419113,4.999889391539627e-13 +-1e-12,1.0,1e-12,1.0000000000005e-12,1.0000000000005e-12,1e-12,0.999999999999,-5.000000000005e-25,0.999999999972369 +-1e-12,1.0,0.01,0.010050335853501492,0.010050335853501492,0.01,0.98999999999999,-5.0504625384088614e-05,0.9534831294344733 +-1e-12,1.0,0.1,0.10536051565783186,0.10536051565783186,0.1,0.8999999999999052,-0.005550419129841921,0.7441572118896289 +-1e-12,1.0,0.5,0.6931471805601855,0.6931471805601855,0.5,0.49999999999965344,-0.24022650695921172,0.3068528194402674 +-1e-12,1.0,0.9,2.3025850929966967,2.3025850929966967,0.9,0.09999999999976973,-2.650949055243269,0.05175535907968245 +-1e-12,1.0,0.99,4.605170185998694,4.605170185998694,0.99,0.00999999999995396,-10.603796220989347,0.005016750503380431 +-1e-12,1.0,0.999999999999,27.631043238275097,27.631043238275097,0.999999999999,9.999778782522465e-13,-381.737275214198,4.999889391539211e-13 +-1e-12,2.0,1e-12,0.6931471805601855,0.6931471805601855,1.6015934531581238e-09,312189088.319337,-0.24022650695921172,0.5000000000003466 +-1e-12,2.0,0.01,0.693247185560519,0.693247185560519,0.010000000000002224,49.99499999995422,-0.2402958301437348,0.49953943692544 +-1e-12,2.0,0.1,0.703197516413694,0.703197516413694,0.09999999999999998,4.94999999999652,-0.24724337354513576,0.4767415647175671 +-1e-12,2.0,0.5,0.9808292530122072,0.9808292530122072,0.5,0.7499999999992644,-0.48101301178208494,0.26895093981361534 +-1e-12,2.0,0.9,2.3538783873843667,2.3538783873843667,0.9,0.10555555555530707,-2.7703717312954397,0.05083148587991278 +-1e-12,2.0,0.99,4.610182727822262,4.610182727822262,0.99,0.010050505050458722,-10.626892391939023,0.005008333165009903 +-1e-12,2.0,0.999999999999,27.631043238275595,27.631043238275595,0.999999999999,9.99977878252749e-13,-381.7372752142118,4.999889391538378e-13 +-1e-16,-2.0,1e-12,-54.568895051297005,-54.568895051297005,9.999999999999986e-13,5.00000000000002e-13,-1488.882153559736,27.1310211159284 +-1e-16,-2.0,0.01,-8.5170931864159,-8.5170931864159,0.01,0.004999500000000004,-36.27043817304609,4.105630749062994 +-1e-16,-2.0,0.1,-3.901972669574644,-3.901972669574644,0.09999999999999999,0.049500000000000016,-7.612695357053737,1.825843528276813 +-1e-16,-2.0,0.5,-0.4054651081081644,-0.4054651081081644,0.5,0.1875,-0.08220097694658271,0.42419624074659373 +-1e-16,-2.0,0.9,2.1431573560659443,2.1431573560659443,0.9,0.08549999999999995,-2.296561726429784,0.054529029778033165 +-1e-16,-2.0,0.99,4.590082056104633,4.590082056104633,0.99,0.009850500000000007,-10.534426640886865,0.005042002688514639 +-1e-16,-2.0,0.999999999999,27.631043237891898,27.631043237891898,0.999999999999,9.999778782783744e-13,-381.7372752071254,4.999889391403573e-13 +-1e-16,-1.0,1e-12,-27.63102111592751,-27.63102111592751,1.0000000000000002e-12,9.99999999999003e-13,-381.7366639544163,26.631021115956106 +-1e-16,-1.0,0.01,-4.595119850134589,-4.595119850134589,0.01,0.009900000000000004,-10.557563218550465,3.651687056553626 +-1e-16,-1.0,0.1,-2.197224577336219,-2.197224577336219,0.1,0.09000000000000002,-2.4138979216251633,1.5584278811044947 +-1e-16,-1.0,0.5,-0.0,-0.0,0.5,0.25,0.0,0.38629436111989063 +-1e-16,-1.0,0.9,2.19722457733622,2.19722457733622,0.9,0.08999999999999996,-2.413897921625165,0.05360515657826301 +-1e-16,-1.0,0.99,4.59511985013459,4.59511985013459,0.99,0.009900000000000008,-10.557563218550467,0.005033585350144125 +-1e-16,-1.0,0.999999999999,27.631043237892396,27.631043237892396,0.999999999999,9.99977878278877e-13,-381.73727520713925,4.99988939140274e-13 +-1e-16,-0.5,1e-12,-14.508656738523708,-14.508656738523708,1.0000000000000014e-12,1.9999980000000057e-12,-105.25056017815476,25.631048746977257 +-1e-16,-0.5,0.01,-2.890371757896164,-2.890371757896164,0.010000000000000004,0.018000000000000013,-4.177124449421882,3.1168557622089894 +-1e-16,-0.5,0.1,-1.4643093189907963,-1.4643093189907963,0.10000000000000002,0.13675444679663246,-1.072100890841645,1.3674738144613616 +-1e-16,-0.5,0.5,0.1882264064595977,0.1882264064595977,0.5,0.2928932188134525,-0.017714590044346844,0.3665525045884378 +-1e-16,-0.5,0.9,2.223911567340232,2.223911567340232,0.9,0.0923700635090751,-2.4728913296748427,0.053142771363957564 +-1e-16,-0.5,0.99,4.597635590633731,4.597635590633731,0.99,0.009924874528892494,-10.569126512130985,0.005029376643761405 +-1e-16,-0.5,0.999999999999,27.631043237892648,27.631043237892648,0.999999999999,9.999778782791246e-13,-381.7372752071461,4.999889391402323e-13 +-1e-16,-0.2,1e-12,-7.131653118353401,-7.131653118353401,1.0000000000000014e-12,4.980094641472335e-12,-25.430238100259903,22.74146186482878 +-1e-16,-0.2,0.01,-2.0227960759349735,-2.0227960759349735,0.009999999999999998,0.030094641472325143,-2.0458519824089634,2.651146451143104 +-1e-16,-0.2,0.1,-1.0731118870250627,-1.0731118870250627,0.09999999999999999,0.1845213277599034,-0.5757845610372454,1.239346749092706 +-1e-16,-0.2,0.5,0.29639757570508823,0.29639757570508823,0.5,0.32362359175968963,-0.04392576144192676,0.3545785768505527 +-1e-16,-0.2,0.9,2.2398127744180236,2.2398127744180236,0.9,0.0938323693756044,-2.5083806322230817,0.052865270430715566 +-1e-16,-0.2,0.99,4.599144024842484,4.599144024842484,0.99,0.009939839275942292,-10.576062880622162,0.005026851414150165 +-1e-16,-0.2,0.999999999999,27.631043237892797,27.631043237892797,0.999999999999,9.999778782792755e-13,-381.7372752071503,4.999889391402073e-13 +-1e-16,-1e-08,1e-12,-3.3189392331910645,-3.3189392331910645,9.99999999999994e-13,2.7631017298562106e-11,-5.507678816807446,13.815511194192043 +-1e-16,-1e-08,0.01,-1.527179648833752,-1.527179648833752,0.010000000000000005,0.046051700799501334,-1.166138839905991,2.302585110667039 +-1e-16,-1e-08,0.1,-0.8340324567608812,-0.8340324567608812,0.1,0.23025850664845557,-0.3478050694652956,1.1512925509152712 +-1e-16,-1e-08,0.5,0.3665129171159284,0.3665129171159284,0.5,0.3465735890788401,-0.06716585920641371,0.34657359068035015 +-1e-16,-1e-08,0.9,2.250367326785643,2.250367326785643,0.9,0.09482446404208986,-2.532076552732181,0.05268025783816385 +-1e-16,-1e-08,0.99,4.600149226726328,4.600149226726328,0.99,0.009949832494466435,-10.580686454075417,0.005025167926834902 +-1e-16,-1e-08,0.999999999999,27.631043237892897,27.631043237892897,0.999999999999,9.99977878279376e-13,-381.73727520715306,4.999889391401906e-13 +-1e-16,-1e-12,1e-12,-3.318939095049771,-3.318939095049771,1.0000000000000012e-12,2.7631021115546854e-11,-5.507678358324897,13.815510558027892 +-1e-16,-1e-12,0.01,-1.5271796258102035,-1.5271796258102035,0.010000000000000004,0.0460517018597749,-1.1661388047448968,2.3025850929958125 +-1e-16,-1e-12,0.1,-0.834032445249107,-0.834032445249107,0.1,0.2302585092991395,-0.3478050598641024,1.1512925464974646 +-1e-16,-1e-12,0.5,0.36651292058131774,0.36651292058131774,0.5,0.3465735902798525,-0.06716586047652368,0.3465735902800127 +-1e-16,-1e-12,0.9,2.250367327312393,2.250367327312393,0.9,0.09482446409203864,-2.5320765539175616,0.05268025782891408 +-1e-16,-1e-12,0.99,4.600149226776575,4.600149226776575,0.99,0.009949832494966379,-10.580686454306559,0.005025167926750736 +-1e-16,-1e-12,0.999999999999,27.631043237892897,27.631043237892897,0.999999999999,9.99977878279376e-13,-381.73727520715306,4.999889391401906e-13 +-1e-16,-1e-16,1e-12,-3.3189390950359567,-3.3189390950359567,1.0000000000000054e-12,2.7631021115928665e-11,-5.507678358279049,13.815510557964275 +-1e-16,-1e-16,0.01,-1.5271796258079011,-1.5271796258079011,0.010000000000000005,0.046051701859880924,-1.1661388047413805,2.3025850929940455 +-1e-16,-1e-16,0.1,-0.8340324452479558,-0.8340324452479558,0.1,0.23025850929940458,-0.34780505986314225,1.1512925464970227 +-1e-16,-1e-16,0.5,0.3665129205816643,0.3665129205816643,0.5,0.34657359027997264,-0.06716586047665069,0.3465735902799727 +-1e-16,-1e-16,0.9,2.250367327312446,2.250367327312446,0.9,0.09482446409204362,-2.53207655391768,0.05268025782891315 +-1e-16,-1e-16,0.99,4.60014922677658,4.60014922677658,0.99,0.009949832494966435,-10.580686454306582,0.005025167926750727 +-1e-16,-1e-16,0.999999999999,27.631043237892897,27.631043237892897,0.999999999999,9.99977878279376e-13,-381.73727520715306,4.999889391401906e-13 +-1e-16,0.0,1e-12,-3.3189390950359554,-3.3189390950359554,1.000000000000004e-12,2.7631021115928665e-11,-5.507678358279044,13.81551055796427 +-1e-16,0.0,0.01,-1.527179625807901,-1.527179625807901,0.010000000000000004,0.04605170185988094,-1.1661388047413803,2.3025850929940455 +-1e-16,0.0,0.1,-0.8340324452479557,-0.8340324452479557,0.1,0.2302585092994046,-0.34780505986314214,1.1512925464970227 +-1e-16,0.0,0.5,0.36651292058166435,0.36651292058166435,0.5,0.34657359027997264,-0.0671658604766507,0.34657359027997264 +-1e-16,0.0,0.9,2.250367327312446,2.250367327312446,0.9,0.09482446409204362,-2.53207655391768,0.05268025782891315 +-1e-16,0.0,0.99,4.60014922677658,4.60014922677658,0.99,0.009949832494966435,-10.580686454306582,0.005025167926750727 +-1e-16,0.0,0.999999999999,27.631043237892897,27.631043237892897,0.999999999999,9.99977878279376e-13,-381.73727520715306,4.999889391401904e-13 +-1e-16,1e-16,1e-12,-3.318939095035954,-3.318939095035954,1.0000000000000028e-12,2.763102111592867e-11,-5.50767835827904,13.815510557964263 +-1e-16,1e-16,0.01,-1.5271796258079007,-1.5271796258079007,0.010000000000000004,0.046051701859880945,-1.1661388047413799,2.302585092994045 +-1e-16,1e-16,0.1,-0.8340324452479556,-0.8340324452479556,0.1,0.2302585092994046,-0.3478050598631421,1.1512925464970227 +-1e-16,1e-16,0.5,0.36651292058166435,0.36651292058166435,0.5,0.34657359027997264,-0.0671658604766507,0.34657359027997264 +-1e-16,1e-16,0.9,2.250367327312446,2.250367327312446,0.9,0.09482446409204363,-2.53207655391768,0.05268025782891315 +-1e-16,1e-16,0.99,4.60014922677658,4.60014922677658,0.99,0.009949832494966435,-10.580686454306582,0.005025167926750727 +-1e-16,1e-16,0.999999999999,27.631043237892897,27.631043237892897,0.999999999999,9.99977878279376e-13,-381.73727520715306,4.999889391401906e-13 +-1e-16,1e-12,1e-12,-3.3189390950221402,-3.3189390950221402,9.999999999999947e-13,2.7631021116310154e-11,-5.507678358233192,13.815510557900646 +-1e-16,1e-12,0.01,-1.5271796258055983,-1.5271796258055983,0.010000000000000005,0.04605170185998698,-1.1661388047378638,2.302585092992278 +-1e-16,1e-12,0.1,-0.8340324452468044,-0.8340324452468044,0.1,0.2302585092996697,-0.34780505986218196,1.1512925464965809 +-1e-16,1e-12,0.5,0.3665129205820109,0.3665129205820109,0.5,0.34657359028009277,-0.06716586047677772,0.3465735902799326 +-1e-16,1e-12,0.9,2.2503673273124987,2.2503673273124987,0.9,0.0948244640920486,-2.5320765539177987,0.052680257828912225 +-1e-16,1e-12,0.99,4.600149226776585,4.600149226776585,0.99,0.009949832494966481,-10.580686454306605,0.005025167926750719 +-1e-16,1e-12,0.999999999999,27.631043237892897,27.631043237892897,0.999999999999,9.99977878279376e-13,-381.73727520715306,4.999889391401906e-13 +-1e-16,1e-08,1e-12,-3.318938956880853,-3.318938956880853,1.000000000000006e-12,2.763102493329571e-11,-5.507677899750684,13.815509921736496 +-1e-16,1e-08,0.01,-1.5271796027820501,-1.5271796027820501,0.010000000000000002,0.04605170292026056,-1.1661387695767702,2.3025850753210517 +-1e-16,1e-08,0.1,-0.8340324337350303,-0.8340324337350303,0.09999999999999999,0.23025851195035366,-0.34780505026098885,1.1512925420787743 +-1e-16,1e-08,0.5,0.3665129240474002,0.3665129240474002,0.5,0.34657359148110517,-0.06716586174688768,0.34657358987959513 +-1e-16,1e-08,0.9,2.2503673278392484,2.2503673278392484,0.9,0.0948244641419974,-2.5320765551031794,0.05268025781966245 +-1e-16,1e-08,0.99,4.600149226826832,4.600149226826832,0.99,0.009949832495466425,-10.580686454537748,0.005025167926666553 +-1e-16,1e-08,0.999999999999,27.631043237892897,27.631043237892897,0.999999999999,9.99977878279376e-13,-381.73727520715306,4.999889391401906e-13 +-1e-16,0.2,1e-12,-1.6054488951676935,-1.6054488951676935,1.0000000000001145e-12,1.2509432157549049e-09,-1.288733077497584,4.889559251099752 +-1e-16,0.2,0.01,-1.1017620387373552,-1.1017620387373552,0.010000000000000002,0.07559432157547903,-0.6069397950013468,1.9540237348449867 +-1e-16,0.2,0.1,-0.6125948684262535,-0.6125948684262535,0.09999999999999999,0.29244659623055674,-0.18763623641108942,1.0632383439013395 +-1e-16,0.2,0.5,0.4350270118170773,0.4350270118170773,0.5,0.3717458874925875,-0.09462425050524777,0.3385686037093926 +-1e-16,0.2,0.9,2.260884877549589,2.260884877549589,0.9,0.09583059420060774,-2.5558002147662093,0.052495245227110736 +-1e-16,0.2,0.99,4.601154092013185,4.601154092013185,0.99,0.009959839114328285,-10.585309489224835,0.00502348443935129 +-1e-16,0.2,0.999999999999,27.631043237892996,27.631043237892996,0.999999999999,9.999778782794764e-13,-381.7372752071558,4.999889391401739e-13 +-1e-16,0.5,1e-12,-0.6931461805594453,-0.6931461805594453,9.999999999220366e-13,1.999997999922037e-06,-0.24022581381207356,1.999972368951253 +-1e-16,0.5,0.01,-0.587786664902119,-0.587786664902119,0.010000000000000007,0.18000000000000008,-0.17274658171837795,1.4883144237791008 +-1e-16,0.5,0.1,-0.3130167724937737,-0.3130167724937737,0.1,0.43245553203367587,-0.04898974993120944,0.9351112785326836 +-1e-16,0.5,0.5,0.5347999967395703,0.5347999967395703,0.5,0.41421356237309503,-0.14300551825632224,0.32659467597150754 +-1e-16,0.5,0.9,2.276591825169145,2.276591825169145,0.9,0.09736659610102756,-2.5914351692134887,0.05221774429386874 +-1e-16,0.5,0.99,4.602660758560481,4.602660758560481,0.99,0.009974874213239915,-10.592243029196272,0.00502095920974005 +-1e-16,0.5,0.999999999999,27.631043237893145,27.631043237893145,0.999999999999,9.999778782796273e-13,-381.73727520715994,4.99988939140149e-13 +-1e-16,1.0,1e-12,1.0000000000005e-12,1.0000000000005e-12,1e-12,0.999999999999,-5.000000000005e-25,0.999999999972369 +-1e-16,1.0,0.01,0.010050335853501442,0.010050335853501442,0.01,0.99,-5.0504625384088275e-05,0.9534831294344637 +-1e-16,1.0,0.1,0.10536051565782631,0.10536051565782631,0.1,0.9,-0.005550419129841532,0.7441572118895505 +-1e-16,1.0,0.5,0.6931471805599453,0.6931471805599453,0.5,0.5,-0.24022650695910072,0.3068528194400547 +-1e-16,1.0,0.9,2.3025850929940463,2.3025850929940463,0.9,0.09999999999999994,-2.6509490552392,0.05175535907956329 +-1e-16,1.0,0.99,4.605170185988092,4.605170185988092,0.99,0.010000000000000002,-10.603796220956795,0.005016750503357329 +-1e-16,1.0,0.999999999999,27.631043237893397,27.631043237893397,0.999999999999,9.99977878279875e-13,-381.7372752071669,4.999889391401073e-13 +-1e-16,2.0,0.01,0.6932471855602786,0.6932471855602786,0.009999999999997651,49.99500000001174,-0.24029583014362374,0.4995394369250937 +-1e-16,2.0,0.1,0.7031975164134467,0.7031975164134467,0.09999999999999984,4.950000000000007,-0.24724337354501988,0.4767415647172319 +-1e-16,2.0,0.5,0.9808292530117263,0.9808292530117263,0.5,0.7499999999999999,-0.48101301178177047,0.2689509398133516 +-1e-16,2.0,0.9,2.3538783873815965,2.3538783873815965,0.9,0.10555555555555553,-2.770371731291093,0.05083148587979314 +-1e-16,2.0,0.99,4.610182727811636,4.610182727811636,0.99,0.010050505050505055,-10.626892391906367,0.005008333164986815 +-1e-16,2.0,0.999999999999,27.6310432378939,27.6310432378939,0.999999999999,9.99977878280374e-13,-381.7372752071807,4.99988939140024e-13 +0.0,-2.0,1e-12,-54.568895051297154,-54.568895051297154,9.999999999999984e-13,4.999999999999992e-13,-1488.8821535597413,27.131021115928547 +0.0,-2.0,0.01,-8.517093186415904,-8.517093186415904,0.01,0.0049995,-36.27043817304611,4.105630749062998 +0.0,-2.0,0.1,-3.9019726695746444,-3.9019726695746444,0.1,0.0495,-7.612695357053739,1.8258435282768137 +0.0,-2.0,0.5,-0.4054651081081644,-0.4054651081081644,0.5,0.1875,-0.08220097694658271,0.42419624074659373 +0.0,-2.0,0.9,2.143157356065944,2.143157356065944,0.9,0.08549999999999999,-2.2965617264297835,0.05452902977803315 +0.0,-2.0,0.99,4.590082056104632,4.590082056104632,0.99,0.009850500000000009,-10.534426640886862,0.005042002688514637 +0.0,-2.0,0.999999999999,27.63104323789186,27.63104323789186,0.999999999999,9.99977878278378e-13,-381.7372752071247,4.999889391403559e-13 +0.0,-1.0,1e-12,-27.63102111592755,-27.63102111592755,9.999999999999992e-13,9.999999999989993e-13,-381.73666395441705,26.63102111595618 +0.0,-1.0,0.01,-4.59511985013459,-4.59511985013459,0.010000000000000002,0.0099,-10.557563218550468,3.6516870565536275 +0.0,-1.0,0.1,-2.197224577336219,-2.197224577336219,0.10000000000000002,0.09000000000000002,-2.4138979216251637,1.5584278811044951 +0.0,-1.0,0.5,0.0,0.0,0.5,0.25,0.0,0.38629436111989063 +0.0,-1.0,0.9,2.1972245773362196,2.1972245773362196,0.9,0.08999999999999998,-2.4138979216251646,0.053605156578263 +0.0,-1.0,0.99,4.595119850134589,4.595119850134589,0.99,0.00990000000000001,-10.557563218550463,0.005033585350144123 +0.0,-1.0,0.999999999999,27.63104323789236,27.63104323789236,0.999999999999,9.99977878278877e-13,-381.7372752071385,4.999889391402726e-13 +0.0,-0.5,1e-12,-14.508656738523719,-14.508656738523719,1.0000000000000012e-12,1.9999980000000024e-12,-105.25056017815487,25.631048746977296 +0.0,-0.5,0.01,-2.8903717578961645,-2.8903717578961645,0.010000000000000004,0.018000000000000006,-4.177124449421883,3.1168557622089903 +0.0,-0.5,0.1,-1.4643093189907965,-1.4643093189907965,0.1,0.13675444679663243,-1.0721008908416452,1.3674738144613618 +0.0,-0.5,0.5,0.1882264064595977,0.1882264064595977,0.5,0.2928932188134525,-0.017714590044346844,0.3665525045884378 +0.0,-0.5,0.9,2.2239115673402314,2.2239115673402314,0.9,0.09237006350907515,-2.4728913296748423,0.05314277136395755 +0.0,-0.5,0.99,4.59763559063373,4.59763559063373,0.99,0.009924874528892496,-10.569126512130982,0.005029376643761402 +0.0,-0.5,0.999999999999,27.63104323789261,27.63104323789261,0.999999999999,9.999778782791285e-13,-381.73727520714544,4.999889391402309e-13 +0.0,-0.2,1e-12,-7.1316531183534035,-7.1316531183534035,1.0000000000000008e-12,4.980094641472328e-12,-25.430238100259913,22.741461864828796 +0.0,-0.2,0.01,-2.0227960759349735,-2.0227960759349735,0.010000000000000005,0.03009464147232515,-2.045851982408964,2.6511464511431044 +0.0,-0.2,0.1,-1.0731118870250627,-1.0731118870250627,0.1,0.18452132775990338,-0.5757845610372454,1.2393467490927061 +0.0,-0.2,0.5,0.29639757570508823,0.29639757570508823,0.5,0.32362359175968963,-0.04392576144192676,0.3545785768505527 +0.0,-0.2,0.9,2.239812774418023,2.239812774418023,0.9,0.09383236937560444,-2.5083806322230813,0.05286527043071555 +0.0,-0.2,0.99,4.5991440248424835,4.5991440248424835,0.99,0.009939839275942294,-10.576062880622159,0.005026851414150163 +0.0,-0.2,0.999999999999,27.631043237892758,27.631043237892758,0.999999999999,9.999778782792791e-13,-381.7372752071496,4.999889391402059e-13 +0.0,-1e-08,1e-12,-3.318939233191065,-3.318939233191065,9.999999999999967e-13,2.7631017298562174e-11,-5.507678816807447,13.815511194192048 +0.0,-1e-08,0.01,-1.5271796488337521,-1.5271796488337521,0.01,0.046051700799501306,-1.1661388399059913,2.302585110667039 +0.0,-1e-08,0.1,-0.8340324567608812,-0.8340324567608812,0.10000000000000002,0.23025850664845554,-0.34780506946529566,1.1512925509152712 +0.0,-1e-08,0.5,0.3665129171159284,0.3665129171159284,0.5,0.3465735890788401,-0.06716585920641371,0.34657359068035015 +0.0,-1e-08,0.9,2.2503673267856428,2.2503673267856428,0.9,0.0948244640420899,-2.5320765527321805,0.05268025783816384 +0.0,-1e-08,0.99,4.6001492267263275,4.6001492267263275,0.99,0.009949832494466439,-10.580686454075414,0.005025167926834899 +0.0,-1e-08,0.999999999999,27.631043237892857,27.631043237892857,0.999999999999,9.999778782793797e-13,-381.7372752071523,4.999889391401893e-13 +0.0,-1e-12,1e-12,-3.3189390950497715,-3.3189390950497715,1.0000000000000042e-12,2.763102111554692e-11,-5.507678358324898,13.815510558027897 +0.0,-1e-12,0.01,-1.5271796258102037,-1.5271796258102037,0.009999999999999998,0.04605170185977487,-1.1661388047448968,2.302585092995813 +0.0,-1e-12,0.1,-0.834032445249107,-0.834032445249107,0.10000000000000002,0.2302585092991395,-0.3478050598641024,1.1512925464974646 +0.0,-1e-12,0.5,0.36651292058131774,0.36651292058131774,0.5,0.3465735902798525,-0.06716586047652366,0.34657359028001267 +0.0,-1e-12,0.9,2.250367327312393,2.250367327312393,0.9,0.09482446409203864,-2.532076553917561,0.052680257828914064 +0.0,-1e-12,0.99,4.6001492267765745,4.6001492267765745,0.99,0.009949832494966383,-10.580686454306557,0.005025167926750733 +0.0,-1e-12,0.999999999999,27.631043237892857,27.631043237892857,0.999999999999,9.999778782793797e-13,-381.7372752071523,4.999889391401893e-13 +0.0,-1e-16,1e-12,-3.3189390950359576,-3.3189390950359576,9.999999999999961e-13,2.7631021115928407e-11,-5.50767835827905,13.81551055796428 +0.0,-1e-16,0.01,-1.5271796258079013,-1.5271796258079013,0.01,0.0460517018598809,-1.1661388047413808,2.302585092994046 +0.0,-1e-16,0.1,-0.834032445247956,-0.834032445247956,0.09999999999999999,0.23025850929940453,-0.34780505986314225,1.151292546497023 +0.0,-1e-16,0.5,0.3665129205816643,0.3665129205816643,0.5,0.34657359027997264,-0.06716586047665069,0.34657359027997264 +0.0,-1e-16,0.9,2.2503673273124454,2.2503673273124454,0.9,0.09482446409204366,-2.5320765539176797,0.05268025782891314 +0.0,-1e-16,0.99,4.600149226776579,4.600149226776579,0.99,0.009949832494966438,-10.58068645430658,0.005025167926750725 +0.0,-1e-16,0.999999999999,27.631043237892857,27.631043237892857,0.999999999999,9.999778782793797e-13,-381.7372752071523,4.999889391401893e-13 +0.0,0.0,1e-12,-3.3189390950359563,-3.3189390950359563,9.999999999999947e-13,2.763102111592841e-11,-5.507678358279046,13.815510557964274 +0.0,0.0,0.01,-1.5271796258079011,-1.5271796258079011,0.01,0.04605170185988091,-1.1661388047413803,2.3025850929940455 +0.0,0.0,0.1,-0.8340324452479557,-0.8340324452479557,0.10000000000000002,0.23025850929940458,-0.3478050598631422,1.1512925464970227 +0.0,0.0,0.5,0.36651292058166435,0.36651292058166435,0.5,0.34657359027997264,-0.06716586047665069,0.34657359027997264 +0.0,0.0,0.9,2.2503673273124454,2.2503673273124454,0.9,0.09482446409204366,-2.5320765539176797,0.05268025782891314 +0.0,0.0,0.99,4.600149226776579,4.600149226776579,0.99,0.009949832494966438,-10.58068645430658,0.005025167926750725 +0.0,0.0,0.999999999999,27.631043237892857,27.631043237892857,0.999999999999,9.999778782793797e-13,-381.7372752071523,4.999889391401891e-13 +0.0,1e-16,1e-12,-3.3189390950359545,-3.3189390950359545,1.0000000000000056e-12,2.7631021115928737e-11,-5.507678358279041,13.815510557964268 +0.0,1e-16,0.01,-1.527179625807901,-1.527179625807901,0.009999999999999998,0.046051701859880924,-1.16613880474138,2.3025850929940455 +0.0,1e-16,0.1,-0.8340324452479556,-0.8340324452479556,0.10000000000000002,0.2302585092994046,-0.3478050598631421,1.1512925464970227 +0.0,1e-16,0.5,0.36651292058166435,0.36651292058166435,0.5,0.34657359027997264,-0.0671658604766507,0.34657359027997264 +0.0,1e-16,0.9,2.2503673273124454,2.2503673273124454,0.9,0.09482446409204366,-2.5320765539176797,0.05268025782891314 +0.0,1e-16,0.99,4.600149226776579,4.600149226776579,0.99,0.009949832494966438,-10.58068645430658,0.005025167926750725 +0.0,1e-16,0.999999999999,27.631043237892857,27.631043237892857,0.999999999999,9.999778782793797e-13,-381.7372752071523,4.999889391401893e-13 +0.0,1e-12,1e-12,-3.3189390950221407,-3.3189390950221407,9.999999999999978e-13,2.7631021116310222e-11,-5.507678358233193,13.815510557900652 +0.0,1e-12,0.01,-1.5271796258055985,-1.5271796258055985,0.01,0.04605170185998695,-1.166138804737864,2.3025850929922784 +0.0,1e-12,0.1,-0.8340324452468045,-0.8340324452468045,0.09999999999999999,0.23025850929966965,-0.34780505986218196,1.151292546496581 +0.0,1e-12,0.5,0.3665129205820109,0.3665129205820109,0.5,0.34657359028009277,-0.06716586047677771,0.3465735902799326 +0.0,1e-12,0.9,2.2503673273124982,2.2503673273124982,0.9,0.09482446409204864,-2.5320765539177983,0.05268025782891221 +0.0,1e-12,0.99,4.600149226776584,4.600149226776584,0.99,0.009949832494966485,-10.580686454306603,0.005025167926750717 +0.0,1e-12,0.999999999999,27.631043237892857,27.631043237892857,0.999999999999,9.999778782793797e-13,-381.7372752071523,4.999889391401893e-13 +0.0,1e-08,1e-12,-3.318938956880854,-3.318938956880854,9.999999999999967e-13,2.763102493329545e-11,-5.507677899750685,13.815509921736501 +0.0,1e-08,0.01,-1.5271796027820503,-1.5271796027820503,0.009999999999999997,0.04605170292026054,-1.1661387695767704,2.302585075321052 +0.0,1e-08,0.1,-0.8340324337350303,-0.8340324337350303,0.1,0.23025851195035366,-0.3478050502609889,1.1512925420787743 +0.0,1e-08,0.5,0.3665129240474002,0.3665129240474002,0.5,0.34657359148110517,-0.06716586174688768,0.34657358987959513 +0.0,1e-08,0.9,2.250367327839248,2.250367327839248,0.9,0.09482446414199744,-2.532076555103179,0.052680257819662436 +0.0,1e-08,0.99,4.600149226826831,4.600149226826831,0.99,0.009949832495466427,-10.580686454537744,0.0050251679266665504 +0.0,1e-08,0.999999999999,27.631043237892857,27.631043237892857,0.999999999999,9.999778782793797e-13,-381.7372752071523,4.999889391401893e-13 +0.0,0.2,1e-12,-1.6054488951676937,-1.6054488951676937,9.999999999999978e-13,1.2509432157547882e-09,-1.2887330774975843,4.889559251099753 +0.0,0.2,0.01,-1.1017620387373552,-1.1017620387373552,0.010000000000000007,0.07559432157547905,-0.6069397950013468,1.954023734844987 +0.0,0.2,0.1,-0.6125948684262535,-0.6125948684262535,0.09999999999999999,0.29244659623055674,-0.18763623641108942,1.0632383439013395 +0.0,0.2,0.5,0.4350270118170773,0.4350270118170773,0.5,0.3717458874925875,-0.09462425050524775,0.3385686037093926 +0.0,0.2,0.9,2.2608848775495884,2.2608848775495884,0.9,0.09583059420060779,-2.555800214766209,0.05249524522711072 +0.0,0.2,0.99,4.601154092013184,4.601154092013184,0.99,0.009959839114328286,-10.585309489224832,0.005023484439351287 +0.0,0.2,0.999999999999,27.631043237892957,27.631043237892957,0.999999999999,9.999778782794803e-13,-381.7372752071551,4.999889391401726e-13 +0.0,0.5,1e-12,-0.6931461805594453,-0.6931461805594453,9.999999999700815e-13,1.999997999970082e-06,-0.24022581381207359,1.999972368951253 +0.0,0.5,0.01,-0.5877866649021191,-0.5877866649021191,0.00999999999999999,0.17999999999999994,-0.17274658171837798,1.488314423779101 +0.0,0.5,0.1,-0.3130167724937737,-0.3130167724937737,0.1,0.43245553203367587,-0.04898974993120944,0.9351112785326837 +0.0,0.5,0.5,0.5347999967395703,0.5347999967395703,0.5,0.41421356237309503,-0.14300551825632224,0.3265946759715075 +0.0,0.5,0.9,2.2765918251691444,2.2765918251691444,0.9,0.0973665961010276,-2.5914351692134887,0.052217744293868724 +0.0,0.5,0.99,4.60266075856048,4.60266075856048,0.99,0.009974874213239919,-10.592243029196268,0.005020959209740048 +0.0,0.5,0.999999999999,27.63104323789311,27.63104323789311,0.999999999999,9.999778782796275e-13,-381.73727520715926,4.999889391401476e-13 +0.0,1.0,1e-12,1.0000000000005e-12,1.0000000000005e-12,1e-12,0.999999999999,-5.000000000004997e-25,0.999999999972369 +0.0,1.0,0.01,0.010050335853501442,0.010050335853501442,0.01,0.99,-5.0504625384088275e-05,0.9534831294344637 +0.0,1.0,0.1,0.10536051565782631,0.10536051565782631,0.1,0.9,-0.005550419129841532,0.7441572118895504 +0.0,1.0,0.5,0.6931471805599453,0.6931471805599453,0.5,0.5,-0.24022650695910072,0.3068528194400547 +0.0,1.0,0.9,2.302585092994046,2.302585092994046,0.9,0.09999999999999998,-2.6509490552391997,0.05175535907956328 +0.0,1.0,0.99,4.605170185988091,4.605170185988091,0.99,0.010000000000000004,-10.603796220956792,0.0050167505033573275 +0.0,1.0,0.999999999999,27.63104323789336,27.63104323789336,0.999999999999,9.999778782798787e-13,-381.73727520716614,4.99988939140106e-13 +0.0,2.0,0.01,0.6932471855602786,0.6932471855602786,0.009999999999998852,49.99500000000574,-0.2402958301436237,0.4995394369250937 +0.0,2.0,0.1,0.7031975164134467,0.7031975164134467,0.09999999999999996,4.950000000000002,-0.24724337354501985,0.47674156471723184 +0.0,2.0,0.5,0.9808292530117262,0.9808292530117262,0.49999999999999994,0.7500000000000001,-0.4810130117817704,0.26895093981335155 +0.0,2.0,0.9,2.3538783873815965,2.3538783873815965,0.9,0.10555555555555553,-2.7703717312910925,0.05083148587979312 +0.0,2.0,0.99,4.610182727811635,4.610182727811635,0.99,0.010050505050505058,-10.626892391906363,0.0050083331649868135 +0.0,2.0,0.999999999999,27.63104323789386,27.63104323789386,0.999999999999,9.999778782803777e-13,-381.73727520717995,4.999889391400226e-13 +1e-16,-2.0,1e-12,-54.5688950512973,-54.5688950512973,1.0000000000000018e-12,4.999999999999982e-13,-1488.8821535597467,27.131021115928696 +1e-16,-2.0,0.01,-8.517093186415908,-8.517093186415908,0.01,0.004999499999999996,-36.27043817304613,4.1056307490630015 +1e-16,-2.0,0.1,-3.9019726695746453,-3.9019726695746453,0.1,0.04949999999999998,-7.612695357053741,1.8258435282768144 +1e-16,-2.0,0.5,-0.4054651081081644,-0.4054651081081644,0.5,0.1875,-0.08220097694658272,0.4241962407465938 +1e-16,-2.0,0.9,2.143157356065944,2.143157356065944,0.9,0.08549999999999999,-2.296561726429783,0.05452902977803314 +1e-16,-2.0,0.99,4.590082056104631,4.590082056104631,0.99,0.009850500000000012,-10.53442664088686,0.0050420026885146345 +1e-16,-2.0,0.999999999999,27.63104323789182,27.63104323789182,0.999999999999,9.999778782783817e-13,-381.737275207124,4.999889391403546e-13 +1e-16,-1.0,1e-12,-27.631021115927588,-27.631021115927588,9.999999999999984e-13,9.999999999989957e-13,-381.73666395441774,26.63102111595625 +1e-16,-1.0,0.01,-4.595119850134591,-4.595119850134591,0.010000000000000004,0.009899999999999999,-10.557563218550472,3.6516870565536292 +1e-16,-1.0,0.1,-2.1972245773362196,-2.1972245773362196,0.1,0.08999999999999998,-2.413897921625164,1.5584278811044956 +1e-16,-1.0,0.5,0.0,0.0,0.5,0.25,0.0,0.38629436111989063 +1e-16,-1.0,0.9,2.1972245773362196,2.1972245773362196,0.9,0.08999999999999998,-2.413897921625164,0.05360515657826299 +1e-16,-1.0,0.99,4.595119850134588,4.595119850134588,0.99,0.009900000000000013,-10.557563218550461,0.0050335853501441204 +1e-16,-1.0,0.999999999999,27.63104323789232,27.63104323789232,0.999999999999,9.999778782788807e-13,-381.7372752071378,4.999889391402712e-13 +1e-16,-0.5,1e-12,-14.50865673852373,-14.50865673852373,1.0000000000000008e-12,1.999997999999999e-12,-105.25056017815497,25.63104874697733 +1e-16,-0.5,0.01,-2.890371757896165,-2.890371757896165,0.010000000000000002,0.018,-4.177124449421884,3.116855762208991 +1e-16,-0.5,0.1,-1.4643093189907965,-1.4643093189907965,0.10000000000000002,0.1367544467966324,-1.0721008908416452,1.367473814461362 +1e-16,-0.5,0.5,0.1882264064595977,0.1882264064595977,0.5,0.2928932188134525,-0.017714590044346844,0.3665525045884378 +1e-16,-0.5,0.9,2.223911567340231,2.223911567340231,0.9,0.09237006350907517,-2.472891329674842,0.053142771363957536 +1e-16,-0.5,0.99,4.597635590633729,4.597635590633729,0.99,0.0099248745288925,-10.569126512130978,0.0050293766437613996 +1e-16,-0.5,0.999999999999,27.63104323789257,27.63104323789257,0.999999999999,9.99977878279132e-13,-381.7372752071447,4.999889391402295e-13 +1e-16,-0.2,1e-12,-7.131653118353406,-7.131653118353406,1.0000000000000002e-12,4.980094641472322e-12,-25.430238100259924,22.74146186482881 +1e-16,-0.2,0.01,-2.022796075934974,-2.022796075934974,0.009999999999999998,0.030094641472325125,-2.0458519824089643,2.651146451143105 +1e-16,-0.2,0.1,-1.0731118870250627,-1.0731118870250627,0.10000000000000002,0.18452132775990338,-0.5757845610372455,1.2393467490927064 +1e-16,-0.2,0.5,0.29639757570508823,0.29639757570508823,0.5,0.3236235917596897,-0.04392576144192675,0.3545785768505527 +1e-16,-0.2,0.9,2.239812774418023,2.239812774418023,0.9,0.09383236937560444,-2.508380632223081,0.05286527043071554 +1e-16,-0.2,0.99,4.599144024842483,4.599144024842483,0.99,0.009939839275942297,-10.576062880622155,0.005026851414150161 +1e-16,-0.2,0.999999999999,27.63104323789272,27.63104323789272,0.999999999999,9.999778782792827e-13,-381.73727520714885,4.999889391402045e-13 +1e-16,-1e-08,1e-12,-3.3189392331910654,-3.3189392331910654,9.999999999999998e-13,2.7631017298562245e-11,-5.507678816807448,13.815511194192052 +1e-16,-1e-08,0.01,-1.5271796488337523,-1.5271796488337523,0.009999999999999995,0.046051700799501286,-1.1661388399059913,2.3025851106670396 +1e-16,-1e-08,0.1,-0.8340324567608813,-0.8340324567608813,0.09999999999999999,0.2302585066484555,-0.34780506946529566,1.1512925509152714 +1e-16,-1e-08,0.5,0.3665129171159284,0.3665129171159284,0.5,0.3465735890788401,-0.06716585920641371,0.34657359068035015 +1e-16,-1e-08,0.9,2.2503673267856428,2.2503673267856428,0.9,0.0948244640420899,-2.53207655273218,0.052680257838163824 +1e-16,-1e-08,0.99,4.600149226726327,4.600149226726327,0.99,0.009949832494466442,-10.58068645407541,0.0050251679268348975 +1e-16,-1e-08,0.999999999999,27.631043237892822,27.631043237892822,0.999999999999,9.999778782793797e-13,-381.73727520715164,4.999889391401878e-13 +1e-16,-1e-12,1e-12,-3.3189390950497724,-3.3189390950497724,9.999999999999947e-13,2.7631021115546663e-11,-5.5076783583249,13.815510558027901 +1e-16,-1e-12,0.01,-1.5271796258102037,-1.5271796258102037,0.010000000000000004,0.046051701859774884,-1.166138804744897,2.3025850929958134 +1e-16,-1e-12,0.1,-0.8340324452491071,-0.8340324452491071,0.09999999999999999,0.23025850929913946,-0.34780505986410243,1.1512925464974648 +1e-16,-1e-12,0.5,0.36651292058131774,0.36651292058131774,0.5,0.3465735902798526,-0.06716586047652366,0.34657359028001267 +1e-16,-1e-12,0.9,2.2503673273123925,2.2503673273123925,0.9,0.09482446409203868,-2.5320765539175607,0.05268025782891405 +1e-16,-1e-12,0.99,4.600149226776573,4.600149226776573,0.99,0.009949832494966393,-10.580686454306553,0.005025167926750731 +1e-16,-1e-12,0.999999999999,27.631043237892822,27.631043237892822,0.999999999999,9.999778782793797e-13,-381.73727520715164,4.999889391401878e-13 +1e-16,-1e-16,1e-12,-3.318939095035958,-3.318939095035958,9.999999999999992e-13,2.7631021115928475e-11,-5.507678358279051,13.815510557964284 +1e-16,-1e-16,0.01,-1.5271796258079013,-1.5271796258079013,0.010000000000000005,0.04605170185988092,-1.166138804741381,2.3025850929940463 +1e-16,-1e-16,0.1,-0.834032445247956,-0.834032445247956,0.1,0.23025850929940453,-0.3478050598631423,1.151292546497023 +1e-16,-1e-16,0.5,0.3665129205816643,0.3665129205816643,0.5,0.34657359027997264,-0.06716586047665067,0.34657359027997264 +1e-16,-1e-16,0.9,2.2503673273124454,2.2503673273124454,0.9,0.09482446409204366,-2.5320765539176793,0.05268025782891313 +1e-16,-1e-16,0.99,4.600149226776578,4.600149226776578,0.99,0.00994983249496644,-10.580686454306576,0.005025167926750723 +1e-16,-1e-16,0.999999999999,27.631043237892822,27.631043237892822,0.999999999999,9.999778782793797e-13,-381.73727520715164,4.999889391401878e-13 +1e-16,0.0,1e-12,-3.3189390950359567,-3.3189390950359567,9.999999999999978e-13,2.7631021115928478e-11,-5.507678358279047,13.815510557964279 +1e-16,0.0,0.01,-1.5271796258079011,-1.5271796258079011,0.010000000000000005,0.046051701859880924,-1.1661388047413805,2.302585092994046 +1e-16,0.0,0.1,-0.8340324452479558,-0.8340324452479558,0.1,0.23025850929940456,-0.3478050598631422,1.151292546497023 +1e-16,0.0,0.5,0.36651292058166435,0.36651292058166435,0.5,0.34657359027997264,-0.06716586047665069,0.34657359027997264 +1e-16,0.0,0.9,2.2503673273124454,2.2503673273124454,0.9,0.09482446409204366,-2.5320765539176793,0.05268025782891313 +1e-16,0.0,0.99,4.600149226776578,4.600149226776578,0.99,0.00994983249496644,-10.580686454306576,0.005025167926750723 +1e-16,0.0,0.999999999999,27.631043237892822,27.631043237892822,0.999999999999,9.999778782793797e-13,-381.73727520715164,4.999889391401876e-13 +1e-16,1e-16,1e-12,-3.3189390950359554,-3.3189390950359554,9.999999999999963e-13,2.763102111592848e-11,-5.507678358279042,13.815510557964272 +1e-16,1e-16,0.01,-1.527179625807901,-1.527179625807901,0.010000000000000004,0.04605170185988093,-1.16613880474138,2.302585092994046 +1e-16,1e-16,0.1,-0.8340324452479557,-0.8340324452479557,0.1,0.23025850929940458,-0.3478050598631421,1.151292546497023 +1e-16,1e-16,0.5,0.36651292058166435,0.36651292058166435,0.5,0.3465735902799727,-0.0671658604766507,0.34657359027997264 +1e-16,1e-16,0.9,2.2503673273124454,2.2503673273124454,0.9,0.09482446409204366,-2.5320765539176793,0.05268025782891313 +1e-16,1e-16,0.99,4.600149226776578,4.600149226776578,0.99,0.00994983249496644,-10.580686454306576,0.005025167926750723 +1e-16,1e-16,0.999999999999,27.631043237892822,27.631043237892822,0.999999999999,9.999778782793797e-13,-381.73727520715164,4.999889391401878e-13 +1e-16,1e-12,1e-12,-3.318939095022141,-3.318939095022141,1.0000000000000006e-12,2.7631021116310293e-11,-5.507678358233194,13.815510557900655 +1e-16,1e-12,0.01,-1.5271796258055987,-1.5271796258055987,0.009999999999999995,0.04605170185998693,-1.166138804737864,2.302585092992279 +1e-16,1e-12,0.1,-0.8340324452468045,-0.8340324452468045,0.1,0.23025850929966965,-0.34780505986218196,1.151292546496581 +1e-16,1e-12,0.5,0.3665129205820109,0.3665129205820109,0.5,0.34657359028009277,-0.06716586047677771,0.3465735902799326 +1e-16,1e-12,0.9,2.250367327312498,2.250367327312498,0.9,0.09482446409204869,-2.532076553917798,0.052680257828912204 +1e-16,1e-12,0.99,4.600149226776583,4.600149226776583,0.99,0.009949832494966488,-10.5806864543066,0.005025167926750714 +1e-16,1e-12,0.999999999999,27.631043237892822,27.631043237892822,0.999999999999,9.999778782793797e-13,-381.73727520715164,4.999889391401878e-13 +1e-16,1e-08,1e-12,-3.3189389568808543,-3.3189389568808543,9.999999999999996e-13,2.7631024933295522e-11,-5.507677899750686,13.815509921736506 +1e-16,1e-08,0.01,-1.5271796027820503,-1.5271796027820503,0.010000000000000002,0.046051702920260555,-1.1661387695767704,2.302585075321052 +1e-16,1e-08,0.1,-0.8340324337350303,-0.8340324337350303,0.10000000000000002,0.23025851195035366,-0.3478050502609889,1.1512925420787745 +1e-16,1e-08,0.5,0.3665129240474002,0.3665129240474002,0.5,0.3465735914811052,-0.06716586174688768,0.34657358987959513 +1e-16,1e-08,0.9,2.250367327839248,2.250367327839248,0.9,0.09482446414199744,-2.5320765551031785,0.05268025781966243 +1e-16,1e-08,0.99,4.600149226826829,4.600149226826829,0.99,0.00994983249546644,-10.58068645453774,0.005025167926666549 +1e-16,1e-08,0.999999999999,27.631043237892822,27.631043237892822,0.999999999999,9.999778782793797e-13,-381.73727520715164,4.999889391401878e-13 +1e-16,0.2,1e-12,-1.605448895167694,-1.605448895167694,9.999999999998812e-13,1.2509432157546716e-09,-1.2887330774975845,4.889559251099753 +1e-16,0.2,0.01,-1.1017620387373555,-1.1017620387373555,0.009999999999999995,0.07559432157547898,-0.6069397950013469,1.9540237348449871 +1e-16,0.2,0.1,-0.6125948684262535,-0.6125948684262535,0.1,0.29244659623055674,-0.18763623641108945,1.0632383439013395 +1e-16,0.2,0.5,0.4350270118170773,0.4350270118170773,0.5,0.3717458874925875,-0.09462425050524775,0.33856860370939257 +1e-16,0.2,0.9,2.2608848775495884,2.2608848775495884,0.9,0.09583059420060779,-2.5558002147662084,0.052495245227110715 +1e-16,0.2,0.99,4.601154092013182,4.601154092013182,0.99,0.009959839114328298,-10.585309489224828,0.005023484439351285 +1e-16,0.2,0.999999999999,27.63104323789292,27.63104323789292,0.999999999999,9.999778782794803e-13,-381.73727520715437,4.999889391401712e-13 +1e-16,0.5,1e-12,-0.6931461805594453,-0.6931461805594453,1.0000000000181267e-12,1.9999980000181264e-06,-0.24022581381207359,1.9999723689512532 +1e-16,0.5,0.01,-0.5877866649021191,-0.5877866649021191,0.009999999999999993,0.17999999999999994,-0.17274658171837798,1.488314423779101 +1e-16,0.5,0.1,-0.3130167724937737,-0.3130167724937737,0.10000000000000002,0.43245553203367587,-0.04898974993120944,0.9351112785326837 +1e-16,0.5,0.5,0.5347999967395703,0.5347999967395703,0.5,0.4142135623730951,-0.14300551825632224,0.3265946759715075 +1e-16,0.5,0.9,2.2765918251691444,2.2765918251691444,0.9,0.09736659610102759,-2.5914351692134883,0.05221774429386872 +1e-16,0.5,0.99,4.6026607585604795,4.6026607585604795,0.99,0.00997487421323992,-10.592243029196265,0.005020959209740046 +1e-16,0.5,0.999999999999,27.63104323789307,27.63104323789307,0.999999999999,9.999778782796311e-13,-381.7372752071585,4.999889391401461e-13 +1e-16,1.0,1e-12,1.0000000000005e-12,1.0000000000005e-12,1e-12,0.999999999999,-5.000000000005e-25,0.999999999972369 +1e-16,1.0,0.01,0.010050335853501442,0.010050335853501442,0.01,0.99,-5.0504625384088275e-05,0.9534831294344637 +1e-16,1.0,0.1,0.10536051565782631,0.10536051565782631,0.1,0.9,-0.005550419129841532,0.7441572118895504 +1e-16,1.0,0.5,0.6931471805599453,0.6931471805599453,0.5,0.5,-0.2402265069591007,0.30685281944005466 +1e-16,1.0,0.9,2.3025850929940455,2.3025850929940455,0.9,0.10000000000000002,-2.650949055239199,0.051755359079563266 +1e-16,1.0,0.99,4.605170185988089,4.605170185988089,0.99,0.010000000000000016,-10.603796220956788,0.005016750503357325 +1e-16,1.0,0.999999999999,27.63104323789332,27.63104323789332,0.999999999999,9.999778782798823e-13,-381.73727520716545,4.999889391401045e-13 +1e-16,2.0,1e-12,0.6931471805599453,0.6931471805599453,9.122404055111098e-10,548101133.1874301,-0.2402265069591007,0.49999999999999994 +1e-16,2.0,0.01,0.6932471855602786,0.6932471855602786,0.010000000000000054,49.994999999999735,-0.2402958301436237,0.49953943692509367 +1e-16,2.0,0.1,0.7031975164134467,0.7031975164134467,0.10000000000000009,4.949999999999996,-0.24724337354501985,0.47674156471723184 +1e-16,2.0,0.5,0.9808292530117262,0.9808292530117262,0.5,0.7500000000000001,-0.4810130117817704,0.26895093981335155 +1e-16,2.0,0.9,2.353878387381596,2.353878387381596,0.9,0.10555555555555557,-2.770371731291092,0.050831485879793116 +1e-16,2.0,0.99,4.610182727811634,4.610182727811634,0.99,0.01005050505050506,-10.62689239190636,0.005008333164986811 +1e-16,2.0,0.999999999999,27.63104323789382,27.63104323789382,0.999999999999,9.999778782803813e-13,-381.73727520717927,4.999889391400212e-13 +1e-12,-2.0,1e-12,-54.56889505278603,-54.56889505278603,1.0000000000000002e-12,4.999999999727157e-13,-1488.8821536139058,27.13102111740906 +1e-12,-2.0,0.01,-8.517093186452174,-8.517093186452174,0.010000000000000004,0.004999499999957421,-36.27043817325205,4.1056307490979655 +1e-12,-2.0,0.1,-3.9019726695822574,-3.9019726695822574,0.09999999999999999,0.04949999999980685,-7.612695357073542,1.8258435282839383 +1e-12,-2.0,0.5,-0.4054651081082466,-0.4054651081082466,0.5,0.18749999999992398,-0.08220097694660493,0.42419624074676576 +1e-12,-2.0,0.9,2.1431573560636474,2.1431573560636474,0.9,0.08550000000018322,-2.296561726426502,0.05452902977791629 +1e-12,-2.0,0.99,4.590082056094097,4.590082056094097,0.99,0.009850500000045226,-10.534426640854626,0.005042002688491493 +1e-12,-2.0,0.999999999999,27.63104323751012,27.63104323751012,0.999999999999,9.999778783060102e-13,-381.7372752000928,4.999889391265407e-13 +1e-12,-1.0,1e-12,-27.631021116309284,-27.631021116309284,1.0000000000000004e-12,9.999999999713694e-13,-381.73666396144887,26.631021116692022 +1e-12,-1.0,0.01,-4.595119850145148,-4.595119850145148,0.009999999999999998,0.009899999999954508,-10.55756321858281,3.6516870565704074 +1e-12,-1.0,0.1,-2.197224577338633,-2.197224577338633,0.1,0.08999999999980225,-2.4138979216286995,1.5584278811079193 +1e-12,-1.0,0.5,0.0,0.0,0.5,0.25,0.0,0.38629436111989063 +1e-12,-1.0,0.9,2.197224577333806,2.197224577333806,0.9,0.09000000000019771,-2.4138979216216288,0.05360515657814522 +1e-12,-1.0,0.99,4.595119850124031,4.595119850124031,0.99,0.009900000000045504,-10.557563218518121,0.005033585350120993 +1e-12,-1.0,0.999999999999,27.63104323751062,27.63104323751062,0.999999999999,9.999778783065092e-13,-381.73727520010664,4.999889391264574e-13 +1e-12,-0.5,1e-12,-14.50865673862897,-14.50865673862897,1.0000000000000004e-12,1.9999979999709837e-12,-105.2505601791729,25.63104874734917 +1e-12,-0.5,0.01,-2.8903717579003416,-2.8903717579003416,0.010000000000000004,0.017999999999947978,-4.1771244494299316,3.116855762217999 +1e-12,-0.5,0.1,-1.4643093189918686,-1.4643093189918686,0.10000000000000002,0.13675444679643217,-1.0721008908426917,1.3674738144633642 +1e-12,-0.5,0.5,0.18822640645958,0.18822640645958,0.5,0.2928932188135076,-0.01771459004434462,0.3665525045883688 +1e-12,-0.5,0.9,2.2239115673377587,2.2239115673377587,0.9,0.09237006350928055,-2.472891329671176,0.05314277136383937 +1e-12,-0.5,0.99,4.5976355906231605,4.5976355906231605,0.99,0.00992487452893813,-10.569126512098586,0.005029376643738279 +1e-12,-0.5,0.999999999999,27.63104323751087,27.63104323751087,0.999999999999,9.999778783067607e-13,-381.7372752001136,4.999889391264157e-13 +1e-12,-0.2,1e-12,-7.131653118378834,-7.131653118378834,1.0000000000000002e-12,4.98009464143681e-12,-25.43023810038082,22.74146186499098 +1e-12,-0.2,0.01,-2.0227960759370194,-2.0227960759370194,0.010000000000000004,0.03009464147226427,-2.0458519824117225,2.6511464511484673 +1e-12,-0.2,0.1,-1.0731118870256384,-1.0731118870256384,0.1,0.18452132775970537,-0.5757845610376574,1.2393467490940362 +1e-12,-0.2,0.5,0.2963975757050443,0.2963975757050443,0.5,0.32362359175978556,-0.04392576144191808,0.35457857685044764 +1e-12,-0.2,0.9,2.239812774415515,2.239812774415515,0.9,0.09383236937581459,-2.508380632219336,0.05286527043059715 +1e-12,-0.2,0.99,4.599144024831907,4.599144024831907,0.99,0.009939839275988013,-10.576062880589731,0.005026851414127044 +1e-12,-0.2,0.999999999999,27.631043237511022,27.631043237511022,0.999999999999,9.999778783069079e-13,-381.7372752001177,4.999889391263907e-13 +1e-12,-1e-08,1e-12,-3.3189392331965726,-3.3189392331965726,9.999999999999992e-13,2.7631017298470532e-11,-5.507678816819634,13.8155111942379 +1e-12,-1e-08,0.01,-1.5271796488349183,-1.5271796488349183,0.009999999999999998,0.046051700799430974,-1.1661388399071786,2.302585110670556 +1e-12,-1e-08,0.1,-0.8340324567612291,-0.8340324567612291,0.1,0.2302585066482635,-0.347805069465489,1.1512925509162315 +1e-12,-1e-08,0.5,0.36651291711586126,0.36651291711586126,0.5,0.3465735890789671,-0.0671658592063973,0.34657359068022314 +1e-12,-1e-08,0.9,2.250367326783111,2.250367326783111,0.9,0.09482446404230326,-2.5320765527283817,0.05268025783804529 +1e-12,-1e-08,0.99,4.600149226715747,4.600149226715747,0.99,0.009949832494512211,-10.580686454042965,0.005025167926811783 +1e-12,-1e-08,0.999999999999,27.631043237511122,27.631043237511122,0.999999999999,9.999778783070084e-13,-381.73727520012045,4.99988939126374e-13 +1e-12,-1e-12,1e-12,-3.3189390950552795,-3.3189390950552795,9.999999999999941e-13,2.7631021115454953e-11,-5.507678358337085,13.81551055807375 +1e-12,-1e-12,0.01,-1.52717962581137,-1.52717962581137,0.009999999999999997,0.04605170185970454,-1.1661388047460841,2.3025850929993292 +1e-12,-1e-12,0.1,-0.8340324452494549,-0.8340324452494549,0.1,0.23025850929894745,-0.3478050598642958,1.151292546498425 +1e-12,-1e-12,0.5,0.36651292058125057,0.36651292058125057,0.5,0.3465735902799796,-0.06716586047650726,0.34657359027988566 +1e-12,-1e-12,0.9,2.2503673273098608,2.2503673273098608,0.9,0.09482446409225205,-2.5320765539137624,0.05268025782879551 +1e-12,-1e-12,0.99,4.600149226765994,4.600149226765994,0.99,0.009949832495012155,-10.580686454274108,0.005025167926727617 +1e-12,-1e-12,0.999999999999,27.631043237511122,27.631043237511122,0.999999999999,9.999778783070084e-13,-381.73727520012045,4.99988939126374e-13 +1e-12,-1e-16,1e-12,-3.3189390950414652,-3.3189390950414652,9.999999999999984e-13,2.7631021115836764e-11,-5.507678358291237,13.815510558010134 +1e-12,-1e-16,0.01,-1.5271796258090675,-1.5271796258090675,0.009999999999999998,0.04605170185981056,-1.166138804742568,2.302585092997562 +1e-12,-1e-16,0.1,-0.8340324452483037,-0.8340324452483037,0.1,0.23025850929921252,-0.34780505986333565,1.151292546497983 +1e-12,-1e-16,0.5,0.3665129205815971,0.3665129205815971,0.5,0.34657359028009965,-0.06716586047663427,0.34657359027984563 +1e-12,-1e-16,0.9,2.2503673273099136,2.2503673273099136,0.9,0.09482446409225702,-2.532076553913881,0.05268025782879459 +1e-12,-1e-16,0.99,4.600149226765998,4.600149226765998,0.99,0.00994983249501221,-10.580686454274131,0.005025167926727608 +1e-12,-1e-16,0.999999999999,27.631043237511122,27.631043237511122,0.999999999999,9.999778783070084e-13,-381.73727520012045,4.99988939126374e-13 +1e-12,0.0,1e-12,-3.318939095041464,-3.318939095041464,9.999999999999972e-13,2.7631021115836764e-11,-5.507678358291232,13.815510558010127 +1e-12,0.0,0.01,-1.5271796258090673,-1.5271796258090673,0.009999999999999998,0.04605170185981058,-1.1661388047425676,2.302585092997562 +1e-12,0.0,0.1,-0.8340324452483036,-0.8340324452483036,0.1,0.23025850929921254,-0.34780505986333554,1.151292546497983 +1e-12,0.0,0.5,0.3665129205815972,0.3665129205815972,0.5,0.34657359028009965,-0.06716586047663428,0.34657359027984563 +1e-12,0.0,0.9,2.2503673273099136,2.2503673273099136,0.9,0.09482446409225703,-2.532076553913881,0.05268025782879459 +1e-12,0.0,0.99,4.600149226765998,4.600149226765998,0.99,0.00994983249501221,-10.580686454274131,0.005025167926727608 +1e-12,0.0,0.999999999999,27.631043237511122,27.631043237511122,0.999999999999,9.999778783070084e-13,-381.73727520012045,4.999889391263738e-13 +1e-12,1e-16,1e-12,-3.3189390950414626,-3.3189390950414626,9.999999999999957e-13,2.7631021115836768e-11,-5.507678358291227,13.815510558010121 +1e-12,1e-16,0.01,-1.527179625809067,-1.527179625809067,0.009999999999999997,0.046051701859810584,-1.1661388047425674,2.3025850929975618 +1e-12,1e-16,0.1,-0.8340324452483034,-0.8340324452483034,0.1,0.23025850929921257,-0.3478050598633355,1.151292546497983 +1e-12,1e-16,0.5,0.3665129205815972,0.3665129205815972,0.5,0.3465735902800997,-0.0671658604766343,0.34657359027984563 +1e-12,1e-16,0.9,2.2503673273099136,2.2503673273099136,0.9,0.09482446409225703,-2.532076553913881,0.05268025782879459 +1e-12,1e-16,0.99,4.600149226765998,4.600149226765998,0.99,0.00994983249501221,-10.580686454274131,0.005025167926727608 +1e-12,1e-16,0.999999999999,27.631043237511122,27.631043237511122,0.999999999999,9.999778783070084e-13,-381.73727520012045,4.99988939126374e-13 +1e-12,1e-12,1e-12,-3.3189390950276483,-3.3189390950276483,1e-12,2.763102111621858e-11,-5.507678358245379,13.815510557946505 +1e-12,1e-12,0.01,-1.5271796258067647,-1.5271796258067647,0.009999999999999998,0.04605170185991662,-1.1661388047390513,2.3025850929957947 +1e-12,1e-12,0.1,-0.8340324452471523,-0.8340324452471523,0.10000000000000002,0.23025850929947764,-0.34780505986237537,1.1512925464975412 +1e-12,1e-12,0.5,0.36651292058194374,0.36651292058194374,0.5,0.3465735902802198,-0.0671658604767613,0.3465735902798056 +1e-12,1e-12,0.9,2.250367327309966,2.250367327309966,0.9,0.09482446409226204,-2.5320765539139996,0.05268025782879366 +1e-12,1e-12,0.99,4.600149226766003,4.600149226766003,0.99,0.009949832495012257,-10.580686454274154,0.0050251679267276006 +1e-12,1e-12,0.999999999999,27.631043237511122,27.631043237511122,0.999999999999,9.999778783070084e-13,-381.73727520012045,4.99988939126374e-13 +1e-12,1e-08,1e-12,-3.3189389568863614,-3.3189389568863614,9.99999999999999e-13,2.7631024933203808e-11,-5.5076778997628715,13.815509921782354 +1e-12,1e-08,0.01,-1.5271796027832163,-1.5271796027832163,0.010000000000000005,0.04605170292019024,-1.1661387695779577,2.3025850753245685 +1e-12,1e-08,0.1,-0.8340324337353782,-0.8340324337353782,0.1,0.2302585119501616,-0.34780505026118225,1.1512925420797346 +1e-12,1e-08,0.5,0.36651292404733304,0.36651292404733304,0.5,0.34657359148123223,-0.06716586174687127,0.3465735898794681 +1e-12,1e-08,0.9,2.2503673278367162,2.2503673278367162,0.9,0.0948244641422108,-2.5320765550993802,0.05268025781954389 +1e-12,1e-08,0.99,4.60014922681625,4.60014922681625,0.99,0.009949832495512201,-10.580686454505296,0.005025167926643434 +1e-12,1e-08,0.999999999999,27.631043237511122,27.631043237511122,0.999999999999,9.999778783070084e-13,-381.73727520012045,4.99988939126374e-13 +1e-12,0.2,1e-12,-1.6054488951689825,-1.6054488951689825,9.999999999999806e-13,1.2509432157527627e-09,-1.2887330774989636,4.8895592511076025 +1e-12,0.2,0.01,-1.1017620387379623,-1.1017620387379623,0.009999999999999997,0.07559432157539571,-0.6069397950017926,1.9540237348471396 +1e-12,0.2,0.1,-0.6125948684264412,-0.6125948684264412,0.1,0.2924465962303776,-0.18763623641116606,1.0632383439019908 +1e-12,0.2,0.5,0.43502701181698267,0.43502701181698267,0.5,0.3717458874927492,-0.09462425050522032,0.3385686037092453 +1e-12,0.2,0.9,2.2608848775470327,2.2608848775470327,0.9,0.09583059420082445,-2.555800214762357,0.05249524522699204 +1e-12,0.2,0.99,4.601154092002599,4.601154092002599,0.99,0.009959839114374114,-10.585309489192362,0.005023484439328174 +1e-12,0.2,0.999999999999,27.63104323751122,27.63104323751122,0.999999999999,9.999778783071088e-13,-381.73727520012324,4.999889391263573e-13 +1e-12,0.5,1e-12,-0.6931461805596856,-0.6931461805596856,9.999999999171842e-13,1.999997999915798e-06,-0.24022581381218458,1.9999723689526392 +1e-12,0.5,0.01,-0.5877866649022917,-0.5877866649022917,0.01000000000000001,0.17999999999989427,-0.17274658171844567,1.4883144237799757 +1e-12,0.5,0.1,-0.3130167724938227,-0.3130167724938227,0.1,0.4324555320335405,-0.04898974993121966,0.9351112785329764 +1e-12,0.5,0.5,0.5347999967394274,0.5347999967394274,0.5,0.4142135623733166,-0.14300551825627125,0.32659467597133285 +1e-12,0.5,0.9,2.276591825166553,2.276591825166553,0.9,0.09736659610124924,-2.5914351692095554,0.05221774429374985 +1e-12,0.5,0.99,4.602660758549888,4.602660758549888,0.99,0.009974874213285832,-10.592243029163766,0.0050209592097169384 +1e-12,0.5,0.999999999999,27.63104323751137,27.63104323751137,0.999999999999,9.999778783072597e-13,-381.7372752001274,4.999889391263324e-13 +1e-12,1.0,1e-12,1.0000000000005e-12,1.0000000000005e-12,1e-12,0.999999999999,-5.000000000005e-25,0.999999999972369 +1e-12,1.0,0.01,0.010050335853501392,0.010050335853501392,0.01,0.99000000000001,-5.0504625384087936e-05,0.9534831294344541 +1e-12,1.0,0.1,0.10536051565782076,0.10536051565782076,0.1,0.9000000000000948,-0.005550419129841141,0.744157211889472 +1e-12,1.0,0.5,0.693147180559705,0.693147180559705,0.5,0.5000000000003466,-0.2402265069589897,0.306852819439842 +1e-12,1.0,0.9,2.302585092991395,2.302585092991395,0.9,0.10000000000023022,-2.65094905523513,0.051755359079444105 +1e-12,1.0,0.99,4.605170185977487,4.605170185977487,0.99,0.010000000000046057,-10.603796220924236,0.0050167505033342245 +1e-12,1.0,0.999999999999,27.631043237511623,27.631043237511623,0.999999999999,9.999778783075075e-13,-381.73727520013426,4.999889391262907e-13 +1e-12,2.0,0.01,0.6932471855600384,0.6932471855600384,0.01000000000000103,49.995000000029506,-0.24029583014351266,0.4995394369247474 +1e-12,2.0,0.1,0.7031975164131995,0.7031975164131995,0.09999999999999995,4.950000000003484,-0.24724337354490394,0.4767415647168966 +1e-12,2.0,0.5,0.9808292530112452,0.9808292530112452,0.5,0.7500000000007356,-0.4810130117814559,0.26895093981308776 +1e-12,2.0,0.9,2.3538783873788263,2.3538783873788263,0.9,0.10555555555580397,-2.770371731286745,0.050831485879673476 +1e-12,2.0,0.99,4.610182727801008,4.610182727801008,0.99,0.010050505050551394,-10.626892391873701,0.005008333164963724 +1e-12,2.0,0.999999999999,27.63104323751212,27.63104323751212,0.999999999999,9.9997787830801e-13,-381.7372752001481,4.999889391262074e-13 +1e-08,-2.0,1e-12,-54.568909940121394,-54.568909940121394,1.0000000000000006e-12,4.999997271555995e-13,-1488.882695204212,27.131035921031028 +1e-08,-2.0,0.01,-8.517093549120297,-8.517093549120297,0.009999999999999997,0.004999499574187942,-36.270440232504185,4.10563109874341 +1e-08,-2.0,0.1,-3.901972745701599,-3.901972745701599,0.10000000000000002,0.04949999806852357,-7.612695555083937,1.8258435995207307 +1e-08,-2.0,0.5,-0.4054651089301742,-0.4054651089301742,0.5,0.1874999992397529,-0.08220097716878023,0.4241962424665615 +1e-08,-2.0,0.9,2.143157333100327,2.143157333100327,0.9,0.08550000183239954,-2.296561693617163,0.054529028609390254 +1e-08,-2.0,0.99,4.590081950760367,4.590081950760367,0.99,0.009850500452146053,-10.53442631852765,0.005042002457082581 +1e-08,-2.0,0.999999999999,27.631039420519457,27.631039420519457,0.999999999999,9.999781545827378e-13,-381.73720488847096,4.999888009882151e-13 +1e-08,-1.0,1e-12,-27.63102493329454,-27.63102493329454,9.99999999999999e-13,9.99999723688826e-13,-381.73673427291646,26.631028474380265 +1e-08,-1.0,0.01,-4.595119955710223,-4.595119955710223,0.010000000000000004,0.009899999545083148,-10.557563541972263,3.651687224353028 +1e-08,-1.0,0.1,-2.197224601475199,-2.197224601475199,0.09999999999999999,0.0899999980224979,-2.413897956984336,1.558427915346656 +1e-08,-1.0,0.5,0.0,0.0,0.5,0.25,0.0,0.38629436111989063 +1e-08,-1.0,0.9,2.1972245531972407,2.1972245531972407,0.9,0.09000000197750212,-2.4138978862659926,0.05360515540043734 +1e-08,-1.0,0.99,4.595119744558959,4.595119744558959,0.99,0.009900000454916881,-10.557562895128681,0.005033585118844848 +1e-08,-1.0,0.999999999999,27.631039420519958,27.631039420519958,0.999999999999,9.999781545832368e-13,-381.73720488848477,4.999888009881317e-13 +1e-08,-0.5,1e-12,-14.508657791029371,-14.508657791029371,1.0000000000000016e-12,1.9999977098271798e-12,-105.25057035845042,25.631052465698446 +1e-08,-0.5,0.01,-2.8903717996674096,-2.8903717996674096,0.009999999999999998,0.01799999947973309,-4.1771245299115005,3.1168558522977103 +1e-08,-0.5,0.1,-1.4643093297118055,-1.4643093297118055,0.1,0.13675444479412432,-1.0721009013075606,1.3674738344854085 +1e-08,-0.5,0.5,0.18822640628245182,0.18822640628245182,0.5,0.2928932193647549,-0.01771459002211782,0.3665525038984892 +1e-08,-0.5,0.9,2.2239115426113183,2.2239115426113183,0.9,0.0923700655633037,-2.472891293011565,0.05314277018210932 +1e-08,-0.5,0.99,4.597635484942466,4.597635484942466,0.99,0.009924874985202073,-10.569126188177707,0.0050293764125289965 +1e-08,-0.5,0.999999999999,27.631039420520207,27.631039420520207,0.999999999999,9.999781545834882e-13,-381.73720488849165,4.9998880098809e-13 +1e-08,-0.2,1e-12,-7.131653372655791,-7.131653372655791,1.0000000000000002e-12,4.980094286309264e-12,-25.430239309324193,22.741463486671027 +1e-08,-0.2,0.01,-2.0227960963934937,-2.0227960963934937,0.009999999999999998,0.03009464086357191,-2.0458520099979065,2.6511465047703915 +1e-08,-0.2,0.1,-1.0731118927829082,-1.0731118927829082,0.10000000000000002,0.1845213257797831,-0.5757845651564538,1.2393467623922836 +1e-08,-0.2,0.5,0.2963975752658306,0.2963975752658306,0.5,0.3236235927189021,-0.04392576135513016,0.3545785757995904 +1e-08,-0.2,0.9,2.239812749334217,2.239812749334217,0.9,0.09383237147727386,-2.5083805947677287,0.052865269246632485 +1e-08,-0.2,0.99,4.599143919081857,4.599143919081857,0.99,0.009939839733089827,-10.576062556349921,0.005026851182958032 +1e-08,-0.2,0.999999999999,27.63103942052036,27.63103942052036,0.999999999999,9.999781545836355e-13,-381.7372048884958,4.99988800988065e-13 +1e-08,-1e-08,1e-12,-3.3189392882678534,-3.3189392882678534,1.0000000000000054e-12,2.7631016381505748e-11,-5.50767893867179,13.815511652720476 +1e-08,-1e-08,0.01,-1.5271796604951406,-1.5271796604951406,0.009999999999999997,0.046051700096209104,-1.1661388517786813,2.302585145831651 +1e-08,-1e-08,0.1,-0.834032460238932,-0.834032460238932,0.1,0.23025850472802487,-0.3478050713991671,1.151292560517425 +1e-08,-1e-08,0.5,0.3665129164442698,0.3665129164442698,0.5,0.3465735903490771,-0.06716585904229934,0.3465735894101132 +1e-08,-1e-08,0.9,2.2503673014648777,2.2503673014648777,0.9,0.09482446617598865,-2.5320765147448316,0.05268025665266454 +1e-08,-1e-08,0.99,4.600149120919465,4.600149120919465,0.99,0.00994983295217359,-10.580686129590509,0.005025167695669681 +1e-08,-1e-08,0.999999999999,27.63103942052046,27.63103942052046,0.999999999999,9.999781545837358e-13,-381.7372048884986,4.999888009880483e-13 +1e-08,-1e-12,1e-12,-3.318939150126556,-3.318939150126556,9.999999999999965e-13,2.7631020198489972e-11,-5.5076784801892265,13.815511016556286 +1e-08,-1e-12,0.01,-1.5271796374715918,-1.5271796374715918,0.01,0.046051701156482674,-1.1661388166175863,2.3025851281604237 +1e-08,-1e-12,0.1,-0.8340324487271576,-0.8340324487271576,0.1,0.23025850737870882,-0.34780506179797377,1.1512925560996181 +1e-08,-1e-12,0.5,0.3665129199096592,0.3665129199096592,0.5,0.34657359155008954,-0.0671658603124093,0.3465735890097757 +1e-08,-1e-12,0.9,2.2503673019916275,2.2503673019916275,0.9,0.09482446622593743,-2.5320765159302123,0.05268025664341477 +1e-08,-1e-12,0.99,4.600149120969711,4.600149120969711,0.99,0.009949832952673543,-10.58068612982165,0.005025167695585515 +1e-08,-1e-12,0.999999999999,27.63103942052046,27.63103942052046,0.999999999999,9.999781545837358e-13,-381.7372048884986,4.999888009880483e-13 +1e-08,-1e-16,1e-12,-3.3189391501127417,-3.3189391501127417,1.0000000000000008e-12,2.7631020198871784e-11,-5.507678480143379,13.81551101649267 +1e-08,-1e-16,0.01,-1.5271796374692894,-1.5271796374692894,0.010000000000000002,0.04605170115658871,-1.1661388166140703,2.3025851281586567 +1e-08,-1e-16,0.1,-0.8340324487260065,-0.8340324487260065,0.10000000000000002,0.23025850737897388,-0.34780506179701365,1.1512925560991762 +1e-08,-1e-16,0.5,0.3665129199100057,0.3665129199100057,0.5,0.3465735915502096,-0.06716586031253631,0.34657358900973567 +1e-08,-1e-16,0.9,2.2503673019916803,2.2503673019916803,0.9,0.09482446622594241,-2.532076515930331,0.052680256643413845 +1e-08,-1e-16,0.99,4.600149120969716,4.600149120969716,0.99,0.00994983295267359,-10.580686129821673,0.005025167695585507 +1e-08,-1e-16,0.999999999999,27.63103942052046,27.63103942052046,0.999999999999,9.999781545837358e-13,-381.7372048884986,4.999888009880483e-13 +1e-08,0.0,1e-12,-3.3189391501127403,-3.3189391501127403,9.999999999999994e-13,2.7631020198871787e-11,-5.507678480143374,13.815511016492662 +1e-08,0.0,0.01,-1.5271796374692892,-1.5271796374692892,0.01,0.046051701156588715,-1.16613881661407,2.3025851281586562 +1e-08,0.0,0.1,-0.8340324487260063,-0.8340324487260063,0.10000000000000002,0.2302585073789739,-0.34780506179701354,1.1512925560991762 +1e-08,0.0,0.5,0.36651291991000573,0.36651291991000573,0.5,0.34657359155020967,-0.06716586031253632,0.34657358900973567 +1e-08,0.0,0.9,2.2503673019916803,2.2503673019916803,0.9,0.09482446622594241,-2.532076515930331,0.052680256643413845 +1e-08,0.0,0.99,4.600149120969716,4.600149120969716,0.99,0.00994983295267359,-10.580686129821673,0.005025167695585507 +1e-08,0.0,0.999999999999,27.63103942052046,27.63103942052046,0.999999999999,9.999781545837358e-13,-381.7372048884986,4.999888009880481e-13 +1e-08,1e-16,1e-12,-3.318939150112739,-3.318939150112739,9.999999999999982e-13,2.763102019887179e-11,-5.50767848014337,13.815511016492657 +1e-08,1e-16,0.01,-1.527179637469289,-1.527179637469289,0.01,0.04605170115658872,-1.1661388166140696,2.3025851281586562 +1e-08,1e-16,0.1,-0.8340324487260062,-0.8340324487260062,0.10000000000000002,0.23025850737897394,-0.3478050617970134,1.1512925560991762 +1e-08,1e-16,0.5,0.36651291991000573,0.36651291991000573,0.5,0.34657359155020967,-0.06716586031253634,0.34657358900973567 +1e-08,1e-16,0.9,2.2503673019916803,2.2503673019916803,0.9,0.09482446622594241,-2.532076515930331,0.052680256643413845 +1e-08,1e-16,0.99,4.600149120969716,4.600149120969716,0.99,0.00994983295267359,-10.580686129821673,0.005025167695585507 +1e-08,1e-16,0.999999999999,27.63103942052046,27.63103942052046,0.999999999999,9.999781545837358e-13,-381.7372048884986,4.999888009880483e-13 +1e-08,1e-12,1e-12,-3.3189391500989247,-3.3189391500989247,1.0000000000000024e-12,2.76310201992536e-11,-5.507678480097521,13.81551101642904 +1e-08,1e-12,0.01,-1.5271796374669866,-1.5271796374669866,0.010000000000000002,0.046051701156694755,-1.1661388166105535,2.302585128156889 +1e-08,1e-12,0.1,-0.834032448724855,-0.834032448724855,0.10000000000000002,0.23025850737923903,-0.3478050617960533,1.1512925560987344 +1e-08,1e-12,0.5,0.3665129199103523,0.3665129199103523,0.5,0.34657359155032974,-0.06716586031266335,0.34657358900969565 +1e-08,1e-12,0.9,2.2503673019917327,2.2503673019917327,0.9,0.09482446622594744,-2.5320765159304495,0.052680256643412915 +1e-08,1e-12,0.99,4.600149120969721,4.600149120969721,0.99,0.009949832952673637,-10.580686129821697,0.005025167695585499 +1e-08,1e-12,0.999999999999,27.63103942052046,27.63103942052046,0.999999999999,9.999781545837358e-13,-381.7372048884986,4.999888009880483e-13 +1e-08,1e-08,1e-12,-3.3189390119576334,-3.3189390119576334,9.999999999999974e-13,2.7631024016238637e-11,-5.507678021614998,13.815510380264849 +1e-08,1e-08,0.01,-1.527179614443438,-1.527179614443438,0.010000000000000002,0.04605170221696835,-1.1661387814494595,2.302585110485662 +1e-08,1e-08,0.1,-0.8340324372130808,-0.8340324372130808,0.1,0.23025851002992298,-0.3478050521948602,1.1512925516809276 +1e-08,1e-08,0.5,0.3665129233757416,0.3665129233757416,0.5,0.3465735927513422,-0.06716586158277331,0.34657358860935816 +1e-08,1e-08,0.9,2.250367302518483,2.250367302518483,0.9,0.09482446627589619,-2.53207651711583,0.05268025663416314 +1e-08,1e-08,0.99,4.600149121019967,4.600149121019967,0.99,0.00994983295317359,-10.58068613005284,0.005025167695501332 +1e-08,1e-08,0.999999999999,27.63103942052046,27.63103942052046,0.999999999999,9.999781545837358e-13,-381.7372048884986,4.999888009880483e-13 +1e-08,0.2,1e-12,-1.6054489080550245,-1.6054489080550245,1.0000000000001052e-12,1.2509431956716416e-09,-1.288733091290885,4.889559329599128 +1e-08,0.2,0.01,-1.1017620448067533,-1.1017620448067533,0.009999999999999997,0.07559432074260945,-0.6069397994593684,1.9540237563736786 +1e-08,0.2,0.1,-0.6125948703026158,-0.6125948703026158,0.10000000000000002,0.2924465944390439,-0.1876362371773894,1.063238350414683 +1e-08,0.2,0.5,0.4350270108708348,0.4350270108708348,0.5,0.37174588910978257,-0.0946242502308204,0.33856860223652774 +1e-08,0.2,0.9,2.2608848519915865,2.2608848519915865,0.9,0.09583059636722722,-2.5558001762437423,0.05249524404025368 +1e-08,0.2,0.99,4.6011539861600905,4.6011539861600905,0.99,0.009959839572595844,-10.585309164527237,0.005023484208213033 +1e-08,0.2,0.999999999999,27.63103942052056,27.63103942052056,0.999999999999,9.999781545838364e-13,-381.7372048885013,4.999888009880317e-13 +1e-08,0.5,1e-12,-0.6931461829617035,-0.6931461829617035,9.999999999170951e-13,1.9999979860541856e-06,-0.24022581492215095,1.9999723828139853 +1e-08,0.5,0.01,-0.5877866666295848,-0.5877866666295848,0.010000000000000007,0.17999999894198407,-0.1727465823952989,1.4883144325272146 +1e-08,0.5,0.1,-0.3130167729836712,-0.3130167729836712,0.1,0.4324555306800175,-0.0489897500334402,0.9351112814597389 +1e-08,0.5,0.5,0.5347999953095152,0.5347999953095152,0.5,0.41421356458830916,-0.1430055177464599,0.3265946742248792 +1e-08,0.5,0.9,2.2765917992547933,2.2765917992547933,0.9,0.09736659831766756,-2.5914351298825546,0.05221774310508384 +1e-08,0.5,0.99,4.602660652638051,4.602660652638051,0.99,0.009974874672349554,-10.592242704179599,0.005020958978642334 +1e-08,0.5,0.999999999999,27.631039420520707,27.631039420520707,0.999999999999,9.999781545839873e-13,-381.73720488850546,4.999888009880067e-13 +1e-08,1.0,1e-12,1.0000000000005e-12,1.0000000000005e-12,1e-12,0.999999999999,-5.000000000005e-25,0.999999999972369 +1e-08,1.0,0.01,0.010050335852996395,0.010050335852996395,0.01,0.9900000000994983,-5.050462538070435e-05,0.9534831293386354 +1e-08,1.0,0.1,0.10536051560232211,0.10536051560232211,0.1,0.9000000009482446,-0.005550419125942898,0.7441572111055026 +1e-08,1.0,0.5,0.6931471781576802,0.6931471781576802,0.5,0.5000000034657359,-0.24022650584901853,0.30685281731311304 +1e-08,1.0,0.9,2.3025850664845557,2.3025850664845557,0.9,0.10000000230258507,-2.650949014545628,0.051755357887852106 +1e-08,1.0,0.99,4.60517007995013,4.60517007995013,0.99,0.010000000460517034,-10.603795895408222,0.005016750272327434 +1e-08,1.0,0.999999999999,27.63103942052096,27.63103942052096,0.999999999999,9.999781545842348e-13,-381.7372048885124,4.99988800987965e-13 +1e-08,2.0,0.01,0.6932471831573204,0.6932471831573204,0.009999999999999806,49.9950003465899,-0.24029582903306101,0.49953943346205065 +1e-08,2.0,0.1,0.703197513941013,0.703197513941013,0.1000000000000001,4.950000034808272,-0.247243372385947,0.476741561364797 +1e-08,2.0,0.5,0.9808292482015961,0.9808292482015961,0.5,0.7500000073562195,-0.4810130086364929,0.2689509371754021 +1e-08,2.0,0.9,2.3538783596778794,2.3538783596778794,0.9,0.10555555804020496,-2.7703716878169717,0.05083148468328178 +1e-08,2.0,0.99,4.610182621542712,4.610182621542712,0.99,0.01005050551385172,-10.626892065293596,0.005008332934093508 +1e-08,2.0,0.999999999999,27.631039420521457,27.631039420521457,0.999999999999,9.999781545847375e-13,-381.7372048885262,4.999888009878817e-13 +0.2,-2.0,1e-12,-274635.1358265296,-274635.1358265296,1e-12,9.102821015130396e-18,-13613633.06965924,1490253.464878212 +0.2,-2.0,0.01,-22.46346428040876,-22.46346428040876,0.010000000000000002,0.0009102092782166644,-121.59156309600222,22.550968685087902 +0.2,-2.0,0.1,-5.911665500472362,-5.911665500472362,0.09999999999999999,0.02268214691783631,-13.018693060021882,3.9845987673517684 +0.2,-2.0,0.5,-0.42235885598849304,-0.42235885598849304,0.5,0.17289523340282395,-0.08678303980217154,0.4600288485378639 +0.2,-2.0,0.9,1.7430002770833513,1.7430002770833513,0.9,0.13125576799778568,-1.7347384705431994,0.035520206975593535 +0.2,-2.0,0.99,3.0034483824366025,3.0034483824366025,0.99,0.024668783700222123,-5.852906158318585,0.0020133237247025792 +0.2,-2.0,0.999999999999,4.9800947295412525,4.9800947295412525,0.999999999999,2.5118419776076795e-10,-24.350470258998676,1.990483011994203e-15 +0.2,-1.0,1e-12,-1250.943215754539,-1250.943215754539,1.0000000000000004e-12,3.981071705531788e-15,-28448.277436146924,6689.410059840208 +0.2,-1.0,0.01,-7.534212210670502,-7.534212210670502,0.009999999999999998,0.003949191155217568,-19.925146281698876,9.154204098760381 +0.2,-1.0,0.1,-2.759227869576798,-2.759227869576798,0.1,0.05799546134795289,-3.2526268283023025,2.418439409558303 +0.2,-1.0,0.5,0.0,0.0,0.5,0.25,0.0,0.38629436111989063 +0.2,-1.0,0.9,1.7780299251137288,1.7780299251137288,0.9,0.13966610165238236,-1.8107577895867095,0.03454284207095126 +0.2,-1.0,0.99,3.0054590125163787,3.0054590125163787,0.99,0.02481774017712761,-5.862140179088861,0.0020079384589719094 +0.2,-1.0,0.999999999999,4.980094729541254,4.980094729541254,0.999999999999,2.5118419776092946e-10,-24.350470258998733,1.9904830119936726e-15 +0.2,-0.5,1e-12,-86.02819194565471,-86.02819194565471,1e-12,1.0985596644575949e-13,-890.5558307396802,466.6296050216564 +0.2,-0.5,0.01,-3.913012289830017,-3.913012289830017,0.009999999999999998,0.010097596309015792,-6.196857551156022,5.556114742839247 +0.2,-0.5,0.1,-1.7012881977207055,-1.7012881977207055,0.1,0.10203593903269703,-1.306317768561941,1.8327672267084076 +0.2,-0.5,0.5,0.18472753122070548,0.18472753122070548,0.5,0.3041294347438069,-0.01727622318136535,0.3530100367413601 +0.2,-0.5,0.9,1.7951810498441583,1.7951810498441583,0.9,0.14411120401135832,-1.8486713147380383,0.034062592146202074 +0.2,-0.5,0.99,3.0064623095995837,3.0064623095995837,0.99,0.024892618225088637,-5.866751711343201,0.0020052503797115804 +0.2,-0.5,0.999999999999,4.980094729541255,4.980094729541255,0.999999999999,2.511841977610102e-10,-24.350470258998758,1.990483011993407e-15 +0.2,-0.2,1e-12,-15.816970346605547,-15.816970346605547,1.000000000000001e-12,1.1961622076971429e-12,-69.37455975401205,94.6816674557204 +0.2,-0.2,0.01,-2.4932088795696132,-2.4932088795696132,0.009999999999999998,0.02008127756479523,-2.6911891199064484,3.9731188257489958 +0.2,-0.2,0.1,-1.196968927447007,-1.196968927447007,0.10000000000000002,0.14888030738918154,-0.665196382333301,1.5360386588919925 +0.2,-0.2,0.5,0.28778347417808303,0.28778347417808303,0.5,0.34338786215181655,-0.042227816439345554,0.3341702059075182 +0.2,-0.2,0.9,1.8053569582995161,1.8053569582995161,0.9,0.14685892625684116,-1.8713824969911872,0.033777133665819956 +0.2,-0.2,0.99,3.0070636429775672,3.0070636429775672,0.99,0.024937673601361298,-5.869516876596768,0.002003638988921898 +0.2,-0.2,0.999999999999,4.980094729541256,4.980094729541256,0.999999999999,2.511841977610407e-10,-24.350470258998776,1.9904830119932478e-15 +0.2,-1e-08,1e-12,-4.710674642863497,-4.710674642863497,1.0000000000000024e-12,1.4227135762842556e-11,-8.675765838635808,26.8315868463275 +0.2,-1e-08,0.01,-1.7860826257451987,-1.7860826257451987,0.010000000000000002,0.03393099033659663,-1.4331541526163853,3.1251065627594365 +0.2,-1e-08,0.1,-0.9076280388137076,-0.9076280388137076,0.1,0.1948823666077435,-0.38901333277272543,1.360281630932883 +0.2,-1e-08,0.5,0.3534020461211973,0.3534020461211973,0.5,0.37293261921825377,-0.06397205986496232,0.32207762746474894 +0.2,-1e-08,0.9,1.8120934511872642,1.8120934511872642,0.9,0.148725288194858,-1.8865065176421592,0.03358794779108519 +0.2,-1e-08,0.99,3.0074642633818716,3.0074642633818716,0.99,0.02496776422026435,-5.8713595888809,0.002002565335345154 +0.2,-1e-08,0.999999999999,4.980094729541256,4.980094729541256,0.999999999999,2.5118419776109097e-10,-24.350470258998786,1.9904830119931417e-15 +0.2,-1e-12,1e-12,-4.71067437457447,-4.71067437457447,9.999999999999961e-13,1.4227138121268493e-11,-8.675764948200849,26.831584869500908 +0.2,-1e-12,0.01,-1.7860825944972585,-1.7860825944972585,0.01,0.03393099127405078,-1.4331541048951673,3.1251065243855476 +0.2,-1e-12,0.1,-0.9076280252122516,-0.9076280252122516,0.1,0.19488236929987365,-0.38901332142866973,1.3602816225812804 +0.2,-1e-12,0.5,0.3534020493416515,0.3534020493416515,0.5,0.37293262076908185,-0.06397206104530039,0.3220776268694826 +0.2,-1e-12,0.9,1.81209345152311,1.81209345152311,0.9,0.14872528828886727,-1.8865065183979357,0.03358794778164922 +0.2,-1e-12,0.99,3.007464263401895,3.007464263401895,0.99,0.02496776422176981,-5.871359588973011,0.002002565335291489 +0.2,-1e-12,0.999999999999,4.980094729541256,4.980094729541256,0.999999999999,2.5118419776109097e-10,-24.350470258998786,1.9904830119931417e-15 +0.2,-1e-16,1e-12,-4.710674374547641,-4.710674374547641,1.0000000000000004e-12,1.4227138121504395e-11,-8.675764948111805,26.831584869303224 +0.2,-1e-16,0.01,-1.7860825944941336,-1.7860825944941336,0.01,0.033930991274144534,-1.433154104890395,3.12510652438171 +0.2,-1e-16,0.1,-0.9076280252108915,-0.9076280252108915,0.1,0.19488236930014288,-0.3890133214275353,1.360281622580445 +0.2,-1e-16,0.5,0.35340204934197356,0.35340204934197356,0.5,0.37293262076923694,-0.06397206104541843,0.3220776268694231 +0.2,-1e-16,0.9,1.8120934515231435,1.8120934515231435,0.9,0.14872528828887668,-1.8865065183980112,0.033587947781648274 +0.2,-1e-16,0.99,3.007464263401897,3.007464263401897,0.99,0.024967764221769974,-5.871359588973021,0.0020025653352914834 +0.2,-1e-16,0.999999999999,4.980094729541256,4.980094729541256,0.999999999999,2.5118419776109097e-10,-24.350470258998786,1.9904830119931417e-15 +0.2,0.0,1e-12,-4.710674374547638,-4.710674374547638,1.0000000000000002e-12,1.4227138121504416e-11,-8.675764948111796,26.831584869303203 +0.2,0.0,0.01,-1.7860825944941334,-1.7860825944941334,0.009999999999999998,0.033930991274144534,-1.4331541048903946,3.1251065243817098 +0.2,0.0,0.1,-0.9076280252108914,-0.9076280252108914,0.09999999999999999,0.19488236930014288,-0.3890133214275352,1.360281622580445 +0.2,0.0,0.5,0.35340204934197356,0.35340204934197356,0.5,0.37293262076923694,-0.06397206104541844,0.3220776268694231 +0.2,0.0,0.9,1.8120934515231435,1.8120934515231435,0.9,0.14872528828887668,-1.8865065183980112,0.033587947781648274 +0.2,0.0,0.99,3.007464263401897,3.007464263401897,0.99,0.024967764221769974,-5.871359588973021,0.0020025653352914834 +0.2,0.0,0.999999999999,4.980094729541256,4.980094729541256,0.999999999999,2.5118419776109097e-10,-24.350470258998786,1.990483011993141e-15 +0.2,1e-16,1e-12,-4.710674374547636,-4.710674374547636,9.999999999999998e-13,1.4227138121504435e-11,-8.675764948111787,26.831584869303185 +0.2,1e-16,0.01,-1.786082594494133,-1.786082594494133,0.010000000000000002,0.033930991274144555,-1.4331541048903942,3.1251065243817093 +0.2,1e-16,0.1,-0.9076280252108911,-0.9076280252108911,0.10000000000000002,0.19488236930014294,-0.3890133214275351,1.360281622580445 +0.2,1e-16,0.5,0.3534020493419736,0.3534020493419736,0.5,0.37293262076923694,-0.06397206104541844,0.3220776268694231 +0.2,1e-16,0.9,1.8120934515231435,1.8120934515231435,0.9,0.14872528828887668,-1.8865065183980112,0.033587947781648274 +0.2,1e-16,0.99,3.007464263401897,3.007464263401897,0.99,0.024967764221769974,-5.871359588973021,0.0020025653352914834 +0.2,1e-16,0.999999999999,4.980094729541256,4.980094729541256,0.999999999999,2.5118419776109097e-10,-24.350470258998786,1.9904830119931417e-15 +0.2,1e-12,1e-12,-4.710674374520806,-4.710674374520806,1.0000000000000042e-12,1.4227138121740339e-11,-8.675764948022744,26.8315848691055 +0.2,1e-12,0.01,-1.7860825944910081,-1.7860825944910081,0.010000000000000004,0.033930991274238306,-1.433154104885622,3.125106524377872 +0.2,1e-12,0.1,-0.907628025209531,-0.907628025209531,0.10000000000000002,0.19488236930041217,-0.3890133214264007,1.3602816225796097 +0.2,1e-12,0.5,0.35340204934229563,0.35340204934229563,0.5,0.37293262076939204,-0.06397206104553649,0.32207762686936353 +0.2,1e-12,0.9,1.812093451523177,1.812093451523177,0.9,0.1487252882888861,-1.886506518398087,0.03358794778164733 +0.2,1e-12,0.99,3.007464263401899,3.007464263401899,0.99,0.024967764221770113,-5.8713595889730295,0.002002565335291478 +0.2,1e-12,0.999999999999,4.980094729541256,4.980094729541256,0.999999999999,2.5118419776109097e-10,-24.350470258998786,1.9904830119931417e-15 +0.2,1e-08,1e-12,-4.710674106231799,-4.710674106231799,1.0000000000000012e-12,1.4227140480166785e-11,-8.675764057587887,26.83158289227903 +0.2,1e-08,0.01,-1.7860825632430684,-1.7860825632430684,0.009999999999999998,0.03393099221169248,-1.433154057164405,3.1251064860039834 +0.2,1e-08,0.1,-0.9076280116080752,-0.9076280116080752,0.1,0.19488237199254235,-0.38901331008234524,1.3602816142280072 +0.2,1e-08,0.5,0.35340205256274987,0.35340205256274987,0.5,0.3729326223202201,-0.06397206222587455,0.32207762627409725 +0.2,1e-08,0.9,1.812093451859023,1.812093451859023,0.9,0.1487252883828953,-1.8865065191538635,0.03358794777221136 +0.2,1e-08,0.99,3.0074642634219226,3.0074642634219226,0.99,0.024967764223275575,-5.8713595890651415,0.0020025653352378128 +0.2,1e-08,0.999999999999,4.980094729541256,4.980094729541256,0.999999999999,2.5118419776109097e-10,-24.350470258998786,1.9904830119931417e-15 +0.2,0.2,1e-12,-1.8931467367413375,-1.8931467367413375,1.0000000000000628e-12,9.073818268564984e-10,-1.600861129023486,6.740889879164335 +0.2,0.2,0.01,-1.2325796753308416,-1.2325796753308416,0.009999999999999998,0.06064448872967376,-0.7039213130313045,2.435721722981785 +0.2,0.2,0.1,-0.6517028329834367,-0.6517028329834367,0.1,0.2587243215653814,-0.2036899884385894,1.2018214320727636 +0.2,0.2,0.5,0.4166392828813806,0.4166392828813806,0.5,0.40553854522527055,-0.08931069755901332,0.3103564076582663 +0.2,0.2,0.9,1.8187921970688226,1.8187921970688226,0.9,0.1506198276521092,-1.9016163713542624,0.03339965674665406 +0.2,0.2,0.99,3.0078646691440483,3.0078646691440483,0.99,0.0249978978838975,-5.8732017163083405,0.0020014921671273602 +0.2,0.2,0.999999999999,4.980094729541257,4.980094729541257,0.999999999999,2.5118419776109634e-10,-24.350470258998797,1.9904830119930356e-15 +0.2,0.5,1e-12,-0.7434906262863605,-0.7434906262863605,1.0000000000454194e-12,1.7410997337507475e-06,-0.26362545925756453,2.2973645107806497 +0.2,0.5,0.01,-0.6237305657104739,-0.6237305657104739,0.010000000000000002,0.160036116503796,-0.18690100497469686,1.6739758632788604 +0.2,0.5,0.1,-0.32302242447414997,-0.32302242447414997,0.09999999999999999,0.40621238983076163,-0.05108317685013094,0.9955236610016336 +0.2,0.5,0.5,0.5071919233900267,0.5071919233900267,0.5,0.46097402260463116,-0.13320587222760458,0.2934654395965212 +0.2,0.5,0.9,1.8287699297565727,1.8287699297565727,0.9,0.1535155033604257,-1.9242531951361046,0.03311889618099973 +0.2,0.5,0.99,3.0084648754405694,3.0084648754405694,0.99,0.02504317922950661,-5.875963810098297,0.0019998833250354934 +0.2,0.5,0.999999999999,4.980094729541257,4.980094729541257,0.999999999999,2.511841977611717e-10,-24.350470258998815,1.9904830119928762e-15 +0.2,1.0,1e-12,1.0000000000004e-12,1.0000000000004e-12,1e-12,0.9999999999992,-5.000000000004333e-25,0.9999999999721689 +0.2,1.0,0.01,0.010040241692870999,0.010040241692870999,0.01,0.9919919678228657,-5.043699791146344e-05,0.9515684892205443 +0.2,1.0,0.1,0.10425818819511606,0.10425818819511606,0.1,0.9191661188401216,-0.005473059156236879,0.7286403153607638 +0.2,1.0,0.5,0.6472471835193793,0.6472471835193793,0.5,0.5743491774985175,-0.21913757517899324,0.2671308948125435 +0.2,1.0,0.9,1.845213277599034,1.845213277599034,0.9,0.15848931924611132,-1.9619015094191596,0.032655423927460105 +0.2,1.0,0.99,3.009464147232513,3.009464147232513,0.99,0.025118864315095826,-5.880564372857358,0.001997204348264419 +0.2,1.0,0.999999999999,4.980094729541258,4.980094729541258,0.999999999999,2.511841977612525e-10,-24.350470258998843,1.9904830119926108e-15 +0.2,2.0,0.01,0.6473342420581201,0.6473342420581201,0.00999999999999737,57.43032291049709,-0.2191979239127952,0.4348656403690846 +0.2,2.0,0.1,0.6559877215807374,0.6559877215807374,0.09999999999999995,5.697497708041991,-0.22523996244974326,0.41419424215289335 +0.2,2.0,0.5,0.8906204262069356,0.8906204262069356,0.5,0.9125465128398086,-0.42250243332958065,0.22104429968428696 +0.2,2.0,0.9,1.877411719618848,1.877411719618848,0.9,0.16901932960350308,-2.036865532213983,0.0317451604165204 +0.2,2.0,0.99,3.011458676142162,3.011458676142162,0.99,0.02527104900944865,-5.889754515921726,0.0019918554924447983 +0.2,2.0,0.999999999999,4.9800947295412605,4.9800947295412605,0.999999999999,2.5118419776136916e-10,-24.350470258998897,1.9904830119920803e-15 +0.5,-2.0,1e-12,-1414213562371.095,-1414213562371.095,1e-12,7.071067811865476e-25,-74343644340516.31,19184529011588.49 +0.5,-2.0,0.01,-139.4142849927121,-139.4142849927121,0.01,7.070714249635605e-05,-925.6100731878809,290.2974184114184 +0.5,-2.0,0.1,-12.071247279470288,-12.071247279470288,0.1,0.007035623639735145,-30.763127752379056,12.845947890001774 +0.5,-2.0,0.5,-0.4494897427831781,-0.4494897427831781,0.5,0.15309310892394862,-0.09420313780106487,0.5195321703179825 +0.5,-2.0,0.9,1.315065110781225,1.315065110781225,0.9,0.2496587671202435,-1.1622069753070197,0.018674417485112213 +0.5,-2.0,0.99,1.798485481381212,1.798485481381212,0.99,0.09776466795064574,-2.6720027868057623,0.000508018372325331 +0.5,-2.0,0.999999999999,1.9999980000221218,1.9999980000221218,0.999999999999,9.99988939095806e-07,-3.9999407385690176,4.999834088024578e-19 +0.5,-1.0,1e-12,-1999997.999999,-1999997.999999,1e-12,9.999999999995001e-19,-51262046.231829464,26631021.115942866 +0.5,-1.0,0.01,-17.8997487421324,-17.8997487421324,0.01,0.00099498743710662,-55.64223297339863,36.333827455157106 +0.5,-1.0,0.1,-4.0,-4.0,0.1,0.03,-5.183347464017316,4.6752836433134854 +0.5,-1.0,0.5,0.0,0.0,0.5,0.25,0.0,0.38629436111989063 +0.5,-1.0,0.9,1.3333333333333335,1.3333333333333335,0.9,0.26999999999999996,-1.2018502817758538,0.017868385526087664 +0.5,-1.0,0.99,1.7989924369481576,1.7989924369481576,0.99,0.09850375627355537,-2.6743310308896135,0.0005058943623229628 +0.5,-1.0,0.999999999999,1.9999980000221218,1.9999980000221218,0.999999999999,9.99988939096806e-07,-3.9999407385690176,4.999834088022495e-19 +0.5,-0.5,1e-12,-2826.425710532274,-2826.425710532274,1.0000000000000002e-12,1.4142128552661374e-15,-35383.80632346327,36247.75863192831 +0.5,-0.5,0.01,-6.48528137423857,-6.48528137423857,0.01,0.004242640687119285,-11.55505489342438,13.223699072630055 +0.5,-0.5,0.1,-2.159113040222282,-2.159113040222282,0.1,0.06576135126604957,-1.772001903089067,2.843739086944378 +0.5,-0.5,0.5,0.17964055775109064,0.17964055775109064,0.5,0.3217971264527913,-0.016641399222871462,0.3336286564037747 +0.5,-0.5,0.9,1.3421699101472493,1.3421699101472493,0.9,0.280832588639268,-1.2213838741265024,0.017479457030688192 +0.5,-0.5,0.99,1.7992451194274555,1.7992451194274555,0.99,0.09887554913322322,-2.675492454941156,0.0005048359537363327 +0.5,-0.5,0.999999999999,1.9999980000221218,1.9999980000221218,0.999999999999,9.99988939097306e-07,-3.9999407385690176,4.999834088021453e-19 +0.5,-0.2,1e-12,-68.73735125815188,-68.73735125815188,1.0000000000000008e-12,1.4080523380915864e-13,-366.99954916795514,804.3353880281301 +0.5,-0.2,0.01,-3.4988843077656764,-3.4988843077656764,0.010000000000000002,0.0109457263648281,-4.125352984237462,7.28917380888974 +0.5,-0.2,0.1,-1.4202140063484725,-1.4202140063484725,0.1,0.10790045734997976,-0.829844293685214,2.11941555498466 +0.5,-0.2,0.5,0.27548061769642607,0.27548061769642607,0.5,0.3753203299198651,-0.039817871221636596,0.30573881416419774 +0.5,-0.2,0.9,1.3473793199700907,1.3473793199700907,0.9,0.28755561154238674,-1.2330105039598134,0.01725048436922932 +0.5,-0.2,0.99,1.7993964751074742,1.7993964751074742,0.99,0.09909934814223832,-2.676188447343148,0.0005042020563947503 +0.5,-0.2,0.999999999999,1.9999980000221218,1.9999980000221218,0.999999999999,9.99988939097606e-07,-3.9999407385690176,4.999834088020828e-19 +0.5,-1e-08,1e-12,-8.513044265729226,-8.513044265729226,9.999999999999992e-13,5.256520680434069e-12,-17.866066542344626,72.62154036910931 +0.5,-1e-08,0.01,-2.2919321019913887,-2.2919321019913887,0.01,0.02145965952170308,-1.9706871563547392,4.94126947701963 +0.5,-1e-08,0.1,-1.0348542762403181,-1.0348542762403181,0.10000000000000002,0.15174271031801087,-0.46145841544334243,1.7470025606744175 +0.5,-1e-08,0.5,0.33489077479919005,0.33489077479919005,0.5,0.4162773034147881,-0.05949751015338791,0.28854144152641026 +0.5,-1e-08,0.9,1.3508143078800012,1.3508143078800012,0.9,0.29213356114620603,-1.2407223451964333,0.017099634822864192 +0.5,-1e-08,0.99,1.799497273295284,1.799497273295284,0.99,0.09924884970885954,-2.6766520833833494,0.0005037799357397408 +0.5,-1e-08,0.999999999999,1.9999980000221218,1.9999980000221218,0.999999999999,9.99988939097806e-07,-3.9999407385690176,4.999834088020412e-19 +0.5,-1e-12,1e-12,-8.513043539586485,-8.513043539586485,1.0000000000000004e-12,5.256521769648002e-12,-17.866064132321046,72.62153200908203 +0.5,-1e-12,0.01,-2.2919320525836357,-2.2919320525836357,0.01,0.021459660262819355,-1.9706870809002248,4.941269382215001 +0.5,-1e-12,0.1,-1.0348542587720397,-1.0348542587720397,0.09999999999999999,0.1517427129382526,-0.46145840087423123,1.7470025439151686 +0.5,-1e-12,0.5,0.33489077768431597,0.33489077768431597,0.5,0.41627730557863246,-0.05949751121082381,0.28854144069315324 +0.5,-1e-12,0.9,1.3508143080509805,1.3508143080509805,0.9,0.292133561377028,-1.2407223455811993,0.017099634815358165 +0.5,-1e-12,0.99,1.7994972733003214,1.7994972733003214,0.99,0.09924884971633992,-2.6766520834065215,0.0005037799357186463 +0.5,-1e-12,0.999999999999,1.9999980000221218,1.9999980000221218,0.999999999999,9.99988939097806e-07,-3.9999407385690176,4.999834088020412e-19 +0.5,-1e-16,1e-12,-8.513043539513871,-8.513043539513871,9.999999999999986e-13,5.256521769756913e-12,-17.866064132080044,72.62153200824604 +0.5,-1e-16,0.01,-2.291932052578695,-2.291932052578695,0.010000000000000004,0.021459660262893473,-1.9706870808926793,4.94126938220552 +0.5,-1e-16,0.1,-1.034854258770293,-1.034854258770293,0.09999999999999999,0.1517427129385146,-0.46145840087277434,1.7470025439134926 +0.5,-1e-16,0.5,0.33489077768460446,0.33489077768460446,0.5,0.41627730557884884,-0.05949751121092956,0.2885414406930699 +0.5,-1e-16,0.9,1.3508143080509976,1.3508143080509976,0.9,0.2921335613770511,-1.240722345581238,0.017099634815357416 +0.5,-1e-16,0.99,1.7994972733003218,1.7994972733003218,0.99,0.0992488497163407,-2.676652083406524,0.0005037799357186443 +0.5,-1e-16,0.999999999999,1.9999980000221218,1.9999980000221218,0.999999999999,9.99988939097806e-07,-3.9999407385690176,4.999834088020412e-19 +0.5,0.0,1e-12,-8.513043539513864,-8.513043539513864,9.999999999999978e-13,5.256521769756921e-12,-17.86606413208002,72.62153200824595 +0.5,0.0,0.01,-2.2919320525786944,-2.2919320525786944,0.010000000000000002,0.021459660262893476,-1.9706870808926786,4.941269382205519 +0.5,0.0,0.1,-1.0348542587702927,-1.0348542587702927,0.1,0.15174271293851463,-0.4614584008727742,1.7470025439134924 +0.5,0.0,0.5,0.3348907776846045,0.3348907776846045,0.5,0.4162773055788489,-0.05949751121092957,0.2885414406930699 +0.5,0.0,0.9,1.3508143080509976,1.3508143080509976,0.9,0.2921335613770511,-1.240722345581238,0.017099634815357416 +0.5,0.0,0.99,1.7994972733003218,1.7994972733003218,0.99,0.0992488497163407,-2.676652083406524,0.0005037799357186443 +0.5,0.0,0.999999999999,1.9999980000221218,1.9999980000221218,0.999999999999,9.99988939097806e-07,-3.9999407385690176,4.999834088020409e-19 +0.5,1e-16,1e-12,-8.513043539513857,-8.513043539513857,9.99999999999997e-13,5.256521769756927e-12,-17.866064132079998,72.62153200824586 +0.5,1e-16,0.01,-2.291932052578694,-2.291932052578694,0.010000000000000002,0.021459660262893483,-1.9706870808926777,4.941269382205518 +0.5,1e-16,0.1,-1.0348542587702925,-1.0348542587702925,0.1,0.15174271293851468,-0.46145840087277407,1.7470025439134922 +0.5,1e-16,0.5,0.3348907776846045,0.3348907776846045,0.5,0.4162773055788489,-0.05949751121092958,0.28854144069306986 +0.5,1e-16,0.9,1.3508143080509976,1.3508143080509976,0.9,0.2921335613770511,-1.240722345581238,0.017099634815357416 +0.5,1e-16,0.99,1.7994972733003218,1.7994972733003218,0.99,0.0992488497163407,-2.676652083406524,0.0005037799357186443 +0.5,1e-16,0.999999999999,1.9999980000221218,1.9999980000221218,0.999999999999,9.99988939097806e-07,-3.9999407385690176,4.999834088020412e-19 +0.5,1e-12,1e-12,-8.513043539441242,-8.513043539441242,1.0000000000000044e-12,5.256521769865887e-12,-17.866064131838993,72.62153200740985 +0.5,1e-12,0.01,-2.291932052573753,-2.291932052573753,0.010000000000000005,0.0214596602629676,-1.9706870808851324,4.941269382196038 +0.5,1e-12,0.1,-1.0348542587685456,-1.0348542587685456,0.1,0.1517427129387767,-0.4614584008713171,1.7470025439118164 +0.5,1e-12,0.5,0.334890777684893,0.334890777684893,0.5,0.4162773055790653,-0.059497511211035324,0.28854144069298654 +0.5,1e-12,0.9,1.3508143080510147,1.3508143080510147,0.9,0.2921335613770742,-1.2407223455812764,0.017099634815356663 +0.5,1e-12,0.99,1.7994972733003225,1.7994972733003225,0.99,0.09924884971634136,-2.6766520834065264,0.0005037799357186422 +0.5,1e-12,0.999999999999,1.9999980000221218,1.9999980000221218,0.999999999999,9.99988939097806e-07,-3.9999407385690176,4.999834088020412e-19 +0.5,1e-08,1e-12,-8.513042813298586,-8.513042813298586,9.99999999999997e-13,5.256522859080018e-12,-17.866061721815793,72.62152364738361 +0.5,1e-08,0.01,-2.2919320031660013,-2.2919320031660013,0.009999999999999997,0.021459661004083887,-1.9706870054306207,4.94126928739141 +0.5,1e-08,0.1,-1.0348542413002673,-1.0348542413002673,0.10000000000000002,0.1517427155590185,-0.4614583863022063,1.7470025271525675 +0.5,1e-08,0.5,0.33489078057001886,0.33489078057001886,0.5,0.41627730774290966,-0.059497512268471234,0.2885414398597295 +0.5,1e-08,0.9,1.3508143082219939,1.3508143082219939,0.9,0.29213356160789616,-1.2407223459660424,0.01709963480785064 +0.5,1e-08,0.99,1.7994972733053598,1.7994972733053598,0.99,0.09924884972382174,-2.6766520834296985,0.0005037799356975478 +0.5,1e-08,0.999999999999,1.9999980000221218,1.9999980000221218,0.999999999999,9.99988939097806e-07,-3.9999407385690176,4.999834088020412e-19 +0.5,0.2,1e-12,-2.463225130540616,-2.463225130540616,9.999999999999943e-13,5.60555732308875e-10,-2.2390295936298856,10.911601863387885 +0.5,0.2,0.01,-1.4695614404316368,-1.4695614404316368,0.01,0.04357572152754532,-0.8835082052712017,3.38980270205319 +0.5,0.2,0.1,-0.7167725540420448,-0.7167725540420448,0.09999999999999999,0.21528971631832147,-0.23073581720335368,1.444288375558138 +0.5,0.2,0.5,0.39096652176608915,0.39096652176608915,0.5,0.46207352739561275,-0.0819600175824418,0.2723841090236613 +0.5,0.2,0.9,1.354219268806769,1.354219268806769,0.9,0.296789884156309,-1.2484026482458461,0.016950208923465767 +0.5,0.2,0.99,1.799597987107205,1.799597987107205,0.99,0.09939859356259352,-2.6771154325450466,0.0005033581966908162 +0.5,0.2,0.999999999999,1.9999980000221218,1.9999980000221218,0.999999999999,9.99988939098006e-07,-3.9999407385690176,4.999834088019995e-19 +0.5,0.5,1e-12,-0.8284257105322742,-0.8284257105322742,9.99999999949682e-13,1.414212855230557e-06,-0.30366105718703273,2.8283866343479316 +0.5,0.5,0.01,-0.6832815729997477,-0.6832815729997477,0.009999999999999995,0.13416407864998733,-0.21063398078733817,1.9967833340780996 +0.5,0.5,0.1,-0.33884113865505977,-0.33884113865505977,0.1,0.36980325417258353,-0.05441422728734996,1.0935383637262854 +0.5,0.5,0.5,0.46926627053964093,0.46926627053964093,0.5,0.541196100146197,-0.1198961475547314,0.2499647431858816 +0.5,0.5,0.9,1.3592710279721312,1.3592710279721312,0.9,0.303924437169957,-1.2598637160765866,0.016728710811512313 +0.5,0.5,0.99,1.7997488997607245,1.7997488997607245,0.99,0.0996236645024288,-2.677809918591574,0.0005027263030034842 +0.5,0.5,0.999999999999,1.9999980000221218,1.9999980000221218,0.999999999999,9.99988939098306e-07,-3.9999407385690176,4.99983408801937e-19 +0.5,1.0,1e-12,1.00000000000025e-12,1.00000000000025e-12,1e-12,0.9999999999995,-5.000000000003333e-25,0.999999999971869 +0.5,1.0,0.01,0.01002512578676009,0.01002512578676009,0.01,0.99498743710662,-5.033574764783548e-05,0.9487037352803966 +0.5,1.0,0.1,0.10263340389897241,0.10263340389897241,0.1,0.9486832980505138,-0.0053592848408058925,0.7059695180434538 +0.5,1.0,0.5,0.585786437626905,0.585786437626905,0.5,0.7071067811865475,-0.1913147317852627,0.21697770945227393 +0.5,1.0,0.9,1.3675444679663242,1.3675444679663242,0.9,0.3162277660168379,-1.2788062558902882,0.016366481581129563 +0.5,1.0,0.99,1.7999999999999998,1.7999999999999998,0.99,0.10000000000000009,-2.678965962802381,0.0005016750503357329 +0.5,1.0,0.999999999999,1.9999980000221218,1.9999980000221218,0.999999999999,9.99988939098806e-07,-3.9999407385690176,4.999834088018328e-19 +0.5,2.0,1e-12,0.585786437626905,0.585786437626905,4.504783315004069e-09,156967989.74356636,-0.1913147317852627,0.3535533905932738 +0.5,2.0,0.01,0.585857150072879,0.585857150072879,0.010000000000001487,70.70714249634554,-0.19136374945358972,0.35321006149212064 +0.5,2.0,0.1,0.5928752720529711,0.5928752720529711,0.09999999999999963,7.035623639735171,-0.19626393012964463,0.33541742227688787 +0.5,2.0,0.5,0.775255128608411,0.775255128608411,0.5,1.2247448713915892,-0.3492446598798669,0.16469814209617514 +0.5,2.0,0.9,1.3835585997031024,1.3835585997031024,0.9,0.3424674446093876,-1.3160891101600904,0.015667316167455824 +0.5,2.0,0.99,1.8005006265674,1.8005006265674,0.99,0.10075725930939396,-2.6812726875265835,0.0004995796641782902 +0.5,2.0,0.999999999999,1.9999980000221218,1.9999980000221218,0.999999999999,9.999889390998059e-07,-3.9999407385690176,4.999834088016245e-19 +1.0,-2.0,1e-12,-5e+23,-5e+23,1e-12,1.0000000000000001e-36,-2.6784447525648577e+25,1.3565510557964274e+25 +1.0,-2.0,0.01,-4998.5,-4998.5,0.01,1e-06,-37582.70738548631,20526.100929940454 +1.0,-2.0,0.1,-48.49999999999999,-48.49999999999999,0.1,0.0010000000000000002,-144.64764714394488,90.37925464970228 +1.0,-2.0,0.5,-0.5,-0.5,0.5,0.125,-0.10819766216224658,0.6362943611198906 +1.0,-2.0,0.9,0.8827160493827161,0.8827160493827161,0.9,0.7290000000000001,-0.6313580878688091,0.0063953800356952445 +1.0,-2.0,0.99,0.9898479746964596,0.9898479746964596,0.99,0.9702990000000001,-0.9432493455175582,5.1186538874319645e-05 +1.0,-2.0,0.999999999999,0.999999999999,0.999999999999,0.999999999999,0.9999999999970001,-0.9999999999713696,4.999778785257303e-25 +1.0,-1.0,1e-12,-999999999998.0,-999999999998.0,1e-12,1e-24,-26631021115901.918,26631021115929.547 +1.0,-1.0,0.01,-98.0,-98.0,0.01,0.0001,-356.9168651633244,361.5170185988091 +1.0,-1.0,0.1,-7.999999999999999,-7.999999999999999,0.1,0.010000000000000002,-11.775021196025973,14.025850929940455 +1.0,-1.0,0.5,0.0,0.0,0.5,0.25,0.0,0.38629436111989063 +1.0,-1.0,0.9,0.888888888888889,0.888888888888889,0.9,0.81,-0.6447528247404202,0.005956128508695887 +1.0,-1.0,0.99,0.98989898989899,0.98989898989899,0.99,0.9801000000000001,-0.9434836378774284,5.0844296466102295e-05 +1.0,-1.0,0.999999999999,0.999999999999,0.999999999999,0.999999999999,0.999999999998,-0.9999999999713696,4.999778785253971e-25 +1.0,-0.5,1e-12,-1999997.0,-1999997.0,1e-12,1e-18,-27017287.459733963,51262046.2318571 +1.0,-0.5,0.01,-17.0,-17.0,0.01,0.001,-35.02669164213096,56.10340371976183 +1.0,-0.5,0.1,-3.3245553203367586,-3.3245553203367586,0.1,0.03162277660168379,-3.0079313357235864,5.913716159750084 +1.0,-0.5,0.5,0.1715728752538099,0.1715728752538099,0.5,0.3535533905932738,-0.015641014549177663,0.3036620374447142 +1.0,-0.5,0.9,0.8918148932210804,0.8918148932210804,0.9,0.8538149682454624,-0.6512207828415032,0.005749256394537454 +1.0,-0.5,0.99,0.9899243694815758,0.9899243694815758,0.99,0.9850375627355537,-0.9436002920119936,5.067414080053206e-05 +1.0,-0.5,0.999999999999,0.999999999999,0.999999999999,0.999999999999,0.9999999999985001,-0.9999999999713696,4.999778785252304e-25 +1.0,-0.2,1e-12,-1249.9432157547903,-1249.9432157547903,1.0000000000000002e-12,3.981071705534972e-15,-7671.349869765895,28448.277436153865 +1.0,-0.2,0.01,-6.559432157547901,-6.559432157547901,0.009999999999999998,0.003981071705534972,-8.731757547036644,20.04116173714018 +1.0,-0.2,0.1,-1.9244659623055673,-1.9244659623055673,0.1,0.06309573444801933,-1.2138132250447258,3.6244273832156773 +1.0,-0.2,0.5,0.25650822501482495,0.25650822501482495,0.5,0.43527528164806206,-0.036139065352546115,0.2636262554743347 +1.0,-0.2,0.9,0.8935215619993246,0.8935215619993246,0.9,0.881233526124879,-0.6550297963653346,0.00562901141994588 +1.0,-0.2,0.99,0.9899395564501734,0.9899395564501734,0.99,0.9880120321448115,-0.9436701276107236,5.05723548854235e-05 +1.0,-0.2,0.999999999999,0.999999999999,0.999999999999,0.999999999999,0.9999999999988001,-0.9999999999713696,4.999778785251304e-25 +1.0,-1e-08,1e-12,-26.63102493329554,-26.63102493329554,1.0000000000000006e-12,9.999997236898277e-13,-65.07466777109956,381.7367342729441 +1.0,-1e-08,0.01,-3.605170292026055,-3.605170292026055,0.01,0.009999999539482994,-3.4277520573699234,10.603796546505377 +1.0,-1e-08,0.1,-1.3025851195035363,-1.3025851195035363,0.10000000000000002,0.09999999769741495,-0.6178456046170454,2.6509490959327713 +1.0,-1e-08,0.5,0.3068528170377896,0.3068528170377896,0.5,0.4999999965342641,-0.052805421019621726,0.2402265080691829 +1.0,-1e-08,0.9,0.8946394842866695,0.8946394842866695,0.9,0.8999999990517553,-0.6575396221921053,0.005550419133740161 +1.0,-1e-08,0.99,0.9899496641459935,0.9899496641459935,0.99,0.9899999999005017,-0.9437166194388457,5.050462538747229e-05 +1.0,-1e-08,0.999999999999,0.999999999999,0.999999999999,0.999999999999,0.999999999999,-0.9999999999713696,4.999778785250637e-25 +1.0,-1e-12,1e-12,-26.631021116310286,-26.631021116310286,9.999999999999986e-13,9.999999999723676e-13,-65.0746551027577,381.7366639614765 +1.0,-1e-12,0.01,-3.6051701859986953,-3.6051701859986953,0.009999999999999998,0.009999999999953947,-3.427751895447098,10.60379622098935 +1.0,-1e-12,0.1,-1.3025850929966967,-1.3025850929966967,0.09999999999999999,0.09999999999976973,-0.6178455825094809,2.6509490552432684 +1.0,-1e-12,0.5,0.30685281943981446,0.30685281943981446,0.5,0.49999999999965344,-0.05280542189999487,0.24022650695921172 +1.0,-1e-12,0.9,0.8946394843421682,0.8946394843421682,0.9,0.8999999999999052,-0.6575396223169977,0.005550419129841918 +1.0,-1e-12,0.99,0.9899496641464985,0.9899496641464985,0.99,0.9899999999999901,-0.9437166194411687,5.05046253840887e-05 +1.0,-1e-12,0.999999999999,0.999999999999,0.999999999999,0.999999999999,0.999999999999,-0.9999999999713696,4.999778785250637e-25 +1.0,-1e-16,1e-12,-26.631021115928586,-26.631021115928586,1e-12,9.999999999999974e-13,-65.07465510149086,381.73666395444536 +1.0,-1e-16,0.01,-3.6051701859880922,-3.6051701859880922,0.010000000000000002,0.009999999999999997,-3.427751895430906,10.603796220956799 +1.0,-1e-16,0.1,-1.302585092994046,-1.302585092994046,0.1,0.09999999999999998,-0.6178455825072701,2.650949055239199 +1.0,-1e-16,0.5,0.30685281944005466,0.30685281944005466,0.5,0.49999999999999994,-0.0528054219000829,0.24022650695910072 +1.0,-1e-16,0.9,0.8946394843421738,0.8946394843421738,0.9,0.9,-0.6575396223170101,0.005550419129841528 +1.0,-1e-16,0.99,0.9899496641464985,0.9899496641464985,0.99,0.99,-0.9437166194411689,5.050462538408836e-05 +1.0,-1e-16,0.999999999999,0.999999999999,0.999999999999,0.999999999999,0.999999999999,-0.9999999999713696,4.999778785250637e-25 +1.0,0.0,1e-12,-26.631021115928547,-26.631021115928547,1.000000000000001e-12,1.000000000000001e-12,-65.07465510149075,381.7366639544447 +1.0,0.0,0.01,-3.6051701859880914,-3.6051701859880914,0.01,0.01,-3.4277518954309043,10.603796220956795 +1.0,0.0,0.1,-1.3025850929940457,-1.3025850929940457,0.1,0.1,-0.6178455825072698,2.6509490552391988 +1.0,0.0,0.5,0.3068528194400547,0.3068528194400547,0.5,0.5,-0.05280542190008291,0.24022650695910072 +1.0,0.0,0.9,0.8946394843421738,0.8946394843421738,0.9,0.9,-0.6575396223170101,0.005550419129841528 +1.0,0.0,0.99,0.9899496641464985,0.9899496641464985,0.99,0.99,-0.9437166194411689,5.050462538408836e-05 +1.0,0.0,0.999999999999,0.999999999999,0.999999999999,0.999999999999,0.999999999999,-0.9999999999713696,4.999778785250635e-25 +1.0,1e-16,1e-12,-26.63102111592851,-26.63102111592851,9.999999999999984e-13,1.000000000000001e-12,-65.07465510149062,381.73666395444394 +1.0,1e-16,0.01,-3.6051701859880905,-3.6051701859880905,0.009999999999999998,0.010000000000000004,-3.4277518954309025,10.603796220956793 +1.0,1e-16,0.1,-1.3025850929940455,-1.3025850929940455,0.09999999999999999,0.10000000000000002,-0.6178455825072696,2.6509490552391983 +1.0,1e-16,0.5,0.3068528194400547,0.3068528194400547,0.5,0.5,-0.05280542190008292,0.2402265069591007 +1.0,1e-16,0.9,0.8946394843421738,0.8946394843421738,0.9,0.9,-0.6575396223170101,0.005550419129841528 +1.0,1e-16,0.99,0.9899496641464985,0.9899496641464985,0.99,0.99,-0.9437166194411689,5.050462538408836e-05 +1.0,1e-16,0.999999999999,0.999999999999,0.999999999999,0.999999999999,0.999999999999,-0.9999999999713696,4.999778785250637e-25 +1.0,1e-12,1e-12,-26.63102111554681,-26.63102111554681,9.999999999999998e-13,1.0000000000276308e-12,-65.07465510022378,381.7366639474128 +1.0,1e-12,0.01,-3.6051701859774874,-3.6051701859774874,0.010000000000000002,0.010000000000046054,-3.4277518954147106,10.603796220924242 +1.0,1e-12,0.1,-1.3025850929913947,-1.3025850929913947,0.1,0.10000000000023027,-0.6178455825050589,2.6509490552351296 +1.0,1e-12,0.5,0.3068528194402949,0.3068528194402949,0.5,0.5000000000003466,-0.05280542190017096,0.2402265069589897 +1.0,1e-12,0.9,0.8946394843421793,0.8946394843421793,0.9,0.900000000000095,-0.6575396223170227,0.005550419129841138 +1.0,1e-12,0.99,0.9899496641464987,0.9899496641464987,0.99,0.99000000000001,-0.9437166194411691,5.0504625384088024e-05 +1.0,1e-12,0.999999999999,0.999999999999,0.999999999999,0.999999999999,0.999999999999,-0.9999999999713696,4.999778785250637e-25 +1.0,1e-08,1e-12,-26.63101729856226,-26.63101729856226,1.000000000000001e-12,1.0000002763102502e-12,-65.0746424318848,381.7365936359598 +1.0,1e-08,0.01,-3.6051700799501307,-3.6051700799501307,0.01,0.01000000046051703,-3.4277517334918928,10.603795895408227 +1.0,1e-08,0.1,-1.3025850664845553,-1.3025850664845553,0.1,0.10000000230258513,-0.6178455603974949,2.6509490145456276 +1.0,1e-08,0.5,0.30685282184231977,0.30685282184231977,0.5,0.5000000034657359,-0.0528054227805441,0.24022650584901853 +1.0,1e-08,0.9,0.8946394843976779,0.8946394843976779,0.9,0.9000000009482447,-0.657539622441915,0.005550419125942894 +1.0,1e-08,0.99,0.9899496641470036,0.9899496641470036,0.99,0.9900000000994983,-0.9437166194434922,5.050462538070444e-05 +1.0,1e-08,0.999999999999,0.999999999999,0.999999999999,0.999999999999,0.999999999999,-0.9999999999713696,4.999778785250637e-25 +1.0,0.2,1e-12,-3.980094641472325,-3.980094641472325,1.0000000000000083e-12,2.5118864315095975e-10,-4.015192798509971,24.35046782556331 +1.0,0.2,0.01,-2.0094641472325137,-2.0094641472325137,0.01,0.025118864315095805,-1.306249207129357,5.88056437285736 +1.0,0.2,0.1,-0.8452132775990336,-0.8452132775990336,0.10000000000000002,0.15848931924611137,-0.28515490741012234,1.9619015094191592 +1.0,0.2,0.5,0.3527528164806207,0.3527528164806207,0.5,0.5743491774985175,-0.07118280832716567,0.21913757517899324 +1.0,0.2,0.9,0.895741811804884,0.895741811804884,0.9,0.9191661188401216,-0.6600260507538271,0.0054730591562368765 +1.0,0.2,0.99,0.989959758307129,0.989959758307129,0.99,0.9919919678228657,-0.9437630591571742,5.043699791146353e-05 +1.0,0.2,0.999999999999,0.999999999999,0.999999999999,0.999999999999,0.9999999999992,-0.9999999999713696,4.99977878524997e-25 +1.0,0.5,1e-12,-0.999998,-0.999998,9.999999999464891e-13,9.999999999732445e-07,-0.3862929748265295,3.9999407379577683 +1.0,0.5,0.01,-0.8,-0.8,0.009999999999999995,0.09999999999999998,-0.2580159968238142,2.6789659628023816 +1.0,0.5,0.1,-0.3675444679663241,-0.3675444679663241,0.1,0.31622776601683794,-0.060519887638209556,1.278806255890288 +1.0,0.5,0.5,0.41421356237309503,0.41421356237309503,0.5,0.7071067811865475,-0.10093497744014174,0.1913147317852627 +1.0,0.5,0.9,0.8973665961010276,0.8973665961010276,0.9,0.9486832980505138,-0.6637122277953441,0.005359284840805889 +1.0,0.5,0.99,0.9899748742132399,0.9899748742132399,0.99,0.99498743710662,-0.9438326211548864,5.033574764783557e-05 +1.0,0.5,0.999999999999,0.999999999999,0.999999999999,0.999999999999,0.9999999999995,-0.9999999999713696,4.99977878524897e-25 +1.0,1.0,1e-12,1e-12,1e-12,1e-12,1.0,-5.000000000001666e-25,0.999999999971369 +1.0,1.0,0.01,0.01,0.01,0.01,1.0,-5.016750503357323e-05,0.943948298140119 +1.0,1.0,0.1,0.1,0.1,0.1,1.0,-0.005175535907956329,0.6697414907005954 +1.0,1.0,0.5,0.5,0.5,0.5,1.0,-0.15342640972002736,0.15342640972002736 +1.0,1.0,0.9,0.9,0.9,0.9,1.0,-0.6697414907005955,0.005175535907956327 +1.0,1.0,0.99,0.99,0.99,0.99,1.0,-0.943948298140119,5.016750503357332e-05 +1.0,1.0,0.999999999999,0.999999999999,0.999999999999,0.999999999999,1.0,-0.9999999999713696,4.999778785247304e-25 +1.0,2.0,0.01,0.50005,0.50005,0.009999999999999449,100.00000000000551,-0.1534610695791387,0.24974474149070058 +1.0,2.0,0.1,0.505,0.505,0.10000000000000005,9.999999999999996,-0.15691722937534386,0.23598707453502976 +1.0,2.0,0.5,0.625,0.625,0.5,2.0,-0.2571890301206027,0.10085660243000684 +1.0,2.0,0.9,0.905,0.905,0.9,1.1111111111111112,-0.6813815531987484,0.004828991158580346 +1.0,2.0,0.99,0.99005,0.99005,0.99,1.0101010101010102,-0.9441786818582741,4.9832914991618836e-05 +1.0,2.0,0.999999999999,0.999999999999,0.999999999999,0.999999999999,1.000000000001,-0.9999999999713696,4.999778785243971e-25 +2.0,-2.0,1e-12,-1.25e+47,-1.25e+47,1e-12,2e-60,-6.758611881412144e+48,6.782755278982137e+48 +2.0,-2.0,0.01,-12497499.624999998,-12497499.624999998,0.01,2.0002000200020006e-10,-100193623.3493694,102620241.59923731 +2.0,-2.0,0.1,-1224.6249999999998,-1224.6249999999998,0.1,2.0202020202020206e-05,-4168.091766812636,4473.773105160262 +2.0,-2.0,0.5,-0.625,-0.625,0.5,0.08333333333333333,-0.14364824662168493,0.954441541679836 +2.0,-2.0,0.9,0.49312223746380124,0.49312223746380124,0.8999999999999999,6.215684210526309,-0.23182099135917153,0.0007500754362852445 +2.0,-2.0,0.99,0.49994846819111816,0.49994846819111816,0.9900000000000018,95.57688943720397,-0.2497376988642918,5.196470378527507e-07 +2.0,-1.0,1e-12,-4.99999999999e+23,-4.99999999999e+23,1e-12,1.000000000001e-36,-1.3565510557936644e+25,2.663102111590292e+25 +2.0,-1.0,0.01,-4900.0,-4900.0,0.01,1.01010101010101e-06,-20068.38482558456,35790.184841282105 +2.0,-1.0,0.1,-39.99999999999999,-39.99999999999999,0.1,0.0011111111111111113,-68.98759538211688,126.23265836946409 +2.0,-1.0,0.5,0.0,0.0,0.5,0.25,0.0,0.38629436111989063 +2.0,-1.0,0.9,0.4938271604938272,0.4938271604938272,0.9000000000000002,7.29000000000002,-0.23335046557199865,0.000661792056521765 +2.0,-1.0,0.99,0.49994898479746963,0.49994898479746963,0.9899999999999985,97.02989999998555,-0.24974007142892896,5.135787521828519e-07 +2.0,-0.5,1e-12,-1999996000001.5,-1999996000001.5,1e-12,5.000005000005e-25,-28017257442448.754,102523989939621.73 +2.0,-0.5,0.01,-161.5,-161.5,0.01,5.555555555555556e-05,-387.4902247791787,1009.8612669557128 +2.0,-0.5,0.1,-8.850889359326482,-8.850889359326482,0.1,0.007312376477871323,-9.267149750050406,25.57419268160869 +2.0,-0.5,0.5,0.1568542494923802,0.1568542494923802,0.5,0.42677669529663687,-0.013838033236259144,0.2515618685748945 +2.0,-0.5,0.9,0.4941479913356169,0.4941479913356169,0.8999999999999999,7.892167357104572,-0.23405964590691158,0.0006219839169424204 +2.0,-0.5,0.99,0.4999492408348281,0.4999492408348281,0.9900000000000012,97.76435935542219,-0.24974124827306887,5.105739195447637e-07 +2.0,-0.2,1e-12,-782428.964521468,-782428.964521468,1e-12,3.182455970339856e-18,-5188801.048285377,35587179.65866676 +2.0,-0.2,0.01,-28.072507272284653,-28.072507272284653,0.01,0.0005266363428581039,-43.760101953858566,151.499802510356 +2.0,-0.2,0.1,-3.776250582341914,-3.776250582341914,0.1,0.021575130386635246,-2.7007700406379977,10.599514515062486 +2.0,-0.2,0.5,0.2236099902646969,0.2236099902646969,0.5,0.5854473395576452,-0.029883666297698893,0.19600395261530834 +2.0,-0.2,0.9,0.49433117112046815,0.49433117112046815,0.8999999999999998,8.276168796909738,-0.23446847021986889,0.0005993683434838006 +2.0,-0.2,0.99,0.4999493937377904,0.4999493937377904,0.9900000000000027,98.2076016083919,-0.24974195138043417,5.087803215065984e-07 +2.0,-1e-08,1e-12,-381.2367694321999,-381.2367694321999,1.0000000000000002e-12,3.619119182527398e-14,-1076.3427561040398,10547.777222650531 +2.0,-1e-08,0.01,-10.10379670927967,-10.10379670927967,0.010000000000000002,0.0021714722595162646,-11.142004180142388,48.83228883865504 +2.0,-1e-08,0.1,-2.1509491162795573,-2.1509491162795573,0.1,0.04342944669032522,-1.1355030460589477,6.104035940856152 +2.0,-1e-08,0.5,0.25977349137577604,0.25977349137577604,0.5,0.7213475129444819,-0.04184062724344894,0.16651232734100282 +2.0,-1e-08,0.9,0.4944495808643105,0.4944495808643105,0.8999999999999999,8.542099409426907,-0.23473430855923386,0.0005847950223560003 +2.0,-1e-08,0.99,0.4999494953746108,0.4999494953746108,0.9899999999999973,98.50417083381087,-0.24974241887387535,5.075884473248794e-07 +2.0,-1e-12,1e-12,-381.23666396499243,-381.23666396499243,1e-12,3.6191206823770984e-14,-1076.3424060647944,10547.7738227894 +2.0,-1e-12,0.01,-10.103796221005629,-10.103796221005629,0.009999999999999998,0.0021714724095012587,-11.142003434460213,48.83228621530579 +2.0,-1e-12,0.1,-2.1509490552453028,-2.1509490552453028,0.1,0.04342944819017519,-1.1355029951543987,6.104035776896826 +2.0,-1e-12,0.5,0.2597734930407328,0.2597734930407328,0.5,0.7213475204437317,-0.0418406278536771,0.1665123259945994 +2.0,-1e-12,0.9,0.4944495808701579,0.4944495808701579,0.9,8.542099422925567,-0.23473430857239258,0.0005847950216372387 +2.0,-1e-12,0.99,0.4999494953746159,0.4999494953746159,0.9900000000000004,98.50417084869056,-0.2497424188738987,5.075884472653687e-07 +2.0,-1e-16,1e-12,-381.2366639544457,-381.2366639544457,1.0000000000000004e-12,3.619120682527085e-14,-1076.3424060297907,10547.773822449415 +2.0,-1e-16,0.01,-10.1037962209568,-10.1037962209568,0.01,0.002171472409516258,-11.142003434385645,48.83228621504345 +2.0,-1e-16,0.1,-2.1509490552391997,-2.1509490552391997,0.09999999999999999,0.04342944819032517,-1.1355029951493083,6.10403577688043 +2.0,-1e-16,0.5,0.25977349304089925,0.25977349304089925,0.5,0.7213475204444816,-0.04184062785373813,0.16651232599446475 +2.0,-1e-16,0.9,0.49444958087015844,0.49444958087015844,0.8999999999999998,8.542099422926892,-0.23473430857239388,0.0005847950216371668 +2.0,-1e-16,0.99,0.4999494953746159,0.4999494953746159,0.9900000000000003,98.50417084869154,-0.2497424188738987,5.075884472653628e-07 +2.0,0.0,1e-12,-381.2366639544447,-381.2366639544447,9.999999999999992e-13,3.6191206825270956e-14,-1076.342406029787,10547.77382244938 +2.0,0.0,0.01,-10.103796220956795,-10.103796220956795,0.010000000000000002,0.0021714724095162597,-11.142003434385638,48.832286215043425 +2.0,0.0,0.1,-2.1509490552391988,-2.1509490552391988,0.1,0.04342944819032519,-1.1355029951493079,6.104035776880428 +2.0,0.0,0.5,0.2597734930408993,0.2597734930408993,0.5,0.7213475204444818,-0.041840627853738134,0.16651232599446475 +2.0,0.0,0.9,0.49444958087015844,0.49444958087015844,0.8999999999999998,8.542099422926892,-0.23473430857239388,0.0005847950216371668 +2.0,0.0,0.99,0.4999494953746159,0.4999494953746159,0.9900000000000003,98.50417084869154,-0.2497424188738987,5.075884472653628e-07 +2.0,1e-16,1e-12,-381.2366639544436,-381.2366639544436,1e-12,3.619120682527114e-14,-1076.3424060297837,10547.773822449346 +2.0,1e-16,0.01,-10.103796220956792,-10.103796220956792,0.009999999999999998,0.0021714724095162606,-11.14200343438563,48.8322862150434 +2.0,1e-16,0.1,-2.1509490552391983,-2.1509490552391983,0.1,0.0434294481903252,-1.1355029951493072,6.104035776880426 +2.0,1e-16,0.5,0.2597734930408993,0.2597734930408993,0.5,0.7213475204444818,-0.04184062785373814,0.16651232599446472 +2.0,1e-16,0.9,0.49444958087015844,0.49444958087015844,0.8999999999999998,8.542099422926892,-0.23473430857239388,0.0005847950216371668 +2.0,1e-16,0.99,0.4999494953746159,0.4999494953746159,0.9900000000000003,98.50417084869154,-0.2497424188738987,5.075884472653628e-07 +2.0,1e-12,1e-12,-381.2366639438969,-381.2366639438969,1.0000000000000004e-12,3.6191206826771003e-14,-1076.3424059947797,10547.77382210936 +2.0,1e-12,0.01,-10.103796220907963,-10.103796220907963,0.010000000000000002,0.0021714724095312594,-11.142003434311063,48.832286214781064 +2.0,1e-12,0.1,-2.1509490552330948,-2.1509490552330948,0.1,0.04342944819047519,-1.1355029951442168,6.1040357768640305 +2.0,1e-12,0.5,0.2597734930410658,0.2597734930410658,0.5,0.7213475204452316,-0.04184062785379916,0.16651232599433008 +2.0,1e-12,0.9,0.49444958087015906,0.49444958087015906,0.9,8.542099422928263,-0.2347343085723952,0.0005847950216370949 +2.0,1e-12,0.99,0.4999494953746159,0.4999494953746159,0.9900000000000003,98.50417084869252,-0.2497424188738987,5.075884472653567e-07 +2.0,1e-08,1e-12,-381.2365584767234,-381.2365584767234,1.0000000000000006e-12,3.6191221825274e-14,-1076.3420559556619,10547.770422249438 +2.0,1e-08,0.01,-10.103795732633946,-10.103795732633946,0.010000000000000002,0.0021714725595162647,-11.142002688628937,48.832283591431974 +2.0,1e-08,0.1,-2.150948994198842,-2.150948994198842,0.1,0.04342944969032521,-1.13550294423967,6.10403561290471 +2.0,1e-08,0.5,0.25977349470602257,0.25977349470602257,0.5,0.7213475279444818,-0.04184062846402732,0.16651232464792667 +2.0,1e-08,0.9,0.49444958087600643,0.49444958087600643,0.9000000000000001,8.542099436426925,-0.2347343085855539,0.0005847950209183333 +2.0,1e-08,0.99,0.49994949537462097,0.49994949537462097,0.989999999999998,98.50417086351754,-0.24974241887392204,5.07588447205846e-07 +2.0,0.2,1e-12,-11.900671319010682,-11.900671319010682,1.0000000000000056e-12,5.0438528026988925e-11,-13.958308408938066,121.26763433543209 +2.0,0.2,0.01,-4.02843722673896,-4.02843722673896,0.01,0.00834662354698426,-2.975041617856572,17.697347645607074 +2.0,0.2,0.1,-1.2024060199138842,-1.2024060199138842,0.10000000000000002,0.0858921411254614,-0.44168218182026575,3.620126714521818 +2.0,0.2,0.5,0.29053554171311546,0.29053554171311546,0.5,0.8873722313869611,-0.05414507348613152,0.14183617833786963 +2.0,0.2,0.9,0.4945651150971359,0.4945651150971359,0.9000000000000002,8.81624872590278,-0.2349949084604599,0.0005706112315139472 +2.0,0.2,0.99,0.49994959677337436,0.49994959677337436,0.9899999999999993,98.80160240835188,-0.249742885374248,5.06399649293924e-07 +2.0,0.5,1e-12,-1.499996000002,-1.499996000002,9.999999999686107e-13,5.000004999926526e-07,-0.6362915885345547,7.99987347603406 +2.0,0.5,0.01,-1.12,-1.12,0.009999999999999993,0.05555555555555554,-0.3922143971414328,4.822138733044287 +2.0,0.5,0.1,-0.43508893593264825,-0.43508893593264825,0.1,0.23123764778713218,-0.07515405275395057,1.7488244208434909 +2.0,0.5,0.5,0.3284271247461901,0.3284271247461901,0.5,1.2071067811865477,-0.0724563892467588,0.11206957519803584 +2.0,0.5,0.9,0.4947331922020552,0.4947331922020552,0.8999999999999999,9.243416490252566,-0.23537622452348936,0.0005500416456760707 +2.0,0.5,0.99,0.4999497484264798,0.4999497484264798,0.9900000000000004,99.24937185533508,-0.24974358326774265,5.046222017401653e-07 +2.0,1.0,1e-12,9.999999999995e-13,9.999999999995e-13,1e-12,1.000000000001,-4.999999999998333e-25,0.9999999999703689 +2.0,1.0,0.01,0.00995,0.00995,0.01,1.0101010101010102,-4.983291499161875e-05,0.9345088151587179 +2.0,1.0,0.1,0.095,0.095,0.1,1.1111111111111112,-0.004828991158580349,0.6027673416305359 +2.0,1.0,0.5,0.375,0.375,0.5,2.0,-0.10085660243000684,0.07671320486001368 +2.0,1.0,0.9,0.495,0.495,0.8999999999999999,9.999999999999996,-0.2359870745350298,0.0005175535907956325 +2.0,1.0,0.99,0.49995,0.49995,0.9900000000000005,100.00000000000551,-0.24974474149070058,5.016750503357336e-07 +2.0,2.0,0.01,0.37502499875,0.37502499875,0.010000000000000968,200.02000200018065,-0.10087393149304519,0.12485988350827576 +2.0,2.0,0.1,0.3774875,0.3774875,0.0999999999999995,20.2020202020203,-0.10259326427039761,0.11681360189483973 +2.0,2.0,0.5,0.4296875,0.4296875,0.5,5.333333333333333,-0.145879193147613,0.037821225911252566 +2.0,2.0,0.9,0.4954875,0.4954875,0.9000000000000002,11.695906432748567,-0.23712187377694055,0.00045875416006513277 +2.0,2.0,0.99,0.49995049875,0.49995049875,0.9900000000000028,101.51768945741584,-0.24974703956724492,4.958375041666079e-07 diff --git a/Test_Numerics/Distributions/Univariate/Test_KappaFourRegression.cs b/Test_Numerics/Distributions/Univariate/Test_KappaFourRegression.cs new file mode 100644 index 00000000..0465dc57 --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Test_KappaFourRegression.cs @@ -0,0 +1,338 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using Numerics; +using Numerics.Distributions; +using Numerics.Mathematics.Integration; + +namespace Distributions.Univariate +{ + /// Independent regression contracts for the Kappa Four numerical repair. + [TestClass] + public class Test_KappaFourRegression + { + // Exact 38 observations supplied with the September 2026 fitting regression. + private static readonly double[] FittingSample = + [ + 1.354784607887268, 0.41693252325057983, 0.8899999856948853, 0.8853314518928528, + 2.170344829559326, 1.334205150604248, 0.5150537490844727, 0.8244029879570007, + 0.5099999904632568, 0.44740423560142517, 0.739365816116333, 1.8200000524520874, + 1.9778419733047485, 0.6553186774253845, 0.8488552570343018, 2.2905187606811523, + 0.9865325689315796, 0.7076110243797302, 0.2929774820804596, 1.5241589546203613, + 0.5311949253082275, 0.5221586227416992, 0.907939612865448, 0.2859921157360077, + 0.6391091346740723, 0.6330636739730835, 0.520042359828949, 2.0499587059020996, + 1.83543860912323, 2.450000047683716, 0.6613662838935852, 1.1201441287994385, + 1.020573377609253, 0.5620101094245911, 0.5419405102729797, 2.2691876888275146, + 1.5167182683944702, 3.119999885559082 + ]; + + /// Frozen 90-digit formulas exercise shape signs, zeros, near zeros, and both tails. + [TestMethod] + public void ProbabilityFunctionsMatchIndependentDecimalOracle() + { + var assembly = typeof(Test_KappaFourRegression).Assembly; + using var reader = new StreamReader(assembly.GetManifestResourceStream(assembly.GetManifestResourceNames().Single(n => n.EndsWith("kappa-four-probabilities.csv")))); + reader.ReadLine(); + var failures = new List(); + int count = 0; + string line; + while ((line = reader.ReadLine()) != null) + { + double[] r = line.Split(',').Select(s => double.Parse(s, CultureInfo.InvariantCulture)).ToArray(); + var d = new KappaFour(0, 1, r[0], r[1]); + string context = $"k={r[0]:R}, h={r[1]:R}, p={r[2]:R}"; + double actual = d.InverseCDF(r[2]); + if (!Tools.IsFinite(actual) || Math.Abs(actual - r[4]) > 2E-12 * Math.Max(1, Math.Abs(r[4]))) + failures.Add($"quantile {context}: {actual:R} expected {r[4]:R}"); + double[] gradient = d.QuantileGradient(r[2]); + for (int component = 2; component < 4; component++) + if (!Tools.IsFinite(gradient[component]) || Math.Abs(gradient[component] - r[component + 5]) > 2E-8 * Math.Max(1E-20, Math.Abs(r[component + 5]))) + failures.Add($"gradient {component} {context}: {gradient[component]:R} expected {r[component + 5]:R}"); + // At a represented support endpoint, the public API returns the declared one-sided + // limit. Decimal may place the same rounded x just inside the exact real support. + if (r[3] > d.Minimum && r[3] < d.Maximum) + { + actual = d.CDF(r[3]); + if (!Tools.IsFinite(actual) || Math.Abs(actual - r[5]) > 2E-10) + failures.Add($"CDF {context}: {actual:R} expected {r[5]:R}"); + actual = d.PDF(r[3]); + if (!Tools.IsFinite(actual) || Math.Abs(actual - r[6]) > 2E-8 * Math.Max(1E-300, r[6])) + failures.Add($"PDF {context}: {actual:R} expected {r[6]:R}"); + } + count++; + } + Assert.AreEqual(1546, count); + Assert.IsEmpty(failures, string.Join(Environment.NewLine, failures.Take(12))); + } + + /// The reported sample receives a supported, finite-likelihood MLE initializer. + [TestMethod] + public void FittingInitializerSupportsEveryObservationWithinOriginalBounds() + { + var d = new KappaFour(); + var constraints = d.GetParameterConstraints(FittingSample); + AssertVector(new[] { -10d, Tools.DoubleMachineEpsilon, -10d, -2d }, constraints.Item2, 0); + AssertVector(new[] { 10d, 100d, 10d, 2d }, constraints.Item3, 0); + d.SetParameters(constraints.Item1); + Assert.IsTrue(Tools.IsFinite(d.LogLikelihood(FittingSample))); + foreach (double observation in FittingSample) + Assert.IsTrue(observation > d.Minimum && observation < d.Maximum); + } + + /// MLE cannot install a failed or nonfinite optimizer result. + [TestMethod] + public void MleDoesNotInstallAnUnsuccessfulEstimate() + { + var d = new KappaFour(); + double[] before = d.GetParameters; + var exception = Assert.ThrowsExactly(() => d.Estimate(FittingSample, ParameterEstimationMethod.MaximumLikelihood)); + StringAssert.Contains(exception.Message, "MaximumIterationsReached"); + AssertVector(before, d.GetParameters, 0); + } + + /// A failed seeded bootstrap propagates failure instead of returning invalid parameters. + [TestMethod] + public void BootstrapRejectsAnInsufficientFittingSample() + { + var d = new KappaFour(0, 1, 0, 0); + Assert.ThrowsExactly(() => d.Bootstrap(ParameterEstimationMethod.MaximumLikelihood, 1, 12345)); + AssertVector(new[] { 0d, 1d, 0d, 0d }, d.GetParameters, 0); + } + + /// Seeded resampling reaches estimation and propagates failure for values that round to a constant. + [TestMethod] + public void BootstrapPropagatesEstimationFailure() + { + var d = new KappaFour(1, double.Epsilon, 0, 0); + AssertVector(new[] { 1d, 1d, 1d, 1d }, d.GenerateRandomValues(4, 12345), 0d); + Assert.ThrowsExactly(() => d.Bootstrap(ParameterEstimationMethod.MaximumLikelihood, 4, 12345)); + AssertVector(new[] { 1d, double.Epsilon, 0d, 0d }, d.GetParameters, 0d); + } + + /// The endpoint likelihood singularity is preserved, rather than hidden by a fitting penalty. + [TestMethod] + public void UnrestrictedLikelihoodIncreasesTowardTheSampleMinimum() + { + double minimum = 0.2859921157360077; + double previous = double.NegativeInfinity; + foreach (double epsilon in new[] { 1E-2, 1E-4, 1E-6, 1E-8, 1E-10 }) + { + var d = new KappaFour(minimum - Math.Log(1.5) - epsilon, 1, 0, 1.5); + double likelihood = d.LogLikelihood(FittingSample); + Assert.IsTrue(Tools.IsFinite(likelihood) && likelihood > previous); + previous = likelihood; + } + } + + /// Near-zero shapes retain their continuous Gumbel limits. + [TestMethod] + public void NearZeroShapesPreserveQuantilesAndProbabilities() + { + foreach (double k in new[] { -1E-16, 0d, 1E-16 }) + foreach (double h in new[] { -1E-16, 0d, 1E-16 }) + { + var d = new KappaFour(0, 1, k, h); + Assert.AreEqual(0.366512920581664327d, d.InverseCDF(0.5), 2E-14); + Assert.AreEqual(0.5, d.CDF(0.366512920581664327d), 2E-14); + Assert.AreEqual(0.346573590279972655d, d.PDF(0.366512920581664327d), 2E-14); + Assert.AreEqual(27.631043237892857d, d.InverseCDF(0.999999999999), 2E-12); + } + Assert.AreEqual(Math.Log(0.2), new KappaFour(0, 1, 1E-16, 0.2).Minimum, 2E-14); + } + + /// Log probabilities remain finite beyond the range of ordinary probabilities. + [TestMethod] + public void LogProbabilitiesDoNotRoundThroughDensityOrCdf() + { + var gumbel = new KappaFour(0, 1, 0, 0); + Assert.AreEqual(-1089.6331584284585, gumbel.LogPDF(-7), 2E-12); + Assert.AreEqual(-40, gumbel.LogCCDF(40), 2E-14); + Assert.AreEqual(4.248354255291589E-18, gumbel.CCDF(40), 1E-31); + Assert.AreEqual(0, gumbel.PDF(double.NegativeInfinity)); + Assert.AreEqual(0, gumbel.PDF(-1000)); + var logistic = new KappaFour(0, 1, 0, -1); + Assert.AreEqual(-1000, logistic.LogPDF(-1000), 1E-12); + Assert.AreEqual(-1000, logistic.LogCDF(-1000), 1E-12); + } + + /// Finite extreme inputs do not overflow intermediate affine or latent transforms. + [TestMethod] + public void ExtremeFiniteParametersRetainRepresentableResults() + { + var bounded = new KappaFour(-1E308, 1E308, 0.5, 0); + Assert.AreEqual(1E308, bounded.Maximum, 1E293); + Assert.AreEqual(1d, bounded.CDF(1.5E308)); + Assert.AreEqual(0d, bounded.PDF(1.5E308)); + var gumbel = new KappaFour(-1E308, 1E308, 0, 0); + Assert.AreEqual(1E308, gumbel.InverseCDF(Math.Exp(-Math.Exp(-2))), 1E294); + Assert.AreEqual(Math.Log(1E308), new KappaFour(0, 1, 0, 1E308).InverseCDF(1E-300), 2E-12); + Assert.AreEqual(-1d, new KappaFour(0, 1, -1, -1E200).QuantileGradient(0.5)[2]); + Assert.AreEqual(double.NegativeInfinity, new KappaFour(0, 1, 0, -1E-308).LogPDF(-1000)); + Assert.AreEqual(0d, new KappaFour(0, 1, 0, -1E-308).PDF(-1000)); + } + + /// Compensated support residuals retain probabilities within a few floating-point steps of either endpoint. + [TestMethod] + public void BoundaryLogProbabilitiesMatchIndependentDecimalOracle() + { + var assembly = typeof(Test_KappaFourRegression).Assembly; + using var reader = new StreamReader(assembly.GetManifestResourceStream(assembly.GetManifestResourceNames().Single(n => n.EndsWith("kappa-four-boundaries.csv")))); + reader.ReadLine(); + int count = 0; + string line; + while ((line = reader.ReadLine()) != null) + { + double[] r = line.Split(',').Select(s => double.Parse(s, CultureInfo.InvariantCulture)).ToArray(); + var d = new KappaFour(0, 1, r[0], r[1]); + string context = $"k={r[0]:R}, h={r[1]:R}, x={r[2]:R}"; + Assert.AreEqual(r[3], d.LogCDF(r[2]), 5E-10, context); + Assert.AreEqual(r[4], d.LogPDF(r[2]), 5E-10, context); + count++; + } + Assert.AreEqual(45, count); + Assert.AreEqual(-74.85989550047409, new KappaFour(0, 1, 2, 0.5).LogCDF(-1.4999999999999998), 5E-12); + } + + /// Support endpoint densities equal their one-sided mathematical limits. + [TestMethod] + public void EndpointDensitiesHaveDefinedLimits() + { + foreach (var pair in new[] { (-0.5, -1d, 0d), (-1d, -1d, 1d), (-2d, -1d, double.PositiveInfinity), (-1d, 0d, 0d) }) + { + var d = new KappaFour(0, 1, pair.Item1, pair.Item2); + Assert.AreEqual(pair.Item3, d.PDF(d.Minimum)); + } + var singular = new KappaFour(0, 1, 0, 1.5); + Assert.AreEqual(double.PositiveInfinity, singular.PDF(singular.Minimum)); + Assert.AreEqual(double.NegativeInfinity, singular.LogPDF(singular.Minimum)); + Assert.AreEqual(1d, new KappaFour(0, 1, 1, 0).PDF(1)); + } + + /// Densities normalize across signs and zero-shape reductions. + [TestMethod] + public void DensityIntegratesToCdfMass() + { + foreach (var shapes in new[] { (-0.2, -0.5), (0.2, -0.5), (-0.2, 0.5), (0.2, 0.5), (0d, -1d), (0d, 0d), (0d, 1d), (1d, 1d) }) + { + var d = new KappaFour(2.5, 1.75, shapes.Item1, shapes.Item2); + var integral = new AdaptiveGaussKronrod(d.PDF, d.InverseCDF(1E-6), d.InverseCDF(1 - 1E-6)); + integral.Integrate(); + Assert.AreEqual(0.999998, integral.Result, 1E-8); + } + } + + /// Gumbel and exponential L-moments have their analytical values. + [TestMethod] + public void LinearMomentsIncludeExactZeroShapes() + { + var d = new KappaFour(); + AssertVector(new[] { 0.5772156649015329, 0.6931471805599453, 0.16992500144231236, 0.15037499278843736 }, d.LinearMomentsFromParameters(new[] { 0d, 1d, 0d, 0d }), 1E-9); + AssertVector(new[] { 1d, 0.5, 1d / 3d, 1d / 6d }, d.LinearMomentsFromParameters(new[] { 0d, 1d, 0d, 1d }), 1E-9); + foreach (double k in new[] { -1E-12, 1E-12 }) + AssertVector(new[] { 0.5772156649015329, 0.6931471805599453, 0.16992500144231236, 0.15037499278843736 }, d.LinearMomentsFromParameters(new[] { 0d, 1d, k, 1E-12 }), 1E-8); + var smallH = d.LinearMomentsFromParameters(new[] { 0d, 1d, 0.2, 0.01 }); + Assert.IsTrue(Array.TrueForAll(smallH, Tools.IsFinite)); + Assert.ThrowsExactly(() => d.LinearMomentsFromParameters(new[] { 0d, 1d, -1d, 0d })); + } + + /// Inverse L-moments solve exact limiting cases without singular Newton expressions. + [TestMethod] + public void LinearMomentFitsHandleGumbelAndExponential() + { + var d = new KappaFour(); + foreach (double[] moments in new[] { new[] { 1d, 0.5, 1d / 3d, 1d / 6d }, new[] { 0.5772156649015329, 0.6931471805599453, 0.16992500144231236, 0.15037499278843736 } }) + AssertVector(moments, d.LinearMomentsFromParameters(d.ParametersFromLinearMoments(moments)), 1E-6); + } + + /// Iteration exhaustion cannot return an unconverged L-moment fit. + [TestMethod] + public void LinearMomentFitRejectsExhaustedIteration() + { + Assert.ThrowsExactly(() => new KappaFour().ParametersFromLinearMoments(new[] { 0d, 1d, -0.9, 0.83375 })); + Assert.ThrowsExactly(() => new KappaFour().ParametersFromLinearMoments(new[] { double.NaN, 1d, 0d, 0.1 })); + } + + /// Finite moments are accurate and divergent moments are reported as undefined. + [TestMethod] + public void MomentsRespectExistenceAndKnownValues() + { + var heavy = new KappaFour(0, 1, -0.5, 0); + Assert.AreEqual(1.544907701811032, heavy.Mean, 1E-8); + Assert.IsTrue(double.IsNaN(heavy.StandardDeviation)); + Assert.IsTrue(double.IsNaN(heavy.Skewness)); + Assert.IsTrue(double.IsNaN(heavy.Kurtosis)); + var exponential = new KappaFour(0, 1, 0, 1); + AssertVector(new[] { 1d, 1d, 2d, 9d }, new[] { exponential.Mean, exponential.StandardDeviation, exponential.Skewness, exponential.Kurtosis }, 1E-8); + exponential.Xi = 3; + exponential.Alpha = 2; + Assert.AreEqual(5d, exponential.Mean, 1E-9); + Assert.AreEqual(2d, exponential.StandardDeviation, 1E-9); + Assert.IsTrue(double.IsNaN(new KappaFour(0, 1, 1, -1).Mean)); + } + + /// Modes include boundaries, interior stationary points, and nonunique cases. + [TestMethod] + public void ModesIncludeSupportEndpoints() + { + Assert.AreEqual(0d, new KappaFour(0, 1, 0, 1).Mode); + Assert.AreEqual(1d, new KappaFour(0, 1, 1, 0).Mode); + Assert.AreEqual(2.5, new KappaFour(2.5, 1.75, 0, 0).Mode, 1E-14); + Assert.IsTrue(double.IsNaN(new KappaFour(0, 1, 1, 1).Mode)); + Assert.IsTrue(double.IsNaN(new KappaFour(0, 1, 2, 2).Mode)); + Assert.AreEqual(1E-20, new KappaFour(0, 1, 1E-20, 0).Mode, 1E-35); + } + + /// Analytical generalized Pareto and reverse exponential L-moments verify gamma ratios independently. + [TestMethod] + public void LinearMomentsMatchAnalyticalFamilies() + { + var d = new KappaFour(); + foreach (double h in new[] { -0.5, -0.01, 0d, 1E-12, 0.001, 0.01, 0.5, 1d, 2d }) + AssertVector(new[] { h / (h + 1), 1 / ((h + 1) * (h + 2)), (h - 1) / (h + 3), + (h - 1) * (h - 2) / ((h + 3) * (h + 4)) }, d.LinearMomentsFromParameters(new[] { 0d, 1d, 1d, h }), 2E-9); + foreach (double k in new[] { -0.5, -1E-12, 0d, 1E-12, 0.2, 1d, 2d }) + AssertVector(new[] { 1 / (1 + k), 1 / ((1 + k) * (2 + k)), (1 - k) / (3 + k), + (1 - k) * (2 - k) / ((3 + k) * (4 + k)) }, d.LinearMomentsFromParameters(new[] { 0d, 1d, k, 1d }), 2E-9); + } + + /// Nonfinite and degenerate data propagate failure while retaining the original distribution. + [TestMethod] + public void InvalidFitsPreserveExistingParameters() + { + var d = new KappaFour(2, 3, 0.2, 0.5); + double[] original = d.GetParameters; + foreach (var method in new[] { ParameterEstimationMethod.MethodOfLinearMoments, ParameterEstimationMethod.MaximumLikelihood }) + { + Assert.ThrowsExactly(() => d.Estimate(new[] { 1d, 2d, 3d, double.NaN }, method)); + AssertVector(original, d.GetParameters, 0d); + } + Assert.ThrowsExactly(() => d.Estimate(new[] { 1d, 1d, 1d, 1d }, ParameterEstimationMethod.MaximumLikelihood)); + AssertVector(original, d.GetParameters, 0d); + } + + /// Quantile derivatives have finite, accurate zero-shape limits. + [TestMethod] + public void QuantileGradientsIncludeZeroShapeLimits() + { + // Q=xi-alpha*log(-log(p)); shape derivatives follow the Taylor limits. + double q = 0.366512920581664327d; + double[] expected = { 1, q, -0.5 * q * q, 0.346573590279972655d }; + foreach (double k in new[] { -1E-12, 0d, 1E-12 }) + foreach (double h in new[] { -1E-12, 0d, 1E-12 }) + AssertVector(expected, new KappaFour(0, 1, k, h).QuantileGradient(0.5), 1E-10); + var d = new KappaFour(0, 1, 0, 0); + var j = d.QuantileJacobian(new[] { 0.1, 0.3, 0.6, 0.9 }, out double determinant); + Assert.IsTrue(Tools.IsFinite(determinant) && determinant != 0); + Assert.AreEqual(1d, j[0, 0]); + } + + private static void AssertVector(double[] expected, double[] actual, double tolerance) + { + Assert.HasCount(expected.Length, actual); + for (int i = 0; i < expected.Length; i++) + Assert.AreEqual(expected[i], actual[i], tolerance, $"component {i}"); + } + } +} diff --git a/Test_Numerics/Test_Numerics.csproj b/Test_Numerics/Test_Numerics.csproj index 844e0a35..723e0131 100644 --- a/Test_Numerics/Test_Numerics.csproj +++ b/Test_Numerics/Test_Numerics.csproj @@ -32,4 +32,9 @@ + + + + + diff --git a/docs/distributions/kappa-four-repair.md b/docs/distributions/kappa-four-repair.md new file mode 100644 index 00000000..5fcc8c28 --- /dev/null +++ b/docs/distributions/kappa-four-repair.md @@ -0,0 +1,96 @@ +# Kappa Four numerical repair and regression evidence + +Review baseline: `94d1713e03a4c416dd472d6fbef0ce907881e9d1`. Approved repair implemented September 8, 2026. + +The user approved the five numerical repair slices after reviewing the audit. This repair preserves public signatures, parameter order, serialization property names, constructor defaults, optimizer selection, fitting bounds, random-number behavior, and the July zero-kappa formula corrections. It does not modify Differential Evolution, restrict the distribution family, penalize the likelihood, or change convergence tolerances. + +A declaration comparison against the baseline found no removed or changed public declaration. The four additions are overrides of the existing inherited `LogPDF`, `LogCDF`, `CCDF`, and `LogCCDF` methods. + +## Implemented slices + +| Slice | Result | +| --- | --- | +| Distribution evaluation and support | Logarithmic PDF/CDF/tail calculations; `Log1p`/`Expm1` divided differences; exact zero-shape limits; stable finite support; mathematical endpoint densities. A private compensated-arithmetic helper retains support residuals when ordinary double logarithms nearly cancel. Affine overflow is avoided when the final answer remains representable. | +| L-moments | Log-gamma probability-weighted moments, exact Gumbel and zero-hondo limits, integration of the actual quantile near zero shapes, strict finite-input and moment-existence checks. Hosking's iteration retains tolerance `1e-6`, at most 20 iterations, ten step reductions, initial shapes and estimation region. Exhaustion throws; final location/scale use moments at the accepted shapes. | +| Fitting | Accept initializers only with valid parameters, unchanged bounds and finite sample likelihood. Use the existing GEV initializer within established K4 bounds when needed. Require successful Nelder-Mead status and a valid finite-likelihood final fit. Estimate and bootstrap propagate failure without installing or returning failed estimates. | +| Moment and mode properties | Standardized quantile quadrature over the full probability interval, with central moments about the computed mean. Relative tolerance `1e-10`, absolute tolerance `1e-12`; return `NaN` for nonexistent or unresolved moments. Modes include endpoints and the existing mathematical stationary point; nonunique modes return `NaN`. Cache invalidation remains in the parameter setters. | +| Derivatives and documentation | Continuous zero-shape quantile gradients and stable nearby evaluations. Jacobian layout and LU determinant retained. Numerical domains, estimator failure and the unimplemented uncertainty methods documented. | + +The moment integral pairs the two transformed tails before estimating error, so a near-zero mean is checked against the complete integral rather than two separately large contributions. Each half uses `p=u^8/2` or its survival counterpart, including its Jacobian. No tail truncation or actual-shape substitution is used. + +`ParameterCovariance` and `QuantileVariance` remain explicitly unimplemented. Floating-point calculations and estimated quadrature errors are not a proof of absolute accuracy for all real inputs. + +## Reported fitting regression + +The exact 38 observations are frozen in `Test_KappaFourRegression.FittingSample`. The following controlled comparison was run against the audit baseline before source changes. These historical statuses describe that baseline; repaired probability arithmetic can change subsequent optimizer trajectories. + +| Baseline configuration | Status | Iterations | Evaluations | Log likelihood | +| --- | --- | ---: | ---: | ---: | +| Current DE, seed 12345, population 40 | MaximumIterationsReached | 10,000 | 400,000 | -23.472600041817795 | +| Only restore pre-September-1 DE boundary repair | Success | 1,231 | 49,280 | -23.522258636620737 | +| Current DE with pre-July zero-kappa PDF behavior | Same as current DE | 10,000 | 400,000 | -23.472600041817795 | + +No DE objective evaluation used exactly zero kappa in these comparisons. The July correction therefore did not explain the reported DE convergence change. + +Current-DE best baseline parameters were `[-0.02304299227317367, 1.3774672309514362, 0.376420487117973, 1.264141217131441]`, with lower endpoint `0.28599211573600747`. Old-repair parameters were `[-0.022253095483755644, 1.3752818361277244, 0.34855569679255555, 1.2628508861048067]`, with lower endpoint `0.2859921157360075`. Both approach the sample minimum `0.2859921157360077`. + +The old L-moment initializer excluded two observations: its lower support was approximately `0.3818457477`. Repaired initialization uses the existing GEV candidate `[0.750064308812039, 0.464304902648505, -0.176396212686591, 0]` within the original K4 bounds: lower `[-10, DoubleMachineEpsilon, -10, -2]`, upper `[10, 100, 10, 2]`. It has finite sample likelihood. The built-in MLE uses Nelder-Mead; unsuccessful termination is now reported as an exception and leaves the distribution unchanged. + +### Why unrestricted MLE remains unresolved + +Let `m` be the minimum observation. Set `alpha=1`, `kappa=0`, `hondo=1.5`, and `xi=m-log(1.5)-epsilon`, with positive epsilon tending to zero. These parameters remain within the existing bounds. The minimum observation approaches the lower endpoint from inside support, where density diverges because `hondo>1`; all other observation contributions remain finite. Thus the likelihood is unbounded and there is no finite global MLE for this problem. + +The deterministic regression tests require increasing finite likelihood along this sequence. They do not require DE to find a finite global maximum. Selecting an MLE restriction or replacement estimator remains a separate scientific-policy decision. Infinite endpoint log-density contributions retain the existing negative-infinity likelihood convention. + +## Relevant history + +| Commit | Date | Audit finding | +| --- | --- | --- | +| `44c70af` | 2025-04-28 | PDF/CDF/quantile rewrite introduced the incorrect zero-kappa PDF and inverse expression. | +| `7329914` | 2026-05-24 | Inherited invalid-likelihood handling changed to negative infinity. | +| `b9af61c` | 2026-07-24 | Corrected the zero-kappa PDF and inverse expression; retained. | +| `48c99a2` | 2026-07-24 | Added finite-shape tests; did not change the class. | +| `2b57771` | 2026-09-01 | DE boundary repair changed; controlled comparison altered convergence status. | + +Several L-moment defects predate these changes and exist in the earliest available September 2023 class history. + +## Independent evidence and validation + +The frozen oracle fixtures and their generator live in `Test_Numerics/Distributions/Univariate/Fixtures`. Python's standard-library Decimal evaluates the defining formulas at 90 digits using exact conversions of the input doubles. Shape derivatives are computed independently by high-precision symmetric differences. The 45 endpoint cases use 400 digits to retain subnormal distances above zero. Tests require no Python, R, network, or external numerical package at runtime. + +Regeneration was checked byte for byte. SHA-256: probabilities `483358B5F45D909C6EDAB850FAF1C502B783691E408437557CA48BDBC2A3D454`; boundaries `B057D04D9D32F3FF45A73F6A2C7E4204EC3FC241DFAF01172EB3E494105E0909`. + +- 1,546 frozen cases cover 15 values of each shape (both signs, exact zeros and near zeros), central probabilities and tails. Checks include quantile, PDF, CDF and both shape gradients. +- 45 additional frozen cases evaluate log probabilities within four representable steps of finite support boundaries. A one-step reverse-exponential boundary case is also checked. +- When a quantile rounds onto the public represented support endpoint, its PDF/CDF follow the documented one-sided limit; interior oracle comparisons are restricted to represented interior arguments. +- Analytical Gumbel, exponential, uniform, logistic, generalized Pareto and zero-hondo reductions, normalization, moment existence, endpoint modes, invalid data and the reported fitting sample supplement the formula grid. +- The heavy-tail mean counterexample `(0,1,-0.5,0)` is checked against `2*(sqrt(pi)-1) = 1.544907701811032`. +- Exact exponential/Gumbel inverse L-moments, iteration exhaustion and gamma-overflow examples have dedicated regressions. + +Formula/source cross-checks from the audit: [SciPy kappa4](https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.kappa4.html), [Hosking's lmom Fortran](https://raw.githubusercontent.com/cran/lmom/master/src/lmoments.f), and [lmomco parkap](https://rdrr.io/cran/lmomco/src/R/parkap.R). The referenced [nsRFA implementation](https://rdrr.io/cran/nsRFA/src/R/KAPPA.R) was not accepted as an unquestioned oracle; its zero-shape substitution is numerically unreliable. + +### Completed validation + +`dotnet build -c Release` completed with **zero warnings and zero errors** on all four target frameworks. XML documentation enforcement was enabled. + +The final full run (`dotnet test -c Release --no-build --logger 'trx;LogFilePrefix=kappa-four-repair'`) produced: + +| Framework | Passed | Failed | K4 methods passed | +| --- | ---: | ---: | ---: | +| net481 | 2,524 | 1 | 32 | +| net8.0 | 2,524 | 1 | 32 | +| net9.0 | 2,524 | 1 | 32 | +| net10.0 | 2,524 | 1 | 32 | + +The sole failure on each framework was `Data.TimeSeriesAnalysis.Test_TimeSeriesDownload.BOM_FullPor_Goodradigbee_Discharge`: the live BOM API returned HTTP 500, `DatasourceError`, `Error connecting to WDP.` The same failure persisted in a focused rerun. An earlier full run also saw intermittent Cotter River and Murray River API failures; these passed in the final run. No download code or tests were changed or suppressed. + +All 32 K4 methods (12 original and 20 new) passed on each framework, including rejection of the reported sample's iteration-exhausted MLE and propagation of a seeded bootstrap estimation failure. The full-run TRXs were inspected to verify those exact K4 outcomes. + +Final evidence files in `Test_Numerics/TestResults`: + +- `kappa-four-repair_net481_20260908112347.trx` +- `kappa-four-repair_net8.0_20260908112339.trx` +- `kappa-four-repair_net9.0_20260908112337.trx` +- `kappa-four-repair_net10.0_20260908112340.trx` + +Haden explicitly authorized committing these reviewed changes with the documented external BOM service failure on September 8, 2026. This exception applies to this repair's commit gate. The MLE estimator-policy decision remains separate, as approved in the repair plan. From 532026f0933e40ef2dfde96726e0dfedbfa12a1f Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Tue, 8 Sep 2026 13:32:00 -0600 Subject: [PATCH 201/222] Reuse shared APIs and remove redundant private helpers --- .../Univariate/EmpiricalDistribution.cs | 27 -------- Numerics/Distributions/Univariate/Mixture.cs | 60 ++++++----------- Numerics/Distributions/Univariate/Normal.cs | 65 +------------------ Numerics/Sampling/MCMC/NUTS.cs | 14 +--- Numerics/Sampling/MCMC/SNIS.cs | 22 ++----- .../Sampling/MCMC/Support/MCMCDiagnostics.cs | 21 +----- .../Distributions/Univariate/Test_Normal.cs | 44 +++++++++++++ Test_Numerics/Utilities/Test_Tools.cs | 46 +++++++++++++ 8 files changed, 122 insertions(+), 177 deletions(-) diff --git a/Numerics/Distributions/Univariate/EmpiricalDistribution.cs b/Numerics/Distributions/Univariate/EmpiricalDistribution.cs index 0302491f..5d2bc4f1 100644 --- a/Numerics/Distributions/Univariate/EmpiricalDistribution.cs +++ b/Numerics/Distributions/Univariate/EmpiricalDistribution.cs @@ -834,33 +834,6 @@ private static double[] DepositAtoms(IList values, IList masses, return lattice; } - /// Returns the smallest value of a list. - /// The list. - private static double Min(IList values) - { - double minimum = double.MaxValue; - for (int i = 0; i < values.Count; i++) if (values[i] < minimum) minimum = values[i]; - return minimum; - } - - /// Returns the largest value of a list. - /// The list. - private static double Max(IList values) - { - double maximum = double.MinValue; - for (int i = 0; i < values.Count; i++) if (values[i] > maximum) maximum = values[i]; - return maximum; - } - - /// Returns the sum of a list. - /// The list. - private static double Sum(IList values) - { - double sum = 0d; - for (int i = 0; i < values.Count; i++) sum += values[i]; - return sum; - } - /// /// Serializes the X and probability tables, probability ordering, and interpolation /// transforms using invariant round-trip numeric formatting. diff --git a/Numerics/Distributions/Univariate/Mixture.cs b/Numerics/Distributions/Univariate/Mixture.cs index c45736eb..557396cf 100644 --- a/Numerics/Distributions/Univariate/Mixture.cs +++ b/Numerics/Distributions/Univariate/Mixture.cs @@ -155,28 +155,6 @@ private void NormalizeComponentWeights() } } - /// - /// Determines whether a value is finite on every target framework. - /// - /// The value to inspect. - /// when the value is neither NaN nor infinite. - private static bool IsFinite(double value) - { - return !double.IsNaN(value) && !double.IsInfinity(value); - } - - /// - /// Restricts a value to an inclusive interval on every target framework. - /// - /// The value to restrict. - /// The inclusive lower bound. - /// The inclusive upper bound. - /// The restricted value. - private static double Clamp(double value, double minimum, double maximum) - { - return value < minimum ? minimum : value > maximum ? maximum : value; - } - /// /// Returns the next representable value greater than the supplied value. /// @@ -227,7 +205,7 @@ private double PositiveConditionalQuantileProbability(int componentIndex, double private bool TryGetPositiveMass(int componentIndex, out double positiveMass) { positiveMass = Distributions[componentIndex].CCDF(0.0); - return IsFinite(positiveMass) && positiveMass > 0.0; + return Tools.IsFinite(positiveMass) && positiveMass > 0.0; } /// @@ -270,7 +248,7 @@ private double PositiveConditionalCDF(int componentIndex, double x) { if (x <= 0.0 || !TryGetPositiveMass(componentIndex, out double positiveMass)) return 0.0; double probability = 1.0 - Distributions[componentIndex].CCDF(x) / positiveMass; - return Clamp(probability, 0.0, 1.0); + return Tools.Clamp(probability, 0.0, 1.0); } /// @@ -284,7 +262,7 @@ private double PositiveConditionalCCDF(int componentIndex, double x) if (x < 0.0) return 1.0; if (!TryGetPositiveMass(componentIndex, out double positiveMass)) return double.NaN; double probability = Distributions[componentIndex].CCDF(x) / positiveMass; - return Clamp(probability, 0.0, 1.0); + return Tools.Clamp(probability, 0.0, 1.0); } /// /// Refreshes validity and cached results after zero-inflation configuration changes. @@ -732,7 +710,7 @@ public void SetParameters(ref double[] parameters) } double componentMass = IsZeroInflated ? 1.0 - ZeroWeight : 1.0; - if (weightSum <= 0.0 || !IsFinite(weightSum)) + if (weightSum <= 0.0 || !Tools.IsFinite(weightSum)) { double uniformWeight = componentMass / componentCount; for (int i = 0; i < componentCount; i++) Weights[i] = uniformWeight; @@ -762,7 +740,7 @@ public void SetParameters(ref double[] parameters) /// public override ArgumentOutOfRangeException? ValidateParameters(IList parameters, bool throwException) { - if (IsZeroInflated && (!IsFinite(ZeroWeight) || ZeroWeight < 0.0 || ZeroWeight >= 1.0)) + if (IsZeroInflated && (!Tools.IsFinite(ZeroWeight) || ZeroWeight < 0.0 || ZeroWeight >= 1.0)) { var exception = new ArgumentOutOfRangeException( nameof(ZeroWeight), @@ -773,7 +751,7 @@ public void SetParameters(ref double[] parameters) for (int i = 0; i < Distributions.Count(); i++) { - if (!IsFinite(Weights[i]) || Weights[i] < 0.0 || Weights[i] > 1.0) + if (!Tools.IsFinite(Weights[i]) || Weights[i] < 0.0 || Weights[i] > 1.0) { var exception = new ArgumentOutOfRangeException( nameof(Weights), @@ -785,7 +763,7 @@ public void SetParameters(ref double[] parameters) double totalMass = IsZeroInflated ? ZeroWeight : 0.0; for (int i = 0; i < Distributions.Count(); i++) totalMass += Weights[i]; - if (!IsFinite(totalMass) || !totalMass.AlmostEquals(1.0, 1E-8)) + if (!Tools.IsFinite(totalMass) || !totalMass.AlmostEquals(1.0, 1E-8)) { var exception = new ArgumentOutOfRangeException( nameof(Weights), @@ -900,7 +878,7 @@ double EStep(double[] parameters) double value = sample[rowIndex]; if (IsZeroInflated && value == 0.0) { - if (!IsFinite(ZeroWeight) || ZeroWeight <= 0.0) + if (!Tools.IsFinite(ZeroWeight) || ZeroWeight <= 0.0) { throw CreateImpossibleRowException(rowIndex, value); } @@ -924,7 +902,7 @@ double EStep(double[] parameters) if (logProbability > maximumLogProbability) maximumLogProbability = logProbability; } - if (!IsFinite(maximumLogProbability)) + if (!Tools.IsFinite(maximumLogProbability)) { throw CreateImpossibleRowException(rowIndex, value); } @@ -934,13 +912,13 @@ double EStep(double[] parameters) { scaledProbabilitySum += Math.Exp(responsibilities[rowIndex, componentIndex] - maximumLogProbability); } - if (!IsFinite(scaledProbabilitySum) || scaledProbabilitySum <= 0.0) + if (!Tools.IsFinite(scaledProbabilitySum) || scaledProbabilitySum <= 0.0) { throw CreateImpossibleRowException(rowIndex, value); } double rowLogProbability = maximumLogProbability + Math.Log(scaledProbabilitySum); - if (!IsFinite(rowLogProbability)) + if (!Tools.IsFinite(rowLogProbability)) { throw CreateImpossibleRowException(rowIndex, value); } @@ -973,7 +951,7 @@ double[] MStep(double[] parameters) double componentWeightSum = mleWeights.Sum(); double componentWeightTarget = IsZeroInflated ? 1.0 - ZeroWeight : 1.0; - if (!IsFinite(componentWeightSum) || componentWeightSum <= 0.0) + if (!Tools.IsFinite(componentWeightSum) || componentWeightSum <= 0.0) { throw new InvalidOperationException("Mixture EM cannot update component weights because no finite positive responsibility mass is available."); } @@ -994,7 +972,7 @@ double Objective(double[] parameters) var distribution = (Mixture)Clone(); distribution.SetParameters(mleWeights, parameters); double logLikelihood = distribution.LogLikelihood(sample); - return IsFinite(logLikelihood) ? logLikelihood : double.NegativeInfinity; + return Tools.IsFinite(logLikelihood) ? logLikelihood : double.NegativeInfinity; } InvalidOperationException CreateImpossibleRowException(int rowIndex, double value) @@ -1079,12 +1057,12 @@ public override double CDF(double x) { hurdleProbability += Weights[i] * PositiveConditionalCDF(i, x); } - return Clamp(hurdleProbability, 0.0, 1.0); + return Tools.Clamp(hurdleProbability, 0.0, 1.0); } double probability = 0.0; for (int i = 0; i < Distributions.Count(); i++) probability += Weights[i] * Distributions[i].CDF(x); - return Clamp(probability, 0.0, 1.0); + return Tools.Clamp(probability, 0.0, 1.0); } /// @@ -1115,7 +1093,7 @@ public override double LogCCDF(double x) { probability += Weights[i] * PositiveConditionalCCDF(i, x); } - return Math.Log(Clamp(probability, 0.0, 1.0)); + return Math.Log(Tools.Clamp(probability, 0.0, 1.0)); } var logProbabilities = new List(); @@ -1144,7 +1122,7 @@ public override double InverseCDF(double probability) if (_empiricalCDFCreated) { double empiricalValue = _empiricalCDF.InverseCDF(probability); - return Clamp(empiricalValue, Minimum, Maximum); + return Tools.Clamp(empiricalValue, Minimum, Maximum); } double componentProbability = IsZeroInflated @@ -1166,7 +1144,7 @@ public override double InverseCDF(double probability) double value; try { - if (lowerBound.AlmostEquals(upperBound)) return Clamp(lowerBound, Minimum, Maximum); + if (lowerBound.AlmostEquals(upperBound)) return Tools.Clamp(lowerBound, Minimum, Maximum); value = Brent.Solve(y => probability - CDF(y), lowerBound, upperBound, 1E-6, 100, true); } catch (Exception) @@ -1175,7 +1153,7 @@ public override double InverseCDF(double probability) value = _empiricalCDF.InverseCDF(probability); } - return Clamp(value, Minimum, Maximum); + return Tools.Clamp(value, Minimum, Maximum); } /// diff --git a/Numerics/Distributions/Univariate/Normal.cs b/Numerics/Distributions/Univariate/Normal.cs index db0ce8b7..a8f4142e 100644 --- a/Numerics/Distributions/Univariate/Normal.cs +++ b/Numerics/Distributions/Univariate/Normal.cs @@ -480,7 +480,7 @@ private static double r8_normal_01_cdf_inverse(double p) if (Math.Abs(q) <= 0.425) { r = 0.180625 - q * q; - value = q * r8poly_value(8, a, r) / r8poly_value(8, b, r); + value = q * Evaluate.Polynomial(a, r) / Evaluate.Polynomial(b, r); } else { @@ -498,12 +498,12 @@ private static double r8_normal_01_cdf_inverse(double p) if (r <= 5.0) { r = r - 1.6; - value = r8poly_value(8, c, r) / r8poly_value(8, d, r); + value = Evaluate.Polynomial(c, r) / Evaluate.Polynomial(d, r); } else { r = r - 5.0; - value = r8poly_value(8, e, r) / r8poly_value(8, f, r); + value = Evaluate.Polynomial(e, r) / Evaluate.Polynomial(f, r); } if (q < 0.0) @@ -516,65 +516,6 @@ private static double r8_normal_01_cdf_inverse(double p) return value; } - /// - /// R8POLY_VALUE evaluates a double precision polynomial. - /// - /// The number of coefficients. - /// The coefficients. - /// The point to evaluate. - private static double r8poly_value(int n, double[] a, double x) - { - //****************************************************************************80 - // - // Purpose: - // - // R8POLY_VALUE evaluates a double precision polynomial. - // - // Discussion: - // - // For sanity's sake, the value of N indicates the NUMBER of - // coefficients, or more precisely, the ORDER of the polynomial, - // rather than the DEGREE of the polynomial. The two quantities - // differ by 1, but cause a great deal of confusion. - // - // Given N and A, the form of the polynomial is: - // - // p(x) = a[0] + a[1] * x + ... + a[n-2] * x^(n-2) + a[n-1] * x^(n-1) - // - // Licensing: - // - // This code is distributed under the GNU LGPL license. - // - // Modified: - // - // 13 August 2004 - // - // Author: - // - // John Burkardt - // - // Parameters: - // - // Input, int N, the order of the polynomial. - // - // Input, double A[N], the coefficients of the polynomial. - // A[0] is the constant term. - // - // Input, double X, the point at which the polynomial is to be evaluated. - // - // Output, double R8POLY_VALUE, the value of the polynomial at X. - // - - int i; - double value = 0.0; - for (i = n - 1; 0 <= i; i--) - { - value = value * x + a[i]; - } - - return value; - } - /// public override double InverseCDF(double probability) { diff --git a/Numerics/Sampling/MCMC/NUTS.cs b/Numerics/Sampling/MCMC/NUTS.cs index a28ed1b9..3cfc1185 100644 --- a/Numerics/Sampling/MCMC/NUTS.cs +++ b/Numerics/Sampling/MCMC/NUTS.cs @@ -799,7 +799,7 @@ protected override ParameterSet ChainIteration(int index, ParameterSet state) // If the subtree is valid, consider accepting its candidate if (subtree.Valid) { - double logSumWeightNew = LogSumExp(logSumWeight, subtree.LogSumWeight); + double logSumWeightNew = Tools.LogSumExp(logSumWeight, subtree.LogSumWeight); double acceptProb = Math.Exp(subtree.LogSumWeight - logSumWeightNew); if (_chainPRNGs[index].NextDouble() < acceptProb) { @@ -1165,7 +1165,7 @@ private TreeState BuildTree(Vector theta, Vector momentum, double epsilon, int d } // Multinomial sampling: accept candidate from tree2 with appropriate probability - double logSumWeightNew = LogSumExp(tree.LogSumWeight, tree2.LogSumWeight); + double logSumWeightNew = Tools.LogSumExp(tree.LogSumWeight, tree2.LogSumWeight); double acceptTree2Prob = Math.Exp(tree2.LogSumWeight - logSumWeightNew); if (_chainPRNGs[chainIndex].NextDouble() < acceptTree2Prob) { @@ -1301,16 +1301,6 @@ private void DualAveragingUpdate(int chainIndex, double avgAcceptProb) _chainStepSizes[chainIndex] = 1e5; } - /// - /// Computes log(exp(a) + exp(b)) in a numerically stable way. - /// - private static double LogSumExp(double a, double b) - { - double max = Math.Max(a, b); - if (double.IsNegativeInfinity(max)) return double.NegativeInfinity; - return max + Math.Log(Math.Exp(a - max) + Math.Exp(b - max)); - } - /// /// Evaluates the log-likelihood, returning negative infinity if the parameters are out of range. /// This prevents ArgumentOutOfRangeException from propagating during leapfrog integration diff --git a/Numerics/Sampling/MCMC/SNIS.cs b/Numerics/Sampling/MCMC/SNIS.cs index 96049300..14d8efef 100644 --- a/Numerics/Sampling/MCMC/SNIS.cs +++ b/Numerics/Sampling/MCMC/SNIS.cs @@ -156,33 +156,33 @@ public override void Sample() // Get the maximum a posteriori for (int i = 0; i < Iterations; i++) - if (IsFinite(MarkovChains[0][i].Weight) && MarkovChains[0][i].Weight > MAP.Weight) + if (Tools.IsFinite(MarkovChains[0][i].Weight) && MarkovChains[0][i].Weight > MAP.Weight) MAP = MarkovChains[0][i].Clone(); // Get the normalization factor double max = MAP.Weight; - if (!IsFinite(max)) + if (!Tools.IsFinite(max)) throw new InvalidOperationException("SNIS failed because all importance weights are non-finite."); double sum = 0; for (int i = 0; i < Iterations; i++) { - if (IsFinite(MarkovChains[0][i].Weight)) + if (Tools.IsFinite(MarkovChains[0][i].Weight)) { sum += Math.Exp(MarkovChains[0][i].Weight - max); } } - if (!IsFinite(sum) || sum <= 0d) + if (!Tools.IsFinite(sum) || sum <= 0d) throw new InvalidOperationException("SNIS failed because the finite importance weights could not be normalized."); double normalization = max + Math.Log(sum); - if (!IsFinite(normalization)) + if (!Tools.IsFinite(normalization)) throw new InvalidOperationException("SNIS failed because the importance-weight normalization is non-finite."); // Compute the posterior weights Parallel.For(0, Iterations, (idx) => { - double w = IsFinite(MarkovChains[0][idx].Weight) ? Math.Exp(MarkovChains[0][idx].Weight - normalization) : 0d; + double w = Tools.IsFinite(MarkovChains[0][idx].Weight) ? Math.Exp(MarkovChains[0][idx].Weight - normalization) : 0d; MarkovChains[0][idx] = new ParameterSet(MarkovChains[0][idx].Values, MarkovChains[0][idx].Fitness, w); }); @@ -210,15 +210,5 @@ public override void Sample() } } - /// - /// Determines whether a value is finite. - /// - /// The value to evaluate. - /// true when the value is neither NaN nor infinite; otherwise false. - private static bool IsFinite(double value) - { - return !double.IsNaN(value) && !double.IsInfinity(value); - } - } } diff --git a/Numerics/Sampling/MCMC/Support/MCMCDiagnostics.cs b/Numerics/Sampling/MCMC/Support/MCMCDiagnostics.cs index aec3e8ca..41a392a9 100644 --- a/Numerics/Sampling/MCMC/Support/MCMCDiagnostics.cs +++ b/Numerics/Sampling/MCMC/Support/MCMCDiagnostics.cs @@ -228,7 +228,7 @@ private static double ComputeConservativeEffectiveSampleSize(double[][] chains) /// The split-chain indicator ESS. private static double ComputeQuantileEffectiveSampleSize(double[][] chains, double probability) { - double threshold = Quantile(Flatten(chains), probability); + double threshold = Statistics.Percentile(Flatten(chains), probability); var indicators = new double[chains.Length][]; for (int chainIndex = 0; chainIndex < chains.Length; chainIndex++) { @@ -413,7 +413,7 @@ private static double[][] RankNormalize(double[][] chains) /// Absolute deviations from the pooled median. private static double[][] FoldAroundMedian(double[][] chains) { - double median = Quantile(Flatten(chains), 0.5d); + double median = Statistics.Percentile(Flatten(chains), 0.5d); var folded = new double[chains.Length][]; for (int chainIndex = 0; chainIndex < chains.Length; chainIndex++) { @@ -477,23 +477,6 @@ private static double[] Flatten(double[][] chains) return flattened; } - /// - /// Computes the R type-7 sample quantile. - /// - /// Pooled sample values. - /// Probability in the closed unit interval. - /// The interpolated sample quantile. - private static double Quantile(double[] values, double probability) - { - var sorted = values.OrderBy(value => value).ToArray(); - double position = (sorted.Length - 1d) * probability; - int lower = (int)Math.Floor(position); - int upper = (int)Math.Ceiling(position); - if (lower == upper) - return sorted[lower]; - return sorted[lower] + (position - lower) * (sorted[upper] - sorted[lower]); - } - /// /// Determines whether pooled diagnostic input is nonfinite or effectively constant. /// diff --git a/Test_Numerics/Distributions/Univariate/Test_Normal.cs b/Test_Numerics/Distributions/Univariate/Test_Normal.cs index 8cf140f6..e7817422 100644 --- a/Test_Numerics/Distributions/Univariate/Test_Normal.cs +++ b/Test_Numerics/Distributions/Univariate/Test_Normal.cs @@ -315,6 +315,50 @@ public void Test_PDF() Assert.AreEqual(0.03549, N2.PDF(-1), 1e-04); } + /// + /// Verifies standard normal quantiles retain their bitwise values across approximation branches and tails. + /// + /// The nonexceedance probability. + /// The IEEE 754 bits of the existing quantile result. + /// Compatibility values were captured from the pre-refactor c0d67b9 implementation. + [TestMethod] + [DataRow(0d, -4592761413495173886L)] + [DataRow(double.Epsilon, -4592761413495173886L)] + [DataRow(1e-300, -4592961304261246354L)] + [DataRow(1e-20, -4601968184877893045L)] + [DataRow(1e-12, -4603765893743795414L)] + [DataRow(0.075d, -4614210144286022365L)] + [DataRow(0.5d, 0L)] + [DataRow(0.925d, 4609161892568753444L)] + [DataRow(0.999999999999d, 4619606146584096420L)] + [DataRow(0.9999999999999999d, 4620811176048912977L)] + [DataRow(1d, 9218868437227405312L)] + public void Test_StandardZ_PreservesBranchAndTailValues(double probability, long expectedBits) + { + Assert.AreEqual(expectedBits, BitConverter.DoubleToInt64Bits(Normal.StandardZ(probability))); + } + + /// + /// Verifies distribution quantile endpoints and the existing NaN and invalid-probability contracts. + /// + [TestMethod] + public void Test_Quantiles_ProbabilityEdges() + { + var normal = new Normal(); + Assert.AreEqual(double.NegativeInfinity, normal.InverseCDF(0d)); + Assert.AreEqual(double.PositiveInfinity, normal.InverseCDF(1d)); + Assert.IsTrue(double.IsNaN(normal.InverseCDF(double.NaN))); + Assert.IsTrue(double.IsNaN(Normal.StandardZ(double.NaN))); + + foreach (double probability in new[] { -1d, 2d, double.NegativeInfinity, double.PositiveInfinity }) + { + var distributionError = Assert.Throws(() => normal.InverseCDF(probability)); + var standardError = Assert.Throws(() => Normal.StandardZ(probability)); + Assert.AreEqual("probability", distributionError.ParamName); + Assert.AreEqual("probability", standardError.ParamName); + } + } + /// /// Testing CDF method. /// diff --git a/Test_Numerics/Utilities/Test_Tools.cs b/Test_Numerics/Utilities/Test_Tools.cs index cdf32751..c68896c1 100644 --- a/Test_Numerics/Utilities/Test_Tools.cs +++ b/Test_Numerics/Utilities/Test_Tools.cs @@ -22,6 +22,38 @@ namespace Utilities [TestClass] public class Test_Tools { + /// + /// Verifies finite classification includes signed zero and subnormal values but excludes NaN and infinities. + /// + [TestMethod] + public void Test_IsFinite_EdgeValues() + { + double negativeZero = BitConverter.Int64BitsToDouble(long.MinValue); + foreach (double value in new[] { 0d, negativeZero, double.Epsilon, -double.Epsilon, double.MinValue, double.MaxValue }) + Assert.IsTrue(Tools.IsFinite(value)); + + Assert.IsFalse(Tools.IsFinite(double.NaN)); + Assert.IsFalse(Tools.IsFinite(double.NegativeInfinity)); + Assert.IsFalse(Tools.IsFinite(double.PositiveInfinity)); + } + + /// + /// Verifies clamping preserves in-range values and signed zero while bounding infinities and propagating NaN. + /// + [TestMethod] + public void Test_Clamp_EdgeValues() + { + Assert.AreEqual(0d, Tools.Clamp(-1d, 0d, 1d)); + Assert.AreEqual(1d, Tools.Clamp(2d, 0d, 1d)); + Assert.AreEqual(0.25d, Tools.Clamp(0.25d, 0d, 1d)); + Assert.AreEqual(0d, Tools.Clamp(double.NegativeInfinity, 0d, 1d)); + Assert.AreEqual(1d, Tools.Clamp(double.PositiveInfinity, 0d, 1d)); + Assert.IsTrue(double.IsNaN(Tools.Clamp(double.NaN, 0d, 1d))); + + double negativeZero = BitConverter.Int64BitsToDouble(long.MinValue); + Assert.AreEqual(long.MinValue, BitConverter.DoubleToInt64Bits(Tools.Clamp(negativeZero, 0d, 1d))); + } + /// /// Test Sign function with varying inputs. /// @@ -558,6 +590,20 @@ public void Test_LogSumExp_AllNegativeInfinity() Assert.AreEqual(double.NegativeInfinity, Tools.LogSumExp(values)); } + /// + /// Verifies the two-value log-sum preserves finite terms beside negative infinity and existing NaN behavior. + /// + [TestMethod] + public void Test_LogSumExp_NonfiniteInputs() + { + Assert.AreEqual(5d, Tools.LogSumExp(5d, double.NegativeInfinity)); + Assert.AreEqual(5d, Tools.LogSumExp(double.NegativeInfinity, 5d)); + Assert.IsTrue(double.IsNaN(Tools.LogSumExp(double.NaN, 5d))); + Assert.IsTrue(double.IsNaN(Tools.LogSumExp(5d, double.NaN))); + Assert.IsTrue(double.IsNaN(Tools.LogSumExp(double.PositiveInfinity, 5d))); + Assert.IsTrue(double.IsNaN(Tools.LogSumExp(5d, double.PositiveInfinity))); + } + /// /// Testing integer sequence. /// From d80bfa8621c48a78cf4ebad7f01326841fac37aa Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Tue, 8 Sep 2026 14:22:44 -0600 Subject: [PATCH 202/222] Serialize test execution --- Test_Numerics/AssemblyAttributes.cs | 2 +- Test_Numerics/Test_Numerics.csproj | 1 + Test_Numerics/test.runsettings | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Test_Numerics/AssemblyAttributes.cs b/Test_Numerics/AssemblyAttributes.cs index be2dce49..3119b2d6 100644 --- a/Test_Numerics/AssemblyAttributes.cs +++ b/Test_Numerics/AssemblyAttributes.cs @@ -1,3 +1,3 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -[assembly: Parallelize(Scope = ExecutionScope.ClassLevel)] +[assembly: DoNotParallelize] diff --git a/Test_Numerics/Test_Numerics.csproj b/Test_Numerics/Test_Numerics.csproj index 723e0131..66a275fc 100644 --- a/Test_Numerics/Test_Numerics.csproj +++ b/Test_Numerics/Test_Numerics.csproj @@ -6,6 +6,7 @@ false true $(MSBuildProjectDirectory)\test.runsettings + false diff --git a/Test_Numerics/test.runsettings b/Test_Numerics/test.runsettings index b3e68c99..f75d4df6 100644 --- a/Test_Numerics/test.runsettings +++ b/Test_Numerics/test.runsettings @@ -1,7 +1,7 @@ - - 0 + + 1 From b8bf912c11f3dc0a770bd9e54d0ae40d2bccb319 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Tue, 8 Sep 2026 14:56:09 -0600 Subject: [PATCH 203/222] Repair distribution tails, moments, and uncertainty --- .../Base/DistributionEndpointTail.cs | 95 + .../Base/DistributionMomentIntegration.cs | 76 + .../Univariate/Base/DistributionNumerics.cs | 318 +++ .../Base/DistributionParameterBounds.cs | 47 + .../Base/DistributionTailTransform.cs | 38 + .../Base/DistributionUncertaintyNumerics.cs | 120 + .../Base/GammaDistributionNumerics.cs | 312 ++ .../Univariate/Base/MixtureLogWeights.cs | 30 + .../Base/UnivariateDistributionBase.cs | 78 +- .../Univariate/CompetingRisks.cs | 426 +-- .../Distributions/Univariate/Exponential.cs | 148 +- .../Univariate/GammaDistribution.cs | 268 +- .../Univariate/GeneralizedExtremeValue.cs | 408 +-- .../Univariate/GeneralizedLogistic.cs | 530 ++-- .../Univariate/GeneralizedNormal.cs | 384 ++- .../Univariate/GeneralizedPareto.cs | 290 +- Numerics/Distributions/Univariate/Gumbel.cs | 132 +- .../Univariate/KappaExpectedInformation.cs | 359 +++ .../Distributions/Univariate/KappaFour.cs | 74 +- .../Univariate/KappaFourBoundary.cs | 47 + Numerics/Distributions/Univariate/LnNormal.cs | 301 +- .../Distributions/Univariate/LogNormal.cs | 246 +- .../Univariate/LogPearsonTypeIII.cs | 622 ++-- Numerics/Distributions/Univariate/Logistic.cs | 115 +- Numerics/Distributions/Univariate/Mixture.cs | 495 ++-- Numerics/Distributions/Univariate/Normal.cs | 140 +- .../Univariate/PearsonTypeIII.cs | 464 ++- .../StandardErrorExtensions.cs | 24 + Numerics/Distributions/Univariate/Weibull.cs | 217 +- .../Univariate/DistributionOracle.cs | 31 + .../Univariate/Test_CompositeRobustness.cs | 208 ++ .../Test_DistributionLikelihoodRegression.cs | 49 + .../Univariate/Test_DistributionNumerics.cs | 118 + .../Test_ExtremePositiveRobustness.cs | 461 +++ .../Test_GeneralizedExtremeValue.cs | 14 +- .../Univariate/Test_GeneralizedLogistic.cs | 7 +- .../Univariate/Test_GeneralizedPareto.cs | 14 +- .../Univariate/Test_GeneralizedRobustness.cs | 460 +++ .../Univariate/Test_KappaFourRegression.cs | 8 +- .../Test_LnNormalParameterContract.cs | 35 + .../Univariate/Test_LogPearsonTypeIII.cs | 10 +- .../Distributions/Univariate/Test_Mixture.cs | 3 +- .../Test_NormalPearsonRobustness.cs | 459 +++ .../Distributions/Univariate/Test_Weibull.cs | 7 +- Test_Numerics/Test_Numerics.csproj | 4 + .../bestfit-robustness-evidence.md | 124 + docs/distributions/bestfit-robustness-plan.md | 75 + .../bestfit-robustness-validation.json | 94 + docs/distributions/oracles/extreme-positive.R | 296 ++ .../oracles/extreme-positive.csv | 2498 +++++++++++++++++ .../distributions/oracles/extreme-positive.md | 183 ++ .../generalized-adjacent-boundaries.csv | 181 ++ .../generalized-adjacent-boundaries.py | 37 + .../oracles/generalized-affine-overflow.R | 32 + .../oracles/generalized-affine-overflow.md | 38 + .../oracles/generalized-fisher.R | 384 +++ .../oracles/generalized-fisher.csv | 1334 +++++++++ .../oracles/generalized-fisher.md | 88 + .../oracles/generalized-scalar-variance.py | 81 + .../generate-gamma-temme-coefficients.py | 37 + docs/distributions/oracles/manifest.json | 25 + .../oracles/normal-pearson-evidence.txt | 25 + docs/distributions/oracles/normal-pearson.R | 100 + docs/distributions/oracles/normal-pearson.csv | 51 + 64 files changed, 11521 insertions(+), 2854 deletions(-) create mode 100644 Numerics/Distributions/Univariate/Base/DistributionEndpointTail.cs create mode 100644 Numerics/Distributions/Univariate/Base/DistributionMomentIntegration.cs create mode 100644 Numerics/Distributions/Univariate/Base/DistributionNumerics.cs create mode 100644 Numerics/Distributions/Univariate/Base/DistributionParameterBounds.cs create mode 100644 Numerics/Distributions/Univariate/Base/DistributionTailTransform.cs create mode 100644 Numerics/Distributions/Univariate/Base/DistributionUncertaintyNumerics.cs create mode 100644 Numerics/Distributions/Univariate/Base/GammaDistributionNumerics.cs create mode 100644 Numerics/Distributions/Univariate/Base/MixtureLogWeights.cs create mode 100644 Numerics/Distributions/Univariate/KappaExpectedInformation.cs create mode 100644 Numerics/Distributions/Univariate/Uncertainty Analysis/StandardErrorExtensions.cs create mode 100644 Test_Numerics/Distributions/Univariate/DistributionOracle.cs create mode 100644 Test_Numerics/Distributions/Univariate/Test_CompositeRobustness.cs create mode 100644 Test_Numerics/Distributions/Univariate/Test_DistributionLikelihoodRegression.cs create mode 100644 Test_Numerics/Distributions/Univariate/Test_DistributionNumerics.cs create mode 100644 Test_Numerics/Distributions/Univariate/Test_ExtremePositiveRobustness.cs create mode 100644 Test_Numerics/Distributions/Univariate/Test_GeneralizedRobustness.cs create mode 100644 Test_Numerics/Distributions/Univariate/Test_LnNormalParameterContract.cs create mode 100644 Test_Numerics/Distributions/Univariate/Test_NormalPearsonRobustness.cs create mode 100644 docs/distributions/bestfit-robustness-evidence.md create mode 100644 docs/distributions/bestfit-robustness-plan.md create mode 100644 docs/distributions/bestfit-robustness-validation.json create mode 100644 docs/distributions/oracles/extreme-positive.R create mode 100644 docs/distributions/oracles/extreme-positive.csv create mode 100644 docs/distributions/oracles/extreme-positive.md create mode 100644 docs/distributions/oracles/generalized-adjacent-boundaries.csv create mode 100644 docs/distributions/oracles/generalized-adjacent-boundaries.py create mode 100644 docs/distributions/oracles/generalized-affine-overflow.R create mode 100644 docs/distributions/oracles/generalized-affine-overflow.md create mode 100644 docs/distributions/oracles/generalized-fisher.R create mode 100644 docs/distributions/oracles/generalized-fisher.csv create mode 100644 docs/distributions/oracles/generalized-fisher.md create mode 100644 docs/distributions/oracles/generalized-scalar-variance.py create mode 100644 docs/distributions/oracles/generate-gamma-temme-coefficients.py create mode 100644 docs/distributions/oracles/manifest.json create mode 100644 docs/distributions/oracles/normal-pearson-evidence.txt create mode 100644 docs/distributions/oracles/normal-pearson.R create mode 100644 docs/distributions/oracles/normal-pearson.csv diff --git a/Numerics/Distributions/Univariate/Base/DistributionEndpointTail.cs b/Numerics/Distributions/Univariate/Base/DistributionEndpointTail.cs new file mode 100644 index 00000000..562776ad --- /dev/null +++ b/Numerics/Distributions/Univariate/Base/DistributionEndpointTail.cs @@ -0,0 +1,95 @@ +using System; +using Numerics.Mathematics.SpecialFunctions; + +namespace Numerics.Distributions +{ + /// One-sided endpoint expansions for independent products of zero tails and infinite densities. + internal static class DistributionEndpointTail + { + /// Returns tail ~ exp(logCoefficient)*distance^power*log(1/distance)^logPower. + /// These are finite lower CDF or upper survival endpoint limits. Infinite power denotes + /// faster-than-polynomial decay. No numerical endpoint offset or density floor is used. + internal static bool TryExpansion(UnivariateDistributionBase distribution, bool lower, + out double power, out double logPower, out double logCoefficient) + { + power = logPower = logCoefficient = 0; + switch (distribution) + { + case GammaDistribution gamma when lower: + power = gamma.Kappa; + logCoefficient = -Gamma.LogGamma(power + 1) - power * Math.Log(gamma.Theta); + return true; + case Weibull weibull when lower: + power = weibull.Kappa; + logCoefficient = -power * Math.Log(weibull.Lambda); + return true; + case Exponential exponential when lower: + power = 1; logCoefficient = -Math.Log(exponential.Alpha); + return true; + case Uniform uniform: + power = 1; logCoefficient = -Math.Log(uniform.Max - uniform.Min); + return true; + case GeneralizedPareto pareto: + power = lower ? 1 : 1 / pareto.Kappa; + logCoefficient = lower ? -Math.Log(pareto.Alpha) : power * (Math.Log(pareto.Kappa) - Math.Log(pareto.Alpha)); + return true; + case GeneralizedExtremeValue extreme: + power = lower ? double.PositiveInfinity : 1 / extreme.Kappa; + logCoefficient = lower ? 0 : power * (Math.Log(extreme.Kappa) - Math.Log(extreme.Alpha)); + return true; + case GeneralizedLogistic logistic: + power = 1 / Math.Abs(logistic.Kappa); + logCoefficient = power * (Math.Log(Math.Abs(logistic.Kappa)) - Math.Log(logistic.Alpha)); + return true; + case GeneralizedNormal _: + case LnNormal _: + case LogNormal _: + power = double.PositiveInfinity; + return true; + case PearsonTypeIII pearson: + power = pearson.Alpha; + logCoefficient = -Gamma.LogGamma(power + 1) - power * Math.Log(Math.Abs(pearson.Beta)); + return true; + case LogPearsonTypeIII pearson: + if (pearson.Gamma == 0) { power = double.PositiveInfinity; return true; } + if (lower && pearson.Gamma < 0) + { + // A reflected gamma survival becomes an algebraic-logarithmic lower tail after exponentiation. + power = 1 / (Math.Abs(pearson.Beta) * Math.Log(pearson.Base)); + logPower = pearson.Alpha - 1; + logCoefficient = -power * pearson.Xi * Math.Log(pearson.Base) + + logPower * Math.Log(power) - Gamma.LogGamma(pearson.Alpha); + } + else + { + power = pearson.Alpha; + double logEndpoint = pearson.Xi * Math.Log(pearson.Base); + logCoefficient = -Gamma.LogGamma(power + 1) + - power * (Math.Log(Math.Abs(pearson.Beta)) + Math.Log(Math.Log(pearson.Base)) + logEndpoint); + } + return true; + case KappaFour kappa: + if (!lower) + { + power = 1 / kappa.Kappa; + logCoefficient = power * (Math.Log(kappa.Kappa) - Math.Log(kappa.Alpha)); + } + else if (kappa.Hondo > 0) + { + power = 1 / kappa.Hondo; + logCoefficient = power * (kappa.Kappa * Math.Log(kappa.Hondo) - Math.Log(kappa.Alpha)); + } + else if (kappa.Hondo < 0) + { + power = 1 / (kappa.Kappa * kappa.Hondo); + logCoefficient = Math.Log(-kappa.Hondo) / kappa.Hondo + + power * (Math.Log(-kappa.Kappa) - Math.Log(kappa.Alpha)); + } + else power = double.PositiveInfinity; + return true; + default: + return false; + } + } + } +} diff --git a/Numerics/Distributions/Univariate/Base/DistributionMomentIntegration.cs b/Numerics/Distributions/Univariate/Base/DistributionMomentIntegration.cs new file mode 100644 index 00000000..cf79f247 --- /dev/null +++ b/Numerics/Distributions/Univariate/Base/DistributionMomentIntegration.cs @@ -0,0 +1,76 @@ +using System; +using Numerics.Mathematics; +using Numerics.Mathematics.Integration; + +namespace Numerics.Distributions +{ + /// Checked full-support central-moment integration for composite distributions. + internal static class DistributionMomentIntegration + { + /// Integrates a normalized log density about a local reference without raw-moment subtraction. + /// Maps each side of the reference through x=center±scale*t/(1-t), including infinite + /// endpoints. Integrates the mean offset first, then directly integrates centered powers. + /// No probability tails are truncated and no estimated mass is silently normalized. + internal static double[] Compute(Func logDensity, double minimum, double maximum, double center, double scale) + { + if (!DistributionNumerics.IsFinite(center) || !(scale > 0) || !DistributionNumerics.IsFinite(scale)) + throw new InvalidOperationException("A finite reference and positive local scale are required for composite moment integration."); + double lower = Limit(center - minimum, scale), upper = Limit(maximum - center, scale); + double logScale = Math.Log(scale); + double Moment(int order, double offset) + { + double Side(bool positive, double limit) + { + if (limit == 0) return 0; + double Function(double t) + { + double magnitude = t / (1 - t); + double coordinate = positive ? magnitude : -magnitude; + double x = center + scale * coordinate; + double log = logDensity(x); + if (double.IsNegativeInfinity(log)) return 0; + double centered = coordinate - offset; + if (order != 0) + { + if (centered == 0) return 0; + log += order * Math.Log(Math.Abs(centered)); + } + double value = Math.Exp(log + logScale - 2 * Tools.Log1p(-t)); + if (!DistributionNumerics.IsFinite(value)) + throw new InvalidOperationException("A composite moment is divergent or cannot be resolved numerically."); + return (order % 2 != 0 && centered < 0) ? -value : value; + } + var integrator = new AdaptiveGaussKronrod(Function, 0, limit) + { + RelativeTolerance = 1E-8, AbsoluteTolerance = 1E-10, + MaxFunctionEvaluations = 200000, ReportFailure = true + }; + integrator.Integrate(); + if (integrator.Status != IntegrationStatus.Success || !DistributionNumerics.IsFinite(integrator.Result) + || !DistributionNumerics.IsFinite(integrator.StandardError) + || integrator.StandardError > Math.Max(1E-10, Math.Abs(integrator.Result) * 1E-8)) + throw new InvalidOperationException("Composite moment integration did not meet its error tolerance."); + return integrator.Result; + } + return Side(false, lower) + Side(true, upper); + } + double mass = Moment(0, 0); + if (Math.Abs(mass - 1) > 5E-8) throw new InvalidOperationException("Composite moment integration did not recover unit probability mass."); + double offset = Moment(1, 0); + double mean = center + scale * offset; + double variance = Moment(2, offset); + if (!(variance > 0)) throw new InvalidOperationException("Composite moment integration did not produce a positive variance."); + double third = Moment(3, offset), fourth = Moment(4, offset); + return new[] { mean, scale * Math.Sqrt(variance), third / variance / Math.Sqrt(variance), fourth / variance / variance }; + } + + /// Maps a finite or infinite one-sided support width to [0,1]. + private static double Limit(double width, double scale) + { + if (width <= 0) return 0; + if (double.IsPositiveInfinity(width)) return 1; + double ratio = width / scale; + return double.IsPositiveInfinity(ratio) ? 1 : ratio / (1 + ratio); + } + } +} diff --git a/Numerics/Distributions/Univariate/Base/DistributionNumerics.cs b/Numerics/Distributions/Univariate/Base/DistributionNumerics.cs new file mode 100644 index 00000000..4e3a08a9 --- /dev/null +++ b/Numerics/Distributions/Univariate/Base/DistributionNumerics.cs @@ -0,0 +1,318 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; +using BigInteger = System.Numerics.BigInteger; + +namespace Numerics.Distributions +{ + /// Numerical primitives shared by the reviewed univariate distributions. + internal static partial class DistributionNumerics + { + /// A cache key including nested distribution settings omitted from flattened parameter vectors. + internal static string ConfigurationState(UnivariateDistributionBase distribution) + { + // Composite serialization flattens scalar parameters and need not support an empirical + // child. Cache identity therefore records configuration independently of that contract. + if (distribution is Mixture mixture) + return FormattableString.Invariant($"Mixture|{mixture.IsZeroInflated}|{mixture.ZeroWeight:R}|{mixture.XTransform}|{mixture.ProbabilityTransform}|") + + string.Join("|", mixture.Weights.Select(w => w.ToString("R", System.Globalization.CultureInfo.InvariantCulture))) + + "[" + string.Join("][", mixture.Distributions.Select(ConfigurationState)) + "]"; + if (distribution is CompetingRisks competing) + return FormattableString.Invariant($"Competing|{competing.MinimumOfRandomVariables}|{competing.Dependency}|{competing.PRNGSeed}|{competing.XTransform}|{competing.ProbabilityTransform}|") + + (competing.CorrelationMatrix is null ? "null" : competing.CorrelationMatrix.GetLength(0) + "x" + competing.CorrelationMatrix.GetLength(1) + + ":" + string.Join("|", competing.CorrelationMatrix.Cast().Select(v => v.ToString("R", System.Globalization.CultureInfo.InvariantCulture)))) + + "[" + string.Join("][", competing.Distributions.Select(ConfigurationState)) + "]"; + return distribution.ToXElement().ToString(SaveOptions.DisableFormatting); + } + + /// Forms an affine standardized value without overflowing a finite difference unnecessarily. + internal static double Standardize(double x, double location, double scale) + { + double difference = x - location; + return double.IsInfinity(difference) && IsFinite(x) && IsFinite(location) + ? x / scale - location / scale : difference / scale; + } + + /// Whether a value is finite on all supported target frameworks. + internal static bool IsFinite(double value) => !double.IsNaN(value) && !double.IsInfinity(value); + + /// Computes log(1-exp(a)) for a nonpositive log probability, including its limits. + internal static double Log1mExp(double a) + { + if (a > 0 || double.IsNaN(a)) return double.NaN; + return a < -0.69314718055994530942 ? Tools.Log1p(-Math.Exp(a)) : Math.Log(-Tools.Expm1(a)); + } + + /// Computes log(exp(a)-exp(b)), with equal arguments representing zero mass. + internal static double LogDifference(double a, double b) + { + if (double.IsNaN(a) || double.IsNaN(b) || b > a) return double.NaN; + if (a == b) return double.NegativeInfinity; + return a + Log1mExp(b - a); + } + + /// Adds two nonnegative quantities represented by their logarithms. + internal static double LogSum(double a, double b) + { + if (double.IsNaN(a) || double.IsNaN(b)) return double.NaN; + if (double.IsPositiveInfinity(a) || double.IsPositiveInfinity(b)) return double.PositiveInfinity; + if (double.IsNegativeInfinity(a)) return b; + if (double.IsNegativeInfinity(b)) return a; + double larger = Math.Max(a, b); + return larger + Tools.Log1p(Math.Exp(Math.Min(a, b) - larger)); + } + + /// The normal log CDF, preserving the logarithm after the tail itself underflows. + /// The far tail uses the convergent Laplace continued fraction for the Mills ratio. + internal static double NormalLogCDF(double z) + { + if (double.IsNaN(z)) return double.NaN; + if (z > 0) return Log1mExp(NormalLogCDF(-z)); + if (z >= -10) return Math.Log(Normal.StandardCDF(z)); + if (double.IsNegativeInfinity(z)) return double.NegativeInfinity; + double x = -z; + // Q(x)/phi(x) = 1/(x+1/(x+2/(x+3/(...)))). At x>=10, + // 64 backward levels are well beyond binary64 convergence. + double fraction = 0; + for (int i = 64; i >= 1; i--) fraction = i / (x + fraction); + return -(0.5 * x) * x - Tools.LogSqrt2PI - Math.Log(x + fraction); + } + + /// The normal log survival function, evaluated directly through reflection. + internal static double NormalLogSurvival(double z) => NormalLogCDF(-z); + + /// The exponential divided difference expm1(x)/x, including x=0. + internal static double Exprel(double x) => x == 0 ? 1 : Tools.Expm1(x) / x; + + /// The derivative of expm1(x)/x, with a convergent series at zero. + internal static double ExprelDerivative(double x) + { + if (Math.Abs(x) >= 0.1) return ((x - 1) * Math.Exp(x) + 1) / x / x; + double sum = 0.5, term = 0.5; + for (int n = 1; n < 20; n++) + { + term *= x * (n + 1.0) / n / (n + 2.0); + sum += term; + if (Math.Abs(term) <= Math.Abs(sum) * 1E-17) break; + } + return sum; + } + + /// Rejects nonfinite and endpoint probabilities for quantile uncertainty calculations. + internal static void ValidateProbability(double probability) + { + if (!(probability > 0 && probability < 1)) + throw new ArgumentOutOfRangeException(nameof(probability), "Quantile uncertainty requires a finite probability strictly between zero and one."); + } + + /// Requires a positive sample size for asymptotic uncertainty. + internal static void ValidateSampleSize(int sampleSize) + { + if (sampleSize <= 0) throw new ArgumentOutOfRangeException(nameof(sampleSize), "Sample size must be positive."); + } + + /// Checks sample size and finite interior probabilities for interval approximations requiring n-1. + internal static void ValidateConfidenceInputs(int sampleSize, IList quantiles, IList percentiles, int minimumSampleSize = 1) + { + if (sampleSize < minimumSampleSize) throw new ArgumentOutOfRangeException(nameof(sampleSize), "Insufficient observations for this confidence interval method."); + if (quantiles == null) throw new ArgumentNullException(nameof(quantiles)); + if (percentiles == null) throw new ArgumentNullException(nameof(percentiles)); + if (quantiles.Count == 0 || percentiles.Count == 0) throw new ArgumentOutOfRangeException(nameof(quantiles), "Probability lists must not be empty."); + foreach (double p in quantiles) ValidateProbability(p); + foreach (double p in percentiles) ValidateProbability(p); + } + + /// Checks that an initialization sample is finite, sufficiently long, and nonconstant. + internal static void ValidateSample(IList sample, int minimumCount = 2, bool positive = false) + { + if (sample == null) throw new ArgumentNullException(nameof(sample)); + if (sample.Count < minimumCount) throw new ArgumentOutOfRangeException(nameof(sample), "Insufficient observations to initialize the distribution."); + bool distinct = false; + for (int i = 0; i < sample.Count; i++) + { + if (!IsFinite(sample[i]) || (positive && sample[i] <= 0)) + throw new ArgumentOutOfRangeException(nameof(sample), positive ? "Observations must be finite and strictly positive." : "Observations must be finite."); + distinct |= sample[i] != sample[0]; + } + if (!distinct) throw new ArgumentOutOfRangeException(nameof(sample), "A constant sample cannot initialize a positive scale."); + } + + /// Assembles quantile gradients by observation row and obtains a determinant without artificial pivots. + internal static double[,] QuantileJacobian(IStandardError distribution, IList probabilities, out double determinant) + { + var matrix = QuantileGradientMatrix(distribution, probabilities); + double log = LogAbsDeterminant(matrix, out int sign); + determinant = sign == 0 ? 0 : sign * Math.Exp(log); + return matrix; + } + + /// Builds the square Jacobian with one quantile per row in the public parameter coordinates. + internal static double[,] QuantileGradientMatrix(IStandardError distribution, IList probabilities) + { + if (distribution == null) throw new ArgumentNullException(nameof(distribution)); + if (probabilities == null) throw new ArgumentNullException(nameof(probabilities)); + int count = probabilities.Count; + if (count == 0) throw new ArgumentOutOfRangeException(nameof(probabilities)); + if (distribution is IUnivariateDistribution univariate && count != univariate.NumberOfParameters) + throw new ArgumentOutOfRangeException(nameof(probabilities), "Provide one probability per public distribution parameter."); + var matrix = new double[count, count]; + for (int i = 0; i < count; i++) + { + ValidateProbability(probabilities[i]); + double[] gradient = distribution.QuantileGradient(probabilities[i]); + if (gradient.Length != count) throw new ArgumentOutOfRangeException(nameof(probabilities), "The quantile Jacobian must be square."); + for (int j = 0; j < count; j++) + { + if (!IsFinite(gradient[j])) throw new InvalidOperationException("The quantile Jacobian contains a nonfinite derivative."); + matrix[i, j] = gradient[j]; + } + } + return matrix; + } + + /// Log absolute determinant by row/column equilibration and partial pivoting. + /// Zero pivots retain exact singularity; no jitter or artificial tiny pivots are inserted. + internal static double LogAbsDeterminant(double[,] matrix, out int sign) + { + int n = matrix.GetLength(0); + if (matrix.GetLength(1) != n) throw new ArgumentException("The matrix must be square.", nameof(matrix)); + var a = (double[,])matrix.Clone(); + double log = 0; + sign = 1; + for (int i = 0; i < n; i++) + { + double scale = 0; + for (int j = 0; j < n; j++) scale = Math.Max(scale, Math.Abs(a[i, j])); + if (!IsFinite(scale)) throw new InvalidOperationException("The determinant requires finite matrix entries."); + if (scale == 0) { sign = 0; return double.NegativeInfinity; } + log += Math.Log(scale); + for (int j = 0; j < n; j++) + { + a[i, j] /= scale; + if (a[i, j] == 0 && matrix[i, j] != 0) return ExactDyadicLogDeterminant(matrix, out sign); + } + } + for (int j = 0; j < n; j++) + { + double scale = 0; + for (int i = 0; i < n; i++) scale = Math.Max(scale, Math.Abs(a[i, j])); + if (scale == 0) return ExactDyadicLogDeterminant(matrix, out sign); + log += Math.Log(scale); + for (int i = 0; i < n; i++) a[i, j] /= scale; + } + for (int j = 0; j < n; j++) + { + int pivot = j; + for (int i = j + 1; i < n; i++) if (Math.Abs(a[i, j]) > Math.Abs(a[pivot, j])) pivot = i; + // An exactly dependent row can leave a rounded residual after equilibration. + // Resolve a small pivot using the original binary64 values as exact dyadic integers. + if (Math.Abs(a[pivot, j]) < 1E-10) return ExactDyadicLogDeterminant(matrix, out sign); + if (pivot != j) + { + for (int k = j; k < n; k++) (a[j, k], a[pivot, k]) = (a[pivot, k], a[j, k]); + sign = -sign; + } + double diagonal = a[j, j]; + sign *= Math.Sign(diagonal); + log += Math.Log(Math.Abs(diagonal)); + for (int i = j + 1; i < n; i++) + { + double ratio = a[i, j] / diagonal; + for (int k = j + 1; k < n; k++) a[i, k] -= ratio * a[j, k]; + } + } + return log; + } + + /// Fraction-free integer elimination for a determinant whose floating-point pivot is unresolved. + /// Each finite binary64 row is scaled by an exact power of two to integers. + /// Bareiss elimination then distinguishes exact dependence from a merely small determinant. + /// The threshold selecting this path does not classify a matrix as singular. + private static double ExactDyadicLogDeterminant(double[,] matrix, out int sign) + { + int n = matrix.GetLength(0), binaryExponent = 0; + var integers = new BigInteger[n, n]; + for (int i = 0; i < n; i++) + { + int minimumExponent = int.MaxValue; + for (int j = 0; j < n; j++) + { + if (matrix[i, j] == 0) continue; + long bits = BitConverter.DoubleToInt64Bits(matrix[i, j]); + int exponent = (int)((bits >> 52) & 0x7ff); + minimumExponent = Math.Min(minimumExponent, exponent == 0 ? -1074 : exponent - 1075); + } + if (minimumExponent == int.MaxValue) { sign = 0; return double.NegativeInfinity; } + binaryExponent += minimumExponent; + for (int j = 0; j < n; j++) + { + long bits = BitConverter.DoubleToInt64Bits(matrix[i, j]); + int exponent = (int)((bits >> 52) & 0x7ff); + long mantissa = bits & 0xfffffffffffffL; + if (exponent != 0) mantissa |= 0x10000000000000L; + if (mantissa == 0) continue; + int power = exponent == 0 ? -1074 : exponent - 1075; + integers[i, j] = new BigInteger(bits < 0 ? -mantissa : mantissa) << (power - minimumExponent); + } + } + sign = 1; + BigInteger previous = BigInteger.One; + for (int k = 0; k < n - 1; k++) + { + int pivot = k; + while (pivot < n && integers[pivot, k].IsZero) pivot++; + if (pivot == n) { sign = 0; return double.NegativeInfinity; } + if (pivot != k) + { + for (int j = k; j < n; j++) (integers[k, j], integers[pivot, j]) = (integers[pivot, j], integers[k, j]); + sign = -sign; + } + BigInteger diagonal = integers[k, k]; + for (int i = k + 1; i < n; i++) + { + for (int j = k + 1; j < n; j++) + integers[i, j] = (diagonal * integers[i, j] - integers[i, k] * integers[k, j]) / previous; + integers[i, k] = BigInteger.Zero; + } + previous = diagonal; + } + BigInteger determinant = integers[n - 1, n - 1]; + sign *= determinant.Sign; + return sign == 0 ? double.NegativeInfinity : BigInteger.Log(BigInteger.Abs(determinant)) + binaryExponent * Math.Log(2); + } + + /// Whether a reviewed distribution has no probability atoms. + private static bool IsContinuous(UnivariateDistributionBase distribution) + { + if (distribution is CompetingRisks competing) return competing.Distributions.All(IsContinuous); + if (distribution is Mixture mixture) return !mixture.IsZeroInflated && mixture.Distributions.All(IsContinuous); + return distribution is Normal || distribution is Logistic || distribution is LnNormal || distribution is LogNormal + || distribution is PearsonTypeIII || distribution is LogPearsonTypeIII || distribution is Exponential + || distribution is GammaDistribution || distribution is Weibull || distribution is Gumbel + || distribution is GeneralizedExtremeValue || distribution is GeneralizedPareto + || distribution is GeneralizedNormal || distribution is GeneralizedLogistic || distribution is KappaFour + || distribution is Uniform; + } + + /// Resolves a positive continuous interval when both pairs of log tails round to identical values. + /// Eight-point Gauss-Legendre integration is used only after tail subtraction collapses. + /// The explicitly identified continuous families avoid treating an atom as a density contribution. + internal static double CollapsedContinuousLogInterval(UnivariateDistributionBase distribution, double lower, double upper) + { + if (!IsContinuous(distribution)) return double.NegativeInfinity; + lower = Math.Max(lower, distribution.Minimum); + upper = Math.Min(upper, distribution.Maximum); + double width = upper - lower; + if (!(width > 0) || !IsFinite(width)) return double.NegativeInfinity; + double[] nodes = { .019855071751231884, .10166676129318663, .23723379504183551, .4082826787521751, + .5917173212478249, .7627662049581645, .8983332387068134, .9801449282487681 }; + double[] weights = { .05061426814518813, .11119051722668724, .15685332293894365, .181341891689181, + .181341891689181, .15685332293894365, .11119051722668724, .05061426814518813 }; + double sum = double.NegativeInfinity; + for (int i = 0; i < nodes.Length; i++) + sum = LogSum(sum, Math.Log(weights[i]) + distribution.LogPDF(lower + width * nodes[i])); + return Math.Log(width) + sum; + } + } +} diff --git a/Numerics/Distributions/Univariate/Base/DistributionParameterBounds.cs b/Numerics/Distributions/Univariate/Base/DistributionParameterBounds.cs new file mode 100644 index 00000000..39f51199 --- /dev/null +++ b/Numerics/Distributions/Univariate/Base/DistributionParameterBounds.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; + +namespace Numerics.Distributions +{ + internal static partial class DistributionNumerics + { + /// Returns a finite magnitude used to evaluate the same initialization estimator in unit coordinates. + internal static double InitializationScale(IList sample) + { + double scale = 0; + for (int i = 0; i < sample.Count; i++) scale = Math.Max(scale, Math.Abs(sample[i])); + if (!(scale > 0) || !IsFinite(scale)) + throw new ArgumentOutOfRangeException(nameof(sample), "A finite nonzero sample magnitude is required for initialization."); + return scale; + } + + /// Constructs ordered finite positive bounds that contain a representable positive initial parameter. + internal static void PositiveParameterBounds(double initial, out double lower, out double upper) + { + if (!(initial > 0) || !IsFinite(initial)) + throw new ArgumentOutOfRangeException(nameof(initial), "The initial parameter must be finite and positive."); + lower = Math.Max(double.Epsilon, Math.Min(Tools.DoubleMachineEpsilon, initial / 10)); + double decade = Math.Pow(10, Math.Ceiling(Math.Log10(initial) + 1)); + upper = IsFinite(decade) ? Math.Max(initial, decade) : double.MaxValue; + if (!(lower < upper)) + throw new ArgumentOutOfRangeException(nameof(initial), "Finite ordered parameter bounds cannot be represented."); + } + + /// Constructs signed, scale-aware finite location bounds and a feasible initial location. + /// For a lower-endpoint family the upper bound is the actual sample minimum. + internal static void LocationParameterBounds(ref double initial, double scale, double dataMinimum, + double dataMaximum, bool upperAtMinimum, out double lower, out double upper) + { + if (!IsFinite(initial) || !(scale > 0) || !IsFinite(scale)) + throw new ArgumentOutOfRangeException(nameof(initial), "Initialization requires a finite location and positive scale."); + double magnitude = Math.Max(Math.Abs(initial), Math.Max(scale, Math.Max(Math.Abs(dataMinimum), Math.Abs(dataMaximum)))); + double radius = Math.Pow(10, Math.Ceiling(Math.Log10(magnitude) + 1)); + if (!IsFinite(radius)) radius = double.MaxValue; + lower = -radius; + upper = upperAtMinimum ? dataMinimum : radius; + if (!(lower < upper)) + throw new ArgumentOutOfRangeException(nameof(initial), "Finite ordered location bounds cannot be represented for this sample."); + if (initial < lower || initial > upper) initial = lower / 2 + upper / 2; + } + } +} diff --git a/Numerics/Distributions/Univariate/Base/DistributionTailTransform.cs b/Numerics/Distributions/Univariate/Base/DistributionTailTransform.cs new file mode 100644 index 00000000..6daa9601 --- /dev/null +++ b/Numerics/Distributions/Univariate/Base/DistributionTailTransform.cs @@ -0,0 +1,38 @@ +using System; + +namespace Numerics.Distributions +{ + internal static partial class DistributionNumerics + { + /// Evaluates the Hosking shape transform without losing a finite logarithm to affine or product overflow. + /// The observation in physical coordinates. + /// The finite location. + /// The finite positive scale. + /// The finite Hosking shape. + /// Minus log(1-shape*(x-location)/scale) divided by shape, with its continuous zero-shape limit. + /// The caller validates parameters and support. Ordinary finite products retain log1p + /// arithmetic. If standardization or multiplication overflows, the same support expression is + /// assembled from signed physical differences and logarithms. + internal static double HoskingShapeTransform(double x, double location, double scale, double shape) + { + double standardized = Standardize(x, location, scale); + if (shape == 0 || double.IsNaN(standardized)) return standardized; + double product = shape * standardized; + if (IsFinite(product)) return product == 0 ? standardized : -Tools.Log1p(-product) / shape; + if (!IsFinite(x) || !IsFinite(location)) return -Tools.Log1p(-product) / shape; + + double difference = x - location; + double logDifference; + if (IsFinite(difference)) logDifference = Math.Log(Math.Abs(difference)); + else + { + double magnitude = Math.Max(Math.Abs(x), Math.Abs(location)); + logDifference = Math.Log(magnitude) + Math.Log(Math.Abs(x / magnitude - location / magnitude)); + } + double logProduct = Math.Log(Math.Abs(shape)) + logDifference - Math.Log(scale); + bool negativeProduct = shape > 0 ? x < location : x > location; + double logSupport = negativeProduct ? LogSum(0, logProduct) : Log1mExp(logProduct); + return -logSupport / shape; + } + } +} diff --git a/Numerics/Distributions/Univariate/Base/DistributionUncertaintyNumerics.cs b/Numerics/Distributions/Univariate/Base/DistributionUncertaintyNumerics.cs new file mode 100644 index 00000000..11bff032 --- /dev/null +++ b/Numerics/Distributions/Univariate/Base/DistributionUncertaintyNumerics.cs @@ -0,0 +1,120 @@ +using System; + +namespace Numerics.Distributions +{ + internal static partial class DistributionNumerics + { + /// Contracts an unchanged covariance and quantile gradient without forming avoidable overflowing products. + /// The finite covariance in the supplied gradient coordinates. + /// The corresponding quantile gradient. + /// An optional finite positive common quantile scale. + /// The nonnegative scalar variance, including true floating-point underflow or positive overflow. + /// Scale or dimensions are invalid. + /// The covariance, gradient or contracted variance is unresolved. + /// Forms each signed covariance-gradient product in logarithms, normalizes by the + /// largest term, and uses compensated summation. No eigenvalue repair, clipping, + /// diagonal inflation or negative-variance floor is applied. + internal static double ScaledQuantileVariance(double[,] covariance, double[] gradient, double scale = 1) + { + if (!(scale > 0) || !IsFinite(scale)) throw new ArgumentOutOfRangeException(nameof(scale)); + if (covariance.GetLength(0) != gradient.Length || covariance.GetLength(1) != gradient.Length) + throw new ArgumentOutOfRangeException(nameof(covariance), "Covariance and gradient dimensions must agree."); + var gradientLogs = new double[gradient.Length]; + for (int i = 0; i < gradient.Length; i++) + { + if (double.IsNaN(gradient[i])) throw new InvalidOperationException("The quantile gradient is undefined."); + if (double.IsInfinity(gradient[i])) + throw new InvalidOperationException("An unrepresentable gradient requires finite physical coordinates before scalar variance contraction."); + gradientLogs[i] = Math.Log(Math.Abs(gradient[i])); + for (int j = 0; j < gradient.Length; j++) + { + if (!IsFinite(covariance[i, j])) throw new InvalidOperationException("The parameter covariance is outside the finite floating-point range."); + } + } + double largestLogTerm = double.NegativeInfinity; + for (int i = 0; i < gradient.Length; i++) + for (int j = 0; j < gradient.Length; j++) + { + if (covariance[i, j] == 0 || gradient[i] == 0 || gradient[j] == 0) continue; + largestLogTerm = Math.Max(largestLogTerm, Math.Log(Math.Abs(covariance[i, j])) + gradientLogs[i] + gradientLogs[j]); + } + if (double.IsNegativeInfinity(largestLogTerm)) return 0; + double quadratic = 0, correction = 0; + for (int i = 0; i < gradient.Length; i++) + for (int j = 0; j < gradient.Length; j++) + { + if (covariance[i, j] == 0 || gradient[i] == 0 || gradient[j] == 0) continue; + double logTerm = Math.Log(Math.Abs(covariance[i, j])) + gradientLogs[i] + gradientLogs[j]; + double term = Math.Sign(covariance[i, j]) * Math.Sign(gradient[i]) * Math.Sign(gradient[j]) + * Math.Exp(logTerm - largestLogTerm); + double next = quadratic + term; + correction += Math.Abs(quadratic) >= Math.Abs(term) ? (quadratic - next) + term : (term - next) + quadratic; + quadratic = next; + } + quadratic += correction; + if (!IsFinite(quadratic) || quadratic < 0) + throw new InvalidOperationException("The quantile variance could not be resolved as a nonnegative quadratic form."); + if (quadratic == 0) return 0; + return Math.Exp(2 * Math.Log(scale) + largestLogTerm + Math.Log(quadratic)); + } + + /// Returns shape squared times the positive Gamma Fisher residual trigamma(shape)-1/shape. + /// The finite positive Gamma shape. + /// The scaled Fisher residual without subtracting rounded reciprocal leading terms. + /// Shape is not finite and positive. + /// Uses the trigamma recurrence below shape 32 and the same Bernoulli expansion as + /// AccurateTrigamma after analytically removing 1/shape. Scaling before evaluation also + /// avoids squaring a large shape or forming an underflowed unscaled residual. + internal static double GammaScaledFisherResidual(double shape) + { + if (!(shape > 0) || !IsFinite(shape)) throw new ArgumentOutOfRangeException(nameof(shape)); + double shifted = shape, recurrence = 0; + while (shifted < 32) + { + double ratio = shape / shifted; + recurrence += ratio * ratio; + shifted++; + } + double r = 1 / shifted, s = r * r; + double residual = .5 + r * (1.0 / 6 + s * (-1.0 / 30 + s * (1.0 / 42 + + s * (-1.0 / 30 + s * (5.0 / 66 - s * 691.0 / 2730))))); + if (shifted == shape) return residual; + double scaleRatio = shape / shifted; + return recurrence - shape + shape * scaleRatio + scaleRatio * scaleRatio * residual; + } + + /// Forms scale times value times exprel(argument), retaining a finite product after unit-scale overflow. + internal static double ScaledExprelProduct(double scale, double value, double argument) + { + return ScaledExprelProductCore(scale, value, argument, false); + } + + /// Forms scale times value squared times exprel'(argument) without squaring a tiny value prematurely. + internal static double ScaledExprelDerivativeProduct(double scale, double value, double argument) + { + return ScaledExprelProductCore(scale, value, argument, true); + } + + /// Combines signed multipliers and divided-exponential logarithms when ordinary product arithmetic loses range. + private static double ScaledExprelProductCore(double scale, double value, double argument, bool derivative) + { + if (value == 0) return 0; + if (double.IsPositiveInfinity(argument)) return derivative ? double.PositiveInfinity : Math.Sign(value) * double.PositiveInfinity; + if (double.IsNegativeInfinity(argument)) return 0; + double divided = derivative ? ExprelDerivative(argument) : Exprel(argument); + double factor = derivative ? value * value : value; + double result = scale * (factor * divided); + if (IsFinite(result) && result != 0) return result; + double logDivided; + if (argument > 50) + logDivided = derivative ? argument + Math.Log(argument - 1) + Tools.Log1p(Math.Exp(-argument) / (argument - 1)) - 2 * Math.Log(argument) + : argument + Tools.Log1p(-Math.Exp(-argument)) - Math.Log(argument); + else if (argument < -50) + logDivided = derivative ? Tools.Log1p((argument - 1) * Math.Exp(argument)) - 2 * Math.Log(-argument) + : Math.Log(-Tools.Expm1(argument)) - Math.Log(-argument); + else logDivided = Math.Log(divided); + double logarithm = Math.Log(scale) + (derivative ? 2 : 1) * Math.Log(Math.Abs(value)) + logDivided; + return (derivative ? 1 : Math.Sign(value)) * Math.Exp(logarithm); + } + } +} diff --git a/Numerics/Distributions/Univariate/Base/GammaDistributionNumerics.cs b/Numerics/Distributions/Univariate/Base/GammaDistributionNumerics.cs new file mode 100644 index 00000000..9fdc0e98 --- /dev/null +++ b/Numerics/Distributions/Univariate/Base/GammaDistributionNumerics.cs @@ -0,0 +1,312 @@ +using System; +using Numerics.Mathematics.SpecialFunctions; + +namespace Numerics.Distributions +{ + internal static partial class DistributionNumerics + { + /// Trigamma with an exact recurrence and a sufficiently large asymptotic argument for covariance work. + internal static double AccurateTrigamma(double shape) + { + ValidateGammaShape(shape); + double sum = 0; + while (shape < 32) { double inverse = 1 / shape; sum += inverse * inverse; shape++; } + double r = 1 / shape, s = r * r; + return sum + r + .5 * s + r * s * (1.0 / 6 + s * (-1.0 / 30 + s * (1.0 / 42 + s * (-1.0 / 30 + s * (5.0 / 66 - s * 691.0 / 2730))))); + } + + /// Log regularized lower gamma integral P(a,x). + internal static double GammaLogCDF(double shape, double x) => GammaLogTail(shape, x, false, out _); + + /// Log regularized upper gamma integral Q(a,x). + internal static double GammaLogSurvival(double shape, double x) => GammaLogTail(shape, x, true, out _); + + /// Log unit-scale gamma density, including one-sided endpoint limits. + internal static double GammaLogDensity(double shape, double x) + { + ValidateGammaShape(shape); + if (double.IsNaN(x)) return double.NaN; + if (x < 0 || double.IsPositiveInfinity(x)) return double.NegativeInfinity; + if (x == 0) return shape == 1 ? 0 : shape < 1 ? double.PositiveInfinity : double.NegativeInfinity; + return shape == 1 ? -x : GammaLogKernel(shape, x) - Math.Log(x); + } + + /// Implicit shape derivative of the actual unit-scale gamma quantile at its value. + /// Differentiates the convergent lower series or upper continued fraction together + /// with the probability. The large-shape expansion is differentiated analytically. No + /// frequency-factor approximation or perturbation across the shape boundary is used. + internal static double GammaQuantileShapeDerivative(double shape, double unitQuantile) + { + ValidateGammaShape(shape); + if (unitQuantile == 0) return 0; + if (!(unitQuantile > 0) || !IsFinite(unitQuantile)) + throw new ArgumentOutOfRangeException(nameof(unitQuantile)); + bool upper = unitQuantile >= shape; + double log = GammaLogTail(shape, unitQuantile, upper, out double derivative); + if (derivative == 0) return 0; + return (upper ? Math.Sign(derivative) : -Math.Sign(derivative)) + * Math.Exp(Math.Log(Math.Abs(derivative)) + log - GammaLogDensity(shape, unitQuantile)); + } + + /// Inverts a gamma tail directly, retaining tiny upper and lower probabilities. + /// Uses a tail-aware bracketed Newton solve; a normal approximation supplies only + /// the initial point. Tiny quantiles are solved in logarithmic coordinates. + internal static double GammaInverseCDF(double shape, double probability, bool upperTail = false) + { + ValidateGammaShape(shape); + if (!(probability >= 0 && probability <= 1)) throw new ArgumentOutOfRangeException(nameof(probability)); + if (probability == 0) return upperTail ? double.PositiveInfinity : 0; + if (probability == 1) return upperTail ? 0 : double.PositiveInfinity; + if (probability > 0.5) { probability = 1 - probability; upperTail = !upperTail; } + double target = Math.Log(probability); + if (shape == 1) return upperTail ? -target : -Tools.Log1p(-probability); + double smallLog = (target + LogGammaOnePlus(shape)) / shape; + if (!upperTail && smallLog < -36) return Math.Exp(smallLog); + double root = Math.Sqrt(shape); + double z = Normal.StandardZ(probability) * (upperTail ? -1 : 1); + double w = 1 - 1 / (9 * shape) + z / (3 * root); + double guess = w > 0 ? shape * w * w * w : Math.Exp(smallLog); + if (!(guess > 0) || !IsFinite(guess)) guess = Math.Max(shape, 1); + double lower = 0, upper = Math.Max(Math.Max(shape, 1), guess); + bool Below(double value) + { + double log = GammaLogTail(shape, value, upperTail, out _); + return upperTail ? log > target : log < target; + } + while (Below(upper)) + { + lower = upper; + upper = upper < double.MaxValue / 2 ? upper * 2 : double.MaxValue; + if (lower == upper) return double.PositiveInfinity; + } + double x = guess > lower && guess < upper ? guess : lower + (upper - lower) / 2; + for (int iteration = 0; iteration < 100; iteration++) + { + double log = GammaLogTail(shape, x, upperTail, out _); + double residual = log - target; + if (Math.Abs(residual) <= 8E-15 * Math.Max(1, Math.Abs(target))) return x; + if (upperTail ? residual > 0 : residual < 0) lower = x; else upper = x; + double slope = Math.Exp(GammaLogDensity(shape, x) - log) * (upperTail ? -1 : 1); + double next = x - residual / slope; + if (!(next > lower && next < upper) || !IsFinite(next)) next = lower + (upper - lower) / 2; + if (next == x || next == lower || next == upper) return next; + x = next; + } + throw new InvalidOperationException("The gamma quantile solve did not converge."); + } + + /// Requires a positive finite gamma shape. + private static void ValidateGammaShape(double shape) + { + if (!(shape > 0) || !IsFinite(shape)) throw new ArgumentOutOfRangeException(nameof(shape), "Gamma shape must be positive and finite."); + } + + /// Evaluates a gamma log tail and its fixed-observation shape derivative. + private static double GammaLogTail(double a, double x, bool upper, out double derivative) + { + ValidateGammaShape(a); + derivative = 0; + if (double.IsNaN(x)) { derivative = double.NaN; return double.NaN; } + if (x <= 0) return upper ? 0 : double.NegativeInfinity; + if (double.IsPositiveInfinity(x)) return upper ? double.NegativeInfinity : 0; + double delta = (x - a) / a; + if (a >= 10000 && Math.Abs(delta) < 0.1) return GammaTemme(a, delta, upper, out derivative); + + if (a < 1 && x <= 1 && (upper || a * Math.Log(x) - LogGammaOnePlus(a) > -0.6931471805599453)) + { + double logQ = GammaSmallUpper(a, x, out double dlogQ); + if (upper) { derivative = dlogQ; return logQ; } + double logP = Log1mExp(logQ); + derivative = -dlogQ * Math.Exp(logQ - logP); + return logP; + } + + if (x < a + 1) + { + double sum = 1, term = 1, dsum = 0, dterm = 0; + for (int n = 1; n <= 100000; n++) + { + double ratio = x / (a + n); + dterm = dterm * ratio - term * ratio / (a + n); + term *= ratio; + sum += term; + dsum += dterm; + if (term <= sum * 2E-16 && Math.Abs(dterm) <= Math.Max(1, Math.Abs(dsum)) * 2E-16) + { + double log = a < 16 ? a * Math.Log(x) - x - LogGammaOnePlus(a) + Math.Log(sum) + : GammaLogKernel(a, x) - Math.Log(a) + Math.Log(sum); + double dlog = a < 16 ? Math.Log(x) - Gamma.Digamma(a + 1) + dsum / sum + : GammaKernelShapeDerivative(a, x) - 1 / a + dsum / sum; + if (!upper) { derivative = dlog; return log; } + double complement = Log1mExp(log); + derivative = -dlog * Math.Exp(log - complement); + return complement; + } + } + throw new InvalidOperationException("The lower gamma series did not converge."); + } + else + { + // Modified Lentz recurrence for DLMF 8.9.2, carrying its derivative. + const double tiny = 1E-300; + double b = x + 1 - a, c = 1 / tiny, dc = 0; + double d = 1 / b, dd = d * d, h = d, dh = dd; + for (int n = 1; n <= 100000; n++) + { + double an = n * (a - n); + b += 2; + double denom = an * d + b; + double ddenom = n * d + an * dd - 1; + double nextC = b + an / c; + double nextDc = -1 + n / c - an * dc / c / c; + if (Math.Abs(denom) < tiny) denom = denom < 0 ? -tiny : tiny; + if (Math.Abs(nextC) < tiny) nextC = nextC < 0 ? -tiny : tiny; + d = 1 / denom; + dd = -ddenom * d * d; + c = nextC; dc = nextDc; + double factor = d * c, dfactor = dd * c + d * dc; + dh = dh * factor + h * dfactor; + h *= factor; + if (Math.Abs(factor - 1) <= 4E-16 && Math.Abs(dfactor) <= 4E-16) + { + double log = GammaLogKernel(a, x) + Math.Log(h); + double dlog = GammaKernelShapeDerivative(a, x) + dh / h; + if (upper) { derivative = dlog; return log; } + double complement = Log1mExp(log); + derivative = -dlog * Math.Exp(log - complement); + return complement; + } + } + throw new InvalidOperationException("The upper gamma continued fraction did not converge."); + } + } + + /// Direct Q series at small shape/argument, avoiding a near-one lower-tail subtraction. + private static double GammaSmallUpper(double a, double x, out double derivative) + { + double sum = 0, dsum = 0, power = 1; + for (int n = 1; n <= 1000; n++) + { + power *= -x / n; + double term = power / (a + n); + sum += term; + dsum -= term / (a + n); + if (Math.Abs(term) < Math.Abs(sum) * 2E-16) break; + } + double logx = Math.Log(x), u = a * logx - LogGammaOnePlus(a); + double leading = Math.Exp(u), factor = a * leading; + double q = -Tools.Expm1(u) - factor * sum; + double du = logx - Gamma.Digamma(a + 1); + derivative = (-leading * du - factor * (du * sum + dsum) - leading * sum) / q; + return Math.Log(q); + } + + /// Log(x^a exp(-x)/Gamma(a)) without subtracting large near-equal terms. + private static double GammaLogKernel(double a, double x) + { + if (a < 16) return a * Math.Log(x) - x - Gamma.LogGamma(a); + double delta = (x - a) / a; + double deviance = Math.Abs(delta) < 0.25 ? a * Log1pMinusX(delta) + : a * (Math.Log(x) - Math.Log(a)) - (x - a); + return deviance + 0.5 * Math.Log(a) - Tools.LogSqrt2PI - StirlingRemainder(a); + } + + /// Fixed-x derivative of the log kernel, retaining its small residual at large shape. + private static double GammaKernelShapeDerivative(double a, double x) + { + if (a < 16) return Math.Log(x) - Gamma.Digamma(a); + double inverse = 1 / a, square = inverse * inverse; + double residual = 0.5 * inverse + square * (1.0 / 12 - square * (1.0 / 120 - square * (1.0 / 252 - square / 240))); + double delta = (x - a) / a; + return (Math.Abs(delta) < 0.25 ? Tools.Log1p(delta) : Math.Log(x) - Math.Log(a)) + residual; + } + + /// Stirling log-gamma remainder for arguments at least sixteen. + private static double StirlingRemainder(double a) + { + double r = 1 / a, r2 = r * r; + return r * (1.0 / 12 + r2 * (-1.0 / 360 + r2 * (1.0 / 1260 + r2 * (-1.0 / 1680 + r2 * (1.0 / 1188 - r2 * 691.0 / 360360))))); + } + + /// Cancellation-free log(1+x)-x. + internal static double Log1pMinusX(double x) + { + if (Math.Abs(x) >= 0.25) return Tools.Log1p(x) - x; + double power = -x * x, sum = power / 2; + for (int n = 3; n < 100; n++) + { + power *= -x; + double term = power / n; + sum += term; + if (Math.Abs(term) <= Math.Abs(sum) * 1E-17) break; + } + return sum; + } + + /// Log Gamma(1+a) with the zeta Taylor series at small a. + internal static double LogGammaOnePlus(double a) + { + if (a > 0.5) return Gamma.LogGamma(a + 1); + double result = -0.57721566490153286061 * a, power = a; + for (int n = 2; n < 60; n++) + { + power *= -a; + double term = -power * ZetaInteger(n) / n; + result += term; + if (Math.Abs(term) <= Math.Abs(result) * 1E-17) break; + } + return result; + } + + /// Integer zeta constants for the log Gamma(1+a) series. + internal static double ZetaInteger(int n) + { + double[] values = { 1.6449340668482264365, 1.2020569031595942854, 1.0823232337111381915, + 1.0369277551433699263, 1.0173430619844491397, 1.0083492773819228268, + 1.0040773561979443394, 1.0020083928260822144, 1.0009945751278180853, + 1.0004941886041194646, 1.0002460865533080483, 1.0001227133475784891, + 1.0000612481350587048, 1.0000305882363070205, 1.0000152822594086519 }; + if (n <= 16) return values[n - 2]; + double sum = 1; + for (int k = 2; k <= 32; k++) sum += Math.Pow(k, -n); + return sum; + } + + // Exact rational coefficients generated by docs/distributions/oracles/generate-gamma-temme-coefficients.py. + // DLMF 8.12.9-11. Four inverse-shape terms suffice in the a>=10000, |x/a-1|<.1 region. + private static readonly double[][] TemmeCoefficients = + { + new double[] { -.3333333333333333333,.0833333333333333333,-.01481481481481481481,.001157407407407407407,.0003527336860670194,-.0001787551440329218,3.919263178522438E-5,-2.185448510679992E-6,-1.85406221071516E-6,8.296711340953087E-7,-1.766595273682608E-7,6.707853543401498E-9,1.026180978424031E-8,-4.382036018453353E-9,9.14769958223679E-10,-2.551419399494625E-11,-5.830772132550426E-11,2.436194802066742E-11 }, + new double[] { -.001851851851851851852,-.003472222222222222222,.002645502645502645503,-.0009902263374485596,.000205761316872428,-4.018775720164609E-7,-1.809855033448998E-5,7.64916091608111E-6,-1.612090089456345E-6,4.647127802807434E-9,1.378633446915721E-7,-5.752545603517705E-8,1.195162859977815E-8,-1.754324171974765E-11,-1.009154371060041E-9,4.162792991842583E-10 }, + new double[] { .004133597883597883598,-.00268132716049382716,.0007716049382716049,2.009387860082305E-6,-.0001073665322636516,5.292344882912013E-5,-1.276063518861873E-5,3.423578734096138E-8,1.372195730906293E-6,-6.298992138380055E-7,1.428061420606424E-7,-2.047709842199087E-10,-1.409252991086752E-8,6.228974084922022E-9 }, + new double[] { .0006494341563786008,.0002294720936213992,-.0004691894943952557,.0002677206320628389,-7.561801671883977E-5,-2.396505113867297E-7,1.10826541153473E-5,-5.674952826991597E-6,1.423090073243588E-6,-2.786108029152814E-11,-1.695840409193028E-7,8.099464905388083E-8 } + }; + + /// Uniform gamma expansion with analytical fixed-x shape differentiation. + private static double GammaTemme(double a, double delta, bool upper, out double derivative) + { + double eta = delta == 0 ? 0 : Math.Sign(delta) * Math.Sqrt(-2 * Log1pMinusX(delta)); + double root = Math.Sqrt(a), z = eta * root; + double etaDerivative = delta == 0 ? -1 / a : -delta / eta / a; + double zDerivative = eta / (2 * root) + root * etaDerivative; + double series = 0, seriesEta = 0, seriesA = 0, inversePower = 1; + for (int k = 0; k < TemmeCoefficients.Length; k++) + { + var coefficients = TemmeCoefficients[k]; + double value = coefficients[coefficients.Length - 1], dvalue = 0; + for (int n = coefficients.Length - 2; n >= 0; n--) { dvalue = dvalue * eta + value; value = value * eta + coefficients[n]; } + series += inversePower * value; + seriesEta += inversePower * dvalue; + seriesA -= k * inversePower / a * value; + inversePower /= a; + } + double normal = upper ? NormalLogSurvival(z) : NormalLogCDF(z); + double logPhi = -(0.5 * z) * z - Tools.LogSqrt2PI; + double signedCorrection = (upper ? 1 : -1) * series * Math.Exp(logPhi - 0.5 * Math.Log(a) - normal); + double log = normal + Tools.Log1p(signedCorrection); + double correctionDerivative = (seriesEta * etaDerivative + seriesA - (z * zDerivative + 0.5 / a) * series) / root; + derivative = Math.Exp(logPhi - log) * (upper ? -zDerivative + correctionDerivative : zDerivative - correctionDerivative); + return log; + } + } +} diff --git a/Numerics/Distributions/Univariate/Base/MixtureLogWeights.cs b/Numerics/Distributions/Univariate/Base/MixtureLogWeights.cs new file mode 100644 index 00000000..c0bd5681 --- /dev/null +++ b/Numerics/Distributions/Univariate/Base/MixtureLogWeights.cs @@ -0,0 +1,30 @@ +using System; + +namespace Numerics.Distributions +{ + /// Normalizes component weights for a single logarithmic mixture observation. + internal static class MixtureLogWeights + { + /// Returns the row log probability and corresponding responsibilities. + /// Nonfinite or impossible rows are returned as nonfinite log probabilities so the + /// caller can retain its observation-specific error message and aggregate likelihood convention. + internal static double Normalize(double[] logDensities, double[] weights, double[] responsibilities) + { + double maximum = double.NegativeInfinity; + for (int i = 0; i < weights.Length; i++) + if (weights[i] > 0) maximum = Math.Max(maximum, logDensities[i]); + if (!DistributionNumerics.IsFinite(maximum)) return maximum; + double weightedMaximum = double.NegativeInfinity; + for (int i = 0; i < weights.Length; i++) + { + responsibilities[i] = weights[i] > 0 ? (logDensities[i] - maximum) + Math.Log(weights[i]) : double.NegativeInfinity; + weightedMaximum = Math.Max(weightedMaximum, responsibilities[i]); + } + double sum = 0; + for (int i = 0; i < weights.Length; i++) sum += Math.Exp(responsibilities[i] - weightedMaximum); + double centeredLogRow = weightedMaximum + Math.Log(sum); + for (int i = 0; i < weights.Length; i++) responsibilities[i] = Math.Exp(responsibilities[i] - centeredLogRow); + return maximum + centeredLogRow; + } + } +} diff --git a/Numerics/Distributions/Univariate/Base/UnivariateDistributionBase.cs b/Numerics/Distributions/Univariate/Base/UnivariateDistributionBase.cs index 144d9fdc..76fcabe4 100644 --- a/Numerics/Distributions/Univariate/Base/UnivariateDistributionBase.cs +++ b/Numerics/Distributions/Univariate/Base/UnivariateDistributionBase.cs @@ -171,8 +171,12 @@ public double LogLikelihood(double value) /// /// The threshold. /// The number of data points below the threshold. + /// The censored log likelihood; zero when the category is empty. + /// The count is negative. public double LogLikelihood_LeftCensored(double threshold, long numberBelow) { + if (numberBelow < 0) throw new ArgumentOutOfRangeException(nameof(numberBelow)); + if (numberBelow == 0) return 0; return numberBelow * LogCDF(threshold); } @@ -181,8 +185,12 @@ public double LogLikelihood_LeftCensored(double threshold, long numberBelow) /// /// The threshold. /// The number of data points above the threshold. + /// The censored log likelihood; zero when the category is empty. + /// The count is negative. public double LogLikelihood_RightCensored(double threshold, long numberAbove) { + if (numberAbove < 0) throw new ArgumentOutOfRangeException(nameof(numberAbove)); + if (numberAbove == 0) return 0; return numberAbove * LogCCDF(threshold); } @@ -191,10 +199,22 @@ public double LogLikelihood_RightCensored(double threshold, long numberAbove) /// /// The lower limit of the interval. /// The upper limit of the interval. + /// The logarithm of the probability of (lowerLimit, upperLimit]. + /// The limits are reversed or NaN. + /// Uses lower or upper logarithmic tails to avoid subtracting rounded probabilities. + /// The open lower and closed upper endpoint convention also applies to atoms. public double LogLikelihood_Intervals(double lowerLimit, double upperLimit) { - double interval = CDF(upperLimit) - CDF(lowerLimit); - return Math.Log(interval); + if (double.IsNaN(lowerLimit) || double.IsNaN(upperLimit) || lowerLimit > upperLimit) + throw new ArgumentOutOfRangeException(nameof(upperLimit), "Interval limits must be ordered and not NaN."); + if (lowerLimit == upperLimit) return double.NegativeInfinity; + if (this is Mixture mixture) return mixture.LogIntervalProbability(lowerLimit, upperLimit); + double lower = LogCDF(lowerLimit), upper = LogCDF(upperLimit); + double result = lower < -0.6931471805599453 + ? DistributionNumerics.LogDifference(upper, lower) + : DistributionNumerics.LogDifference(LogCCDF(lowerLimit), LogCCDF(upperLimit)); + return double.IsNegativeInfinity(result) + ? DistributionNumerics.CollapsedContinuousLogInterval(this, lowerLimit, upperLimit) : result; } /// @@ -302,6 +322,8 @@ public double[] InverseCDF(IList probabilities) /// met. A moment whose integration fails is returned as rather than /// throwing. /// + /// This inherited approximation is independent of analytical moment properties overridden + /// by individual distributions; use those properties when an analytical result is available. /// public virtual double[] CentralMoments(double tolerance = 1E-8) { @@ -353,8 +375,8 @@ public virtual double[] CentralMoments(double tolerance = 1E-8) /// the discrete expectation Σᵢ xᵢᵏ · ΔFᵢ. It is therefore a bin-probability (midpoint) expectation /// against the distribution, not a trapezoidal rule applied to the integrand x·f(x). The /// representative point xᵢ is the bin midpoint for interior bins, the upper bound for the first bin - /// and the lower bound for the last. The standard deviation is recovered from the raw second moment - /// as √(E[X²] − E[X]²), and skewness and kurtosis are accumulated as already-standardized powers. + /// and the lower bound for the last. Moments are accumulated in local coordinates about the + /// resulting mean to avoid cancellation between large raw first and second moments. /// /// /// The first and last bins carry the whole of their tails — ΔF₀ is CDF(upper bound of bin 0), taken @@ -362,6 +384,9 @@ public virtual double[] CentralMoments(double tolerance = 1E-8) /// endpoints the total probability sums to one and the effective range is not truncated. Cost /// and accuracy are both fixed by and there is no convergence check. /// + /// This legacy approximation does not dispatch to analytical moment properties. Its + /// fixed tail representatives can be inaccurate for heavy tails. Prefer the distribution's + /// moment properties when analytical values are available. /// public virtual double[] CentralMoments(int steps = 300) { @@ -371,41 +396,30 @@ public virtual double[] CentralMoments(int steps = 300) var bins = Stratify.XValues(new StratificationOptions(a, b, steps)); var dFx = new double[steps]; - double u1, u2, u3, u4; - double sumU1 = 0; - double sumU2 = 0; - double sumU3 = 0; - double sumU4 = 0; - - // First compute the mean and standard deviation + var coordinates = new double[steps]; + double reference = a / 2 + b / 2; + double scale = Math.Max(Math.Abs(a - reference), Math.Abs(b - reference)); + // Preserve the existing bins and probability masses, including endpoint tail representatives. dFx[0] = CDF(bins[0].UpperBound); - sumU1 += bins[0].UpperBound * dFx[0]; - sumU2 += Math.Pow(bins[0].UpperBound, 2d) * dFx[0]; + coordinates[0] = DistributionNumerics.Standardize(bins[0].UpperBound, reference, scale); for (int i = 1; i < steps - 1; i++) { dFx[i] = CDF(bins[i].UpperBound) - CDF(bins[i].LowerBound); - sumU1 += bins[i].Midpoint * dFx[i]; - sumU2 += Math.Pow(bins[i].Midpoint, 2d) * dFx[i]; + coordinates[i] = DistributionNumerics.Standardize(bins[i].Midpoint, reference, scale); } dFx[steps - 1] = 1 - CDF(bins.Last().LowerBound); - sumU1 += bins.Last().LowerBound * dFx[steps - 1]; - sumU2 += Math.Pow(bins.Last().LowerBound, 2d) * dFx[steps - 1]; - u1 = sumU1; - u2 = Math.Sqrt(sumU2 - Math.Pow(u1, 2d)); - - // Then compute skewness and kurtosis - sumU3 += Math.Pow((bins[0].UpperBound - u1) / u2, 3d) * dFx[0]; - sumU4 += Math.Pow((bins[0].UpperBound - u1) / u2, 4d) * dFx[0]; - for (int i = 1; i < steps - 1; i++) + coordinates[steps - 1] = DistributionNumerics.Standardize(bins.Last().LowerBound, reference, scale); + double offset = 0; + for (int i = 0; i < steps; i++) offset += coordinates[i] * dFx[i]; + double second = 0, third = 0, fourth = 0; + for (int i = 0; i < steps; i++) { - sumU3 += Math.Pow((bins[i].Midpoint - u1) / u2, 3d) * dFx[i]; - sumU4 += Math.Pow((bins[i].Midpoint - u1) / u2, 4d) * dFx[i]; + double centered = coordinates[i] - offset, square = centered * centered; + second += square * dFx[i]; + third += square * centered * dFx[i]; + fourth += square * square * dFx[i]; } - sumU3 += Math.Pow((bins.Last().LowerBound - u1) / u2, 3d) * dFx[steps - 1]; - sumU4 += Math.Pow((bins.Last().LowerBound - u1) / u2, 4d) * dFx[steps - 1]; - u3 = sumU3; - u4 = sumU4; - return [u1, u2, u3, u4]; + return [reference + scale * offset, scale * Math.Sqrt(second), third / second / Math.Sqrt(second), fourth / second / second]; } /// @@ -901,4 +915,4 @@ public int CompareTo(UnivariateDistributionBase? other) } } -} \ No newline at end of file +} diff --git a/Numerics/Distributions/Univariate/CompetingRisks.cs b/Numerics/Distributions/Univariate/CompetingRisks.cs index a28b9f7e..e4d20543 100644 --- a/Numerics/Distributions/Univariate/CompetingRisks.cs +++ b/Numerics/Distributions/Univariate/CompetingRisks.cs @@ -1,4 +1,4 @@ -using Numerics.Data; +using Numerics.Data; using Numerics.Data.Statistics; using Numerics.Mathematics; using Numerics.Mathematics.LinearAlgebra; @@ -61,9 +61,18 @@ public CompetingRisks(IUnivariateDistribution[] distributions) private MultivariateNormal _mvn = null!; private int _prngSeed = MultivariateNormal.DefaultMVNUNISeed; - // Soft finite floor used in tail arithmetic before returning the final log-density. - private const double _logZero = -745.0; - private const double _minDensity = 1E-300; + private string? _cachedConfiguration; + + /// Invalidates derived caches when mutable components or configuration change. + private void RefreshCachedConfiguration() + { + string configuration = DistributionNumerics.ConfigurationState(this); + if (configuration == _cachedConfiguration) return; + _cachedConfiguration = configuration; + _momentsComputed = false; + _empiricalCDFCreated = false; + _mvnCreated = false; + } /// /// Returns the array of univariate probability distributions. @@ -238,7 +247,8 @@ public override string[] GetParameterPropertyNames /// private void ComputeMoments() { - var mom = CentralMoments(1000); + double center = InverseCDF(.5), scale = InverseCDF(.75) - InverseCDF(.25); + var mom = DistributionMomentIntegration.Compute(LogPDF, Minimum, Maximum, center, scale); u1 = mom[0]; u2 = mom[1]; u3 = mom[2]; @@ -251,6 +261,7 @@ public override double Mean { get { + RefreshCachedConfiguration(); if (!_momentsComputed) ComputeMoments(); return u1; } @@ -278,6 +289,7 @@ public override double StandardDeviation { get { + RefreshCachedConfiguration(); if (!_momentsComputed) ComputeMoments(); return u2; } @@ -288,6 +300,7 @@ public override double Skewness { get { + RefreshCachedConfiguration(); if (!_momentsComputed) ComputeMoments(); return u3; } @@ -298,6 +311,7 @@ public override double Kurtosis { get { + RefreshCachedConfiguration(); if (!_momentsComputed) ComputeMoments(); return u4; } @@ -306,13 +320,13 @@ public override double Kurtosis /// public override double Minimum { - get { return Distributions.Min(p => p.Minimum); } + get { return MinimumOfRandomVariables ? Distributions.Min(p => p.Minimum) : Distributions.Max(p => p.Minimum); } } /// public override double Maximum { - get { return Distributions.Max(p => p.Maximum); } + get { return MinimumOfRandomVariables ? Distributions.Min(p => p.Maximum) : Distributions.Max(p => p.Maximum); } } /// @@ -375,7 +389,7 @@ public void SetParameters(UnivariateDistributionBase[] distributions) { if (distributions == null) throw new ArgumentNullException(nameof(Distributions)); _distributions = distributions; - _parametersValid = ValidateParameters(Array.Empty(), false) is null; + _parametersValid = ValidateParameters(GetParameters, false) is null; _momentsComputed = false; _empiricalCDFCreated = false; _mvnCreated = false; @@ -393,7 +407,7 @@ public void SetParameters(IUnivariateDistribution[] distributions) { _distributions[i] = (UnivariateDistributionBase)distributions[i]; } - _parametersValid = ValidateParameters(Array.Empty(), false) is null; + _parametersValid = ValidateParameters(GetParameters, false) is null; _momentsComputed = false; _empiricalCDFCreated = false; _mvnCreated = false; @@ -434,27 +448,30 @@ public override void SetParameters(IList parameters) /// public override ArgumentOutOfRangeException? ValidateParameters(IList parameters, bool throwException) { - if (Distributions.Count == 0) - { - var exception = new ArgumentOutOfRangeException(nameof(Distributions), "There must be at least 1 distribution."); - if (throwException) throw exception; - return exception; - } - for (int i = 0; i < Distributions.Count; i++) + ArgumentOutOfRangeException? error = null; + if (_distributions == null || _distributions.Length == 0 || _distributions.Any(d => d is null)) + error = new ArgumentOutOfRangeException(nameof(Distributions), "There must be at least one non-null distribution."); + else if (parameters == null || parameters.Count != _distributions.Sum(d => d.GetParameters.Length)) + error = new ArgumentOutOfRangeException(nameof(parameters), "The flattened parameter count must match the component distributions."); + else { - if (Distributions[i].ParametersValid == false) + int offset = 0; + foreach (var distribution in Distributions) { - if (throwException) - throw new ArgumentOutOfRangeException(nameof(Distributions), "One of the distributions have invalid parameters."); - return new ArgumentOutOfRangeException(nameof(Distributions), "One of the distributions have invalid parameters."); + var candidate = new double[distribution.GetParameters.Length]; + for (int j = 0; j < candidate.Length; j++) candidate[j] = parameters[offset++]; + error = distribution.ValidateParameters(candidate, false); + if (error != null) break; } } - return null; + if (throwException && error != null) throw error; + return error; } /// public Tuple GetParameterConstraints(IList sample) { + DistributionNumerics.ValidateSample(sample); var initialVals = new double[NumberOfParameters]; var lowerVals = new double[NumberOfParameters]; var upperVals = new double[NumberOfParameters]; @@ -501,283 +518,130 @@ double logLH(double[] x) } /// - public override double PDF(double x) - { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(GetParameters, true); - - if (Distributions.Count == 1) - { - return Distributions[0].PDF(x); - } - - double f = double.NaN; + public override double PDF(double x) => Math.Exp(LogPDF(x)); - // Only compute the exact PDF for independent random variables - if (Dependency == Probability.DependencyType.Independent) - { - if (MinimumOfRandomVariables) - { - f = PDFMinimumIndependent(x); - } - else - { - f = PDFMaximumIndependent(x); - } - } - else - { - // Compute the PDF using numerical differentiation - f = NumericalDerivative.Derivative(CDF, x); - } - - // Return minimum density instead of zero to prevent log-likelihood issues - return f < _minDensity ? _minDensity : f; - } - - /// - /// Computes PDF for minimum of independent random variables. - /// f(x) = h(x) * S(x) where h(x) = sum of hazard rates, S(x) = product of survival functions - /// - private double PDFMinimumIndependent(double x) + /// + public override double LogPDF(double x) { - double sumHazard = 0.0; - double productSurvival = 1.0; - + ValidateEvaluation(); + if (double.IsNaN(x)) return double.NaN; + if (x < Minimum || x > Maximum || double.IsInfinity(x)) return double.NegativeInfinity; + if (Distributions.Count == 1) return Distributions[0].LogPDF(x); + if (Dependency != Probability.DependencyType.Independent) + return Math.Log(DependentDensity(x)); + + // Sum f_i times the other factors, without dividing by possibly zero tails. + // Computing each excluded product also avoids infinity-minus-infinity in the log sum. + double total = double.NegativeInfinity; for (int i = 0; i < Distributions.Count; i++) { - double ccdf = Distributions[i].CCDF(x); - double pdf = Distributions[i].PDF(x); - - productSurvival *= ccdf; - - // Safe hazard calculation - if (ccdf > _minDensity) - { - sumHazard += pdf / ccdf; - } - else if (pdf > _minDensity) + double product = 0; + for (int j = 0; j < Distributions.Count; j++) + if (j != i) product += MinimumOfRandomVariables ? Distributions[j].LogCCDF(x) : Distributions[j].LogCDF(x); + double logDensity = Distributions[i].LogPDF(x); + if (double.IsNegativeInfinity(product)) { - // CCDF ≈ 0 but PDF > 0: we're at the boundary - // The hazard is very large, but productSurvival will be ≈ 0 - // so the contribution is negligible - sumHazard += pdf / _minDensity; // Cap the hazard + if (double.IsPositiveInfinity(logDensity)) return IndependentEndpointLogDensity(x); + continue; } + double term = logDensity + product; + total = DistributionNumerics.LogSum(total, term); } - - return sumHazard * productSurvival; + return total; } - /// - /// Computes PDF for maximum of independent random variables. - /// f(x) = sum_i [f_i(x) * prod_{j≠i} F_j(x)] - /// = [sum_i (f_i/F_i)] * [prod_j F_j] - /// - private double PDFMaximumIndependent(double x) + /// Combines endpoint tail exponents before evaluating the one-sided density limit. + /// For a product tail c*t^a*log(1/t)^b, its density tends to zero for a>1, + /// infinity for a<1, and c times the logarithmic limit for a=1. + private double IndependentEndpointLogDensity(double x) { - double sumRatio = 0.0; - double productCdf = 1.0; - - for (int i = 0; i < Distributions.Count; i++) - { - double cdf = Distributions[i].CDF(x); - double pdf = Distributions[i].PDF(x); - - productCdf *= cdf; - - // Safe ratio calculation - if (cdf > _minDensity) - { - sumRatio += pdf / cdf; - } - else if (pdf > _minDensity) - { - // CDF ≈ 0 but PDF > 0: we're at the left boundary - // The ratio is very large, but productCdf will be ≈ 0 - // so the contribution is negligible - sumRatio += pdf / _minDensity; // Cap the ratio - } - } - - return sumRatio * productCdf; + double power = 0, logPower = 0, logCoefficient = 0; + bool lower = !MinimumOfRandomVariables; + foreach (var distribution in Distributions) + { + double logTail = lower ? distribution.LogCDF(x) : distribution.LogCCDF(x); + if (!double.IsNegativeInfinity(logTail)) { logCoefficient += logTail; continue; } + if (!DistributionEndpointTail.TryExpansion(distribution, lower, out double componentPower, + out double componentLogPower, out double componentCoefficient)) + throw new InvalidOperationException("The independent endpoint density limit is unavailable for this component family."); + power += componentPower; + logPower += componentLogPower; + logCoefficient += componentCoefficient; + } + if (power > 1 || (power == 1 && logPower < 0)) return double.NegativeInfinity; + if (power < 1 || logPower > 0) return double.PositiveInfinity; + return logCoefficient; } - /// - public override double LogPDF(double x) + /// Checks current component validity, including mutations through public component references. + private void ValidateEvaluation() { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(GetParameters, true); - - if (Distributions.Count == 1) - { - return Distributions[0].LogPDF(x); - } - - // Only compute the exact LogPDF for independent random variables - if (Dependency == Probability.DependencyType.Independent) - { - if (MinimumOfRandomVariables) - { - return LogPDFMinimumIndependent(x); - } - else - { - return LogPDFMaximumIndependent(x); - } - } - else - { - // For dependent cases, fall back to numerical differentiation - // but use a more stable approach - double pdf = NumericalDerivative.Derivative(CDF, x); - return pdf > _minDensity ? Math.Log(pdf) : double.NegativeInfinity; - } - + if (!_parametersValid || Distributions.Any(d => !d.ParametersValid)) ValidateParameters(GetParameters, true); } - /// - /// Computes log-PDF for minimum of independent random variables. - /// Uses the formula: log(f(x)) = log(sum of hazards) + sum of log(survival functions) - /// - /// For minimum: f(x) = h(x) * S(x) where: - /// h(x) = sum_i h_i(x) = sum_i [f_i(x) / S_i(x)] - /// S(x) = prod_i S_i(x) - /// - /// In log space: log(f) = log(h(x)) + sum_i log(S_i(x)) - /// - private double LogPDFMinimumIndependent(double x) + /// Numerically differentiates the existing dependent CDF within its mathematical support. + /// The dependence model and its probability-combination rule are unchanged. A centered + /// local step is used in the interior; endpoints use a one-sided step. Negative or unresolved + /// density is reported rather than replaced by a positive likelihood floor. + private double DependentDensity(double x) { - int n = Distributions.Count; - var logSurvival = new double[n]; - var logHazard = new double[n]; - - double sumLogSurvival = 0.0; - bool allSurvivalZero = true; + double scale = double.PositiveInfinity; + foreach (var distribution in Distributions) + { + double width = distribution.InverseCDF(.75) - distribution.InverseCDF(.25); + if (width > 0 && DistributionNumerics.IsFinite(width)) scale = Math.Min(scale, width); + } + if (!DistributionNumerics.IsFinite(scale)) scale = Math.Max(1, Math.Abs(x)); + double step = Math.Pow(Tools.DoubleMachineEpsilon, 1.0 / 3) * scale; + double left = Math.Max(Minimum, x - step), right = Math.Min(Maximum, x + step); + if (!(right > left)) throw new InvalidOperationException("The dependent density cannot be resolved at this floating-point scale."); + double density = (CDF(right) - CDF(left)) / (right - left); + if (!DistributionNumerics.IsFinite(density) || density < 0) + throw new InvalidOperationException("Numerical differentiation of the dependent CDF did not produce a nonnegative finite density."); + return density; + } - for (int i = 0; i < n; i++) + /// + public override double LogCDF(double x) + { + ValidateEvaluation(); + if (Dependency != Probability.DependencyType.Independent) return Math.Log(CDF(x)); + if (!MinimumOfRandomVariables) return Distributions.Sum(d => d.LogCDF(x)); + double union = double.NegativeInfinity, precedingSurvival = 0; + foreach (var distribution in Distributions) { - double ccdf = Distributions[i].CCDF(x); - double pdf = Distributions[i].PDF(x); - - if (ccdf > _minDensity) - { - logSurvival[i] = Math.Log(ccdf); - allSurvivalZero = false; - } - else - { - // Survival is essentially zero - we're far in the right tail - logSurvival[i] = _logZero; - } - - sumLogSurvival += logSurvival[i]; - - // Compute log-hazard: log(f_i / S_i) = log(f_i) - log(S_i) - if (pdf > _minDensity && ccdf > _minDensity) - { - logHazard[i] = Math.Log(pdf) - Math.Log(ccdf); - } - else if (pdf <= _minDensity) - { - logHazard[i] = _logZero; - } - else - { - // pdf > 0 but ccdf ≈ 0: hazard is very large - // This happens in the far right tail - logHazard[i] = Math.Log(pdf) - _logZero; // Will be very large - } + union = DistributionNumerics.LogSum(union, precedingSurvival + distribution.LogCDF(x)); + precedingSurvival += distribution.LogCCDF(x); } - - // If all survival functions are zero, density is zero - if (allSurvivalZero) - return _logZero; - - // Compute log of sum of hazards using log-sum-exp trick - double logSumHazard = Tools.LogSumExp(logHazard); - - // Final result: log(f) = log(sum h_i) + sum log(S_i) - double logPdf = logSumHazard + sumLogSurvival; - - return double.IsNaN(logPdf) || double.IsInfinity(logPdf) ? double.NegativeInfinity : logPdf; + return union; } - /// - /// Computes log-PDF for maximum of independent random variables. - /// Uses the formula: f(x) = sum_i [f_i(x) * prod_{j≠i} F_j(x)] - /// - /// In log space, we use the identity: - /// f(x) = [sum_i (f_i/F_i)] * [prod_j F_j] - /// - /// So: log(f) = log(sum_i f_i/F_i) + sum_j log(F_j) - /// - private double LogPDFMaximumIndependent(double x) + /// + public override double LogCCDF(double x) { - int n = Distributions.Count; - var logCdf = new double[n]; - var logRatio = new double[n]; // log(f_i / F_i) - - double sumLogCdf = 0.0; - bool allCdfZero = true; - - for (int i = 0; i < n; i++) + ValidateEvaluation(); + if (Dependency != Probability.DependencyType.Independent) return DistributionNumerics.Log1mExp(Math.Log(CDF(x))); + if (MinimumOfRandomVariables) return Distributions.Sum(d => d.LogCCDF(x)); + double union = double.NegativeInfinity, precedingCDF = 0; + foreach (var distribution in Distributions) { - double cdf = Distributions[i].CDF(x); - double pdf = Distributions[i].PDF(x); - - if (cdf > _minDensity) - { - logCdf[i] = Math.Log(cdf); - allCdfZero = false; - } - else - { - // CDF is essentially zero - we're far in the left tail - logCdf[i] = _logZero; - } - - sumLogCdf += logCdf[i]; - - // Compute log(f_i / F_i) = log(f_i) - log(F_i) - if (pdf > _minDensity && cdf > _minDensity) - { - logRatio[i] = Math.Log(pdf) - Math.Log(cdf); - } - else if (pdf <= _minDensity) - { - logRatio[i] = _logZero; - } - else - { - // pdf > 0 but cdf ≈ 0: ratio is very large - // This can happen but contributes negligibly when multiplied by near-zero CDF product - logRatio[i] = Math.Log(pdf) - _logZero; - } + union = DistributionNumerics.LogSum(union, precedingCDF + distribution.LogCCDF(x)); + precedingCDF += distribution.LogCDF(x); } - - // If all CDFs are zero, density is zero - if (allCdfZero) - return _logZero; - - // Compute log of sum of ratios using log-sum-exp trick - double logSumRatio = Tools.LogSumExp(logRatio); - - // Final result: log(f) = log(sum f_i/F_i) + sum log(F_i) - double logPdf = logSumRatio + sumLogCdf; - - return double.IsNaN(logPdf) || double.IsInfinity(logPdf) ? double.NegativeInfinity : logPdf; + return union; } + /// + public override double CCDF(double x) => Math.Exp(LogCCDF(x)); /// public override double CDF(double x) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(GetParameters, true); - + ValidateEvaluation(); + if (Dependency == Probability.DependencyType.Independent) return Math.Exp(LogCDF(x)); + RefreshCachedConfiguration(); + if (x < Minimum) return 0; + if (x > Maximum) return 1; if (Distributions.Count == 1) { return Distributions[0].CDF(x); @@ -827,13 +691,15 @@ public override double CDF(double x) public override double InverseCDF(double probability) { // Validate probability - if (probability < 0.0d || probability > 1.0d) + if (!(probability >= 0.0d && probability <= 1.0d)) throw new ArgumentOutOfRangeException("probability", "Probability must be between 0 and 1."); + ValidateEvaluation(); + RefreshCachedConfiguration(); if (probability == 0.0d) return Minimum; if (probability == 1.0d) return Maximum; // Validate parameters if (_parametersValid == false) - ValidateParameters([0], true); + ValidateParameters(GetParameters, true); // If there is only one distribution, return its inverse CDF if (Distributions.Count() == 1) @@ -854,8 +720,22 @@ public override double InverseCDF(double probability) double maxX = xVals.Max(); try { - Brent.Bracket((y) => { return probability - CDF(y); }, ref minX, ref maxX, out var f1, out var f2); - x = Brent.Solve((y) => { return probability - CDF(y); }, minX, maxX, 1E-6, 100, true); + double reference = minX / 2 + maxX / 2; + double scale = maxX / 2 - minX / 2; + if (!(scale > 0) || !DistributionNumerics.IsFinite(scale)) + scale = Distributions.Select(d => d.InverseCDF(.75) - d.InverseCDF(.25)) + .Where(width => width > 0 && DistributionNumerics.IsFinite(width)).DefaultIfEmpty(1).Min(); + double Argument(double t) + { + double value = reference + scale * t; + if (double.IsInfinity(value) && DistributionNumerics.IsFinite(t)) value = scale * (reference / scale + t); + return Math.Max(Minimum, Math.Min(Maximum, value)); + } + double Residual(double t) => probability <= .5 ? LogCDF(Argument(t)) - Math.Log(probability) + : LogCCDF(Argument(t)) - Tools.Log1p(-probability); + double lower = -1, upper = 1; + Brent.Bracket(Residual, ref lower, ref upper, out _, out _); + x = Argument(Brent.Solve(Residual, lower, upper, 1E-6 / Math.Max(1, scale), 100, true)); } catch (Exception) { @@ -876,6 +756,7 @@ public override double InverseCDF(double probability) /// Optional. The stratification bins to integrate over. Default is 200 bins. public List CumulativeIncidenceFunctions(List? bins = null) { + RefreshCachedConfiguration(); // Get stratification bins if (bins == null) { @@ -1148,6 +1029,7 @@ private void CreateMultivariateNormal() /// public void CreateEmpiricalCDF() { + RefreshCachedConfiguration(); // Get min & max double minP = 1E-16; double maxP = 1 - 1E-16; diff --git a/Numerics/Distributions/Univariate/Exponential.cs b/Numerics/Distributions/Univariate/Exponential.cs index 8a86d85a..aef50c56 100644 --- a/Numerics/Distributions/Univariate/Exponential.cs +++ b/Numerics/Distributions/Univariate/Exponential.cs @@ -196,6 +196,7 @@ public override double[] MaximumOfParameters /// public void Estimate(IList sample, ParameterEstimationMethod estimationMethod) { + DistributionNumerics.ValidateSample(sample, 4); if (estimationMethod == ParameterEstimationMethod.MethodOfMoments) { SetParameters(ParametersFromMoments(Statistics.ProductMoments(sample))); @@ -241,6 +242,8 @@ public void SetParameters(double location, double scale) /// public override void SetParameters(IList parameters) { + if (parameters == null || parameters.Count != NumberOfParameters) + throw new ArgumentOutOfRangeException(nameof(parameters), "Exactly two parameters are required."); SetParameters(parameters[0], parameters[1]); } @@ -251,6 +254,12 @@ public override void SetParameters(IList parameters) /// Determines whether to throw an exception or not. public override ArgumentOutOfRangeException? ValidateParameters(IList parameters, bool throwException) { + if (parameters == null || parameters.Count != NumberOfParameters) + { + var exception = new ArgumentOutOfRangeException(nameof(parameters), "Exactly two parameters are required."); + if (throwException) throw exception; + return exception; + } if (double.IsNaN(parameters[0]) || double.IsInfinity(parameters[0])) { if (throwException) @@ -278,6 +287,7 @@ public double[] ParametersFromMoments(IList moments) /// public double[] MomentsFromParameters(IList parameters) { + ValidateParameters(parameters, true); var dist = new Exponential(); dist.SetParameters(parameters); var m1 = dist.Mean; @@ -300,6 +310,7 @@ public double[] ParametersFromLinearMoments(IList moments) /// public double[] LinearMomentsFromParameters(IList parameters) { + ValidateParameters(parameters, true); double xi = parameters[0]; double alpha = parameters[1]; double L1 = xi + alpha; @@ -310,36 +321,28 @@ public double[] LinearMomentsFromParameters(IList parameters) } /// + /// Requires at least four finite, nonconstant observations. The existing initialization + /// estimator is evaluated in unit coordinates before finite location and positive scale bounds are formed. + /// The sample or a representable feasible initialization is invalid. public Tuple GetParameterConstraints(IList sample) { + DistributionNumerics.ValidateSample(sample, 4); var initialVals = new double[NumberOfParameters]; var lowerVals = new double[NumberOfParameters]; var upperVals = new double[NumberOfParameters]; - - // Get initial values - var moments = Statistics.ProductMoments(sample); + double normalization = DistributionNumerics.InitializationScale(sample); + var normalized = new double[sample.Count]; + for (int i = 0; i < sample.Count; i++) normalized[i] = sample[i] / normalization; + // The existing bias-corrected start is evaluated in unit coordinates. + var moments = Statistics.ProductMoments(normalized); double minData = Statistics.Minimum(sample); - initialVals[0] = (sample.Count * minData - moments[0]) / (sample.Count - 1); - initialVals[1] = sample.Count * (moments[0] - minData) / (sample.Count - 1); - - // Get bounds of location - if (initialVals[0] == 0d) initialVals[0] = Tools.DoubleMachineEpsilon; - lowerVals[0] = initialVals[0] - Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])))); - upperVals[0] = minData; - - // Get bounds of scale - lowerVals[1] = Tools.DoubleMachineEpsilon; - upperVals[1] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[1]) + 1d)); - - // Correct initial values if necessary - if (initialVals[0] <= lowerVals[0] || initialVals[0] >= upperVals[0]) - { - initialVals[0] = Statistics.Mean([lowerVals[0], upperVals[0]]); - } - if (initialVals[1] <= lowerVals[1] || initialVals[1] >= upperVals[1]) - { - initialVals[1] = Statistics.Mean([lowerVals[1], upperVals[1]]); - } + double unitMinimum = minData / normalization; + double n = sample.Count; + initialVals[0] = (unitMinimum - (moments[0] - unitMinimum) / (n - 1)) * normalization; + initialVals[1] = (n / (n - 1)) * (moments[0] - unitMinimum) * normalization; + DistributionNumerics.LocationParameterBounds(ref initialVals[0], initialVals[1], minData, + Statistics.Maximum(sample), true, out lowerVals[0], out upperVals[0]); + DistributionNumerics.PositiveParameterBounds(initialVals[1], out lowerVals[1], out upperVals[1]); return new Tuple(initialVals, lowerVals, upperVals); } @@ -368,11 +371,7 @@ double logLH(double[] x) /// public override double PDF(double X) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters([Xi, Alpha], true); - if (X < Minimum || X > Maximum) return 0.0d; - return 1d / Alpha * Math.Exp(-((X - Xi) / Alpha)); + return Math.Exp(LogPDF(X)); } /// @@ -386,7 +385,7 @@ public override double LogPDF(double X) if (_parametersValid == false) ValidateParameters([Xi, Alpha], true); if (X < Minimum || X > Maximum) return double.NegativeInfinity; - double lf = -Math.Log(Alpha) - (X - Xi) / Alpha; + double lf = -Math.Log(Alpha) - DistributionNumerics.Standardize(X, Xi, Alpha); return double.IsNaN(lf) ? double.NegativeInfinity : lf; } @@ -398,14 +397,33 @@ public override double CDF(double X) ValidateParameters([Xi, Alpha], true); if (X <= Minimum) return 0d; if (X >= Maximum) return 1d; - return 1d - Math.Exp(-((X - Xi) / Alpha)); + return -Tools.Expm1(-DistributionNumerics.Standardize(X, Xi, Alpha)); + } + + /// + /// Computes the logarithm directly without subtracting a near-unit exponential. + public override double LogCDF(double X) + { + if (!_parametersValid) ValidateParameters([Xi, Alpha], true); + if (X <= Xi) return double.NegativeInfinity; + return DistributionNumerics.Log1mExp(-DistributionNumerics.Standardize(X, Xi, Alpha)); + } + + /// + public override double CCDF(double X) => Math.Exp(LogCCDF(X)); + + /// + public override double LogCCDF(double X) + { + if (!_parametersValid) ValidateParameters([Xi, Alpha], true); + return X <= Xi ? 0 : -DistributionNumerics.Standardize(X, Xi, Alpha); } /// public override double InverseCDF(double probability) { // Validate probability - if (probability < 0.0d || probability > 1.0d) + if (!(probability >= 0.0d && probability <= 1.0d)) throw new ArgumentOutOfRangeException("probability", "Probability must be between 0 and 1."); if (probability == 0.0d) return Minimum; @@ -414,7 +432,10 @@ public override double InverseCDF(double probability) // Validate parameters if (_parametersValid == false) ValidateParameters([Xi, Alpha], true); - return Xi - Alpha * Math.Log(1d - probability); + double unitQuantile = -Tools.Log1p(-probability); + double displacement = Alpha * unitQuantile; + return double.IsInfinity(displacement) && DistributionNumerics.IsFinite(unitQuantile) + ? Alpha * (Xi / Alpha + unitQuantile) : Xi + displacement; } /// @@ -424,8 +445,13 @@ public override UnivariateDistributionBase Clone() } /// + /// The actual MLE uses the sample minimum and mean minus minimum. Its covariance is + /// diagonal with alpha squared times [1/n squared, (n-1)/n squared]. The MoM covariance is unchanged. + /// Parameters are invalid or sample size is not positive. + /// The requested estimation method is unsupported. public double[,] ParameterCovariance(int sampleSize, ParameterEstimationMethod estimationMethod) { + DistributionNumerics.ValidateSampleSize(sampleSize); if (estimationMethod != ParameterEstimationMethod.MethodOfMoments && estimationMethod != ParameterEstimationMethod.MaximumLikelihood) { @@ -436,48 +462,50 @@ public override UnivariateDistributionBase Clone() ValidateParameters([Xi, _alpha], true); // Compute covariance - double a = Alpha; + double n = sampleSize; + double a = Alpha / Math.Sqrt(n); var covar = new double[2, 2]; if (estimationMethod == ParameterEstimationMethod.MethodOfMoments) { - covar[0, 0] = a * a / sampleSize; // location - covar[1, 1] = 2d * a * a / sampleSize; // scale - covar[0, 1] = -(a * a) / sampleSize; // location & scale + covar[0, 0] = a * a; // location + covar[1, 1] = 2d * (a * a); // scale + covar[0, 1] = -(a * a); // location & scale covar[1, 0] = covar[0, 1]; } else if (estimationMethod == ParameterEstimationMethod.MaximumLikelihood) { - covar[0, 0] = a * a / (sampleSize * (sampleSize - 1)); // location - covar[1, 1] = a * a / (sampleSize - 1); // scale - covar[0, 1] = -(a * a) / (sampleSize * (sampleSize - 1)); // location & scale - covar[1, 0] = covar[0, 1]; + // Actual MLE: location=min(sample), scale=mean(sample)-min(sample). + double locationScale = Alpha / n; + covar[0, 0] = locationScale * locationScale; + covar[1, 1] = (a * a) * ((n - 1) / n); } return covar; } /// + /// Uses the same covariance quadratic form in normalized coordinates; probability must be finite and strictly between zero and one. public double QuantileVariance(double probability, int sampleSize, ParameterEstimationMethod estimationMethod) { - var covar = ParameterCovariance(sampleSize, estimationMethod); - var grad = QuantileGradient(probability); - double varA = covar[0, 0]; - double varB = covar[1, 1]; - double covAB = covar[1, 0]; - double dQx1 = grad[0]; - double dQx2 = grad[1]; - return Math.Pow(dQx1, 2d) * varA + Math.Pow(dQx2, 2d) * varB + 2d * dQx1 * dQx2 * covAB; + DistributionNumerics.ValidateProbability(probability); + if (!_parametersValid) ValidateParameters([Xi, Alpha], true); + var unit = new Exponential(0, 1); + return DistributionNumerics.ScaledQuantileVariance(unit.ParameterCovariance(sampleSize, estimationMethod), + unit.QuantileGradient(probability), Alpha); } /// + /// Returns the derivative of the actual inverse CDF in location and scale coordinates. + /// Parameters are invalid or probability is not finite and strictly interior. public double[] QuantileGradient(double probability) { + DistributionNumerics.ValidateProbability(probability); // Validate parameters if (_parametersValid == false) ValidateParameters([Xi, _alpha], true); var gradient = new double[] { 1.0d, // location - -Math.Log(1d - probability) // scale + -Tools.Log1p(-probability) // scale }; return gradient; } @@ -485,25 +513,7 @@ public double[] QuantileGradient(double probability) /// public double[,] QuantileJacobian(IList probabilities, out double determinant) { - if (probabilities.Count != NumberOfParameters) - { - throw new ArgumentOutOfRangeException(nameof(probabilities), "The number of probabilities must be the same length as the number of distribution parameters."); - } - // Get gradients - var dQp1 = QuantileGradient(probabilities[0]); - var dQp2 = QuantileGradient(probabilities[1]); - // Compute determinant - // |a b| - // |c d| - // |A| = ad − bc - double a = dQp1[0]; - double b = dQp1[1]; - double c = dQp2[0]; - double d = dQp2[1]; - determinant = a * d - b * c; - // Return Jacobian - var jacobian = new double[,] { { a, b }, { c, d } }; - return jacobian; + return DistributionNumerics.QuantileJacobian(this, probabilities, out determinant); } /// diff --git a/Numerics/Distributions/Univariate/GammaDistribution.cs b/Numerics/Distributions/Univariate/GammaDistribution.cs index 7f835ea3..38c14871 100644 --- a/Numerics/Distributions/Univariate/GammaDistribution.cs +++ b/Numerics/Distributions/Univariate/GammaDistribution.cs @@ -249,7 +249,7 @@ public override double Mode /// public override double StandardDeviation { - get { return Math.Sqrt(Kappa * Math.Pow(Theta, 2d)); } + get { return Theta * Math.Sqrt(Kappa); } } /// @@ -291,6 +291,7 @@ public override double[] MaximumOfParameters /// public void Estimate(IList sample, ParameterEstimationMethod estimationMethod) { + DistributionNumerics.ValidateSample(sample, 4, true); if (estimationMethod == ParameterEstimationMethod.MethodOfMoments) { SetParameters(ParametersFromMoments(Statistics.ProductMoments(sample))); @@ -336,6 +337,8 @@ public void SetParameters(double scale, double shape) /// public override void SetParameters(IList parameters) { + if (parameters == null || parameters.Count != NumberOfParameters) + throw new ArgumentOutOfRangeException(nameof(parameters), "Exactly two parameters are required."); SetParameters(parameters[0], parameters[1]); } @@ -365,6 +368,12 @@ public override void SetParameters(IList parameters) /// public override ArgumentOutOfRangeException? ValidateParameters(IList parameters, bool throwException) { + if (parameters == null || parameters.Count != NumberOfParameters) + { + var exception = new ArgumentOutOfRangeException(nameof(parameters), "Exactly two parameters are required."); + if (throwException) throw exception; + return exception; + } return ValidateParameters(parameters[0], parameters[1], throwException); } @@ -372,14 +381,16 @@ public override void SetParameters(IList parameters) public double[] ParametersFromMoments(IList moments) { var parms = new double[NumberOfParameters]; - parms[0] = 1d / (moments[0] / Math.Pow(moments[1], 2d)); - parms[1] = Math.Pow(moments[0], 2d) / Math.Pow(moments[1], 2d); + parms[0] = moments[1] * (moments[1] / moments[0]); + double ratio = moments[0] / moments[1]; + parms[1] = ratio * ratio; return parms; } /// public double[] MomentsFromParameters(IList parameters) { + ValidateParameters(parameters, true); var dist = new GammaDistribution(); dist.SetParameters(parameters); var m1 = dist.Mean; @@ -420,6 +431,7 @@ public double[] ParametersFromLinearMoments(IList moments) /// public double[] LinearMomentsFromParameters(IList parameters) { + ValidateParameters(parameters, true); double alpha = parameters[1]; double beta = parameters[0]; double L1 = alpha * beta; @@ -466,19 +478,21 @@ public double[] LinearMomentsFromParameters(IList parameters) } /// + /// Requires at least four finite, strictly positive, nonconstant observations. The + /// existing moment initialization is evaluated in unit coordinates to preserve small and large scales. + /// The sample or a finite feasible initialization is invalid. public Tuple GetParameterConstraints(IList sample) { - var initialVals = new double[NumberOfParameters]; + DistributionNumerics.ValidateSample(sample, 4, true); var lowerVals = new double[NumberOfParameters]; var upperVals = new double[NumberOfParameters]; - // Get initial values - initialVals = ParametersFromMoments(Statistics.ProductMoments(sample)); - // Get bounds of scale - lowerVals[0] = Tools.DoubleMachineEpsilon; - upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[0]) + 1d)); - // Get bounds of shape - lowerVals[1] = Tools.DoubleMachineEpsilon; - upperVals[1] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[1]) + 1d)); + double normalization = DistributionNumerics.InitializationScale(sample); + var normalized = new double[sample.Count]; + for (int i = 0; i < sample.Count; i++) normalized[i] = sample[i] / normalization; + var initialVals = ParametersFromMoments(Statistics.ProductMoments(normalized)); + initialVals[0] *= normalization; + DistributionNumerics.PositiveParameterBounds(initialVals[0], out lowerVals[0], out upperVals[0]); + DistributionNumerics.PositiveParameterBounds(initialVals[1], out lowerVals[1], out upperVals[1]); return new Tuple(initialVals, lowerVals, upperVals); } @@ -510,6 +524,7 @@ double logLH(double[] x) /// Array of sample data. public void MLE_NR(IList sample) { + DistributionNumerics.ValidateSample(sample, 2, true); double lnsum = 0d; for (int i = 0; i < sample.Count; i++) lnsum += Math.Log(sample[i]); @@ -531,6 +546,7 @@ public void MLE_NR(IList sample) /// Array of sample data. public void MLE_Bobee(IList sample) { + DistributionNumerics.ValidateSample(sample, 2, true); double A = Statistics.Mean(sample); double G = Statistics.GeometricMean(sample); double U = Math.Log(A) - Math.Log(G); @@ -544,11 +560,7 @@ public void MLE_Bobee(IList sample) /// public override double PDF(double X) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Theta, Kappa, true); - if (X < Minimum || X > Maximum) return 0.0d; - return Math.Exp(-X / Theta + (Kappa - 1.0d) * Math.Log(X) - Kappa * Math.Log(Theta) - Gamma.LogGamma(Kappa)); + return Math.Exp(LogPDF(X)); } /// @@ -563,29 +575,48 @@ public override double LogPDF(double X) // Validate parameters if (_parametersValid == false) ValidateParameters(Theta, Kappa, true); - if (X < Minimum || X > Maximum) return double.NegativeInfinity; - double lf = -X / Theta + (Kappa - 1.0d) * Math.Log(X) - Kappa * Math.Log(Theta) - Gamma.LogGamma(Kappa); + if (X < Minimum || double.IsPositiveInfinity(X)) return double.NegativeInfinity; + if (X == 0) return Kappa == 1 ? -Math.Log(Theta) : Kappa < 1 ? double.PositiveInfinity : double.NegativeInfinity; + double unit = X / Theta; + double lf = unit == 0 && X > 0 + ? (Kappa - 1) * (Math.Log(X) - Math.Log(Theta)) - Gamma.LogGamma(Kappa) - Math.Log(Theta) + : DistributionNumerics.GammaLogDensity(Kappa, unit) - Math.Log(Theta); return double.IsNaN(lf) ? double.NegativeInfinity : lf; } /// public override double CDF(double X) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Theta, Kappa, true); - if (X <= Minimum) - return 0d; - if (X >= Maximum) - return 1d; - return Gamma.LowerIncomplete(Kappa, X / Theta); + return Math.Exp(LogCDF(X)); + } + + /// + public override double LogCDF(double X) + { + if (!_parametersValid) ValidateParameters(Theta, Kappa, true); + if (X <= 0) return double.NegativeInfinity; + double unit = X / Theta; + return unit == 0 ? Kappa * (Math.Log(X) - Math.Log(Theta)) - DistributionNumerics.LogGammaOnePlus(Kappa) + : DistributionNumerics.GammaLogCDF(Kappa, unit); + } + + /// + public override double CCDF(double X) => Math.Exp(LogCCDF(X)); + + /// + public override double LogCCDF(double X) + { + if (!_parametersValid) ValidateParameters(Theta, Kappa, true); + if (X <= 0) return 0; + double unit = X / Theta; + return unit == 0 ? DistributionNumerics.Log1mExp(LogCDF(X)) : DistributionNumerics.GammaLogSurvival(Kappa, unit); } /// public override double InverseCDF(double probability) { // Validate probability - if (probability < 0.0d || probability > 1.0d) + if (!(probability >= 0.0d && probability <= 1.0d)) throw new ArgumentOutOfRangeException("probability", "Probability must be between 0 and 1."); if (probability == 0.0d) return Minimum; @@ -594,7 +625,15 @@ public override double InverseCDF(double probability) // Validate parameters if (_parametersValid == false) ValidateParameters(Theta, Kappa, true); - return Gamma.InverseLowerIncomplete(Kappa, probability) * Theta; + // Preserve the exact exponential identity, including subnormal probabilities whose + // unnecessary log/exp round trip loses range on .NET Framework. + if (Kappa == 1) return -Theta * Tools.Log1p(-probability); + double unitQuantile = DistributionNumerics.GammaInverseCDF(Kappa, probability); + // The lower-tail series has negligible higher-order terms at these unit quantiles. + // Combine logs before multiplication when the scale can rescue an underflowed unit value. + return unitQuantile < 1E-200 + ? Math.Exp(Math.Log(Theta) + (Math.Log(probability) + DistributionNumerics.LogGammaOnePlus(Kappa)) / Kappa) + : unitQuantile * Theta; } /// @@ -607,7 +646,7 @@ public override double InverseCDF(double probability) public double WilsonHilfertyInverseCDF(double probability) { // Validate probability - if (probability < 0.0d || probability > 1.0d) + if (!(probability >= 0.0d && probability <= 1.0d)) throw new ArgumentOutOfRangeException("probability", "Probability must be between 0 and 1."); if (probability == 0.0d) return Minimum; @@ -625,8 +664,13 @@ public double WilsonHilfertyInverseCDF(double probability) /// /// Coefficient of skewness. /// Probability between 0 and 1. + /// The named approximate frequency factor, not the actual Gamma quantile. + /// Large skew magnitude is capped at 9.75 before evaluating its powers. Negative + /// skew uses the reflected probability and sign of the corresponding positive-skew approximation. public static double FrequencyFactorKp(double skewness, double probability) { + if (!DistributionNumerics.IsFinite(skewness)) throw new ArgumentOutOfRangeException(nameof(skewness)); + if (!(probability >= 0 && probability <= 1)) throw new ArgumentOutOfRangeException(nameof(probability)); double C = skewness; double absC = Math.Abs(C); // If skew is sufficiently close to zero, return standard Normal Z variate. @@ -660,10 +704,9 @@ public static double FrequencyFactorKp(double skewness, double probability) { // If abs(skew) is greater than 2, use Modified Wilson-Hilferty transformation (Kirby, 1972) // Only, valid if abs(skew) <= 9.75. Enforce limits. - if (C < -9.75d) - C = -9.75d; - if (C > 9.75d) - C = 9.75d; + double sign = Math.Sign(C); + C = Math.Min(absC, 9.75); + absC = Math.Abs(C); // Hoshi and Burges (1981b) gave polynomial expressions for 1/A, B, G and H^3 as a function of Cs // Compute skew orders @@ -697,7 +740,8 @@ public static double FrequencyFactorKp(double skewness, double probability) double G = g0 + g1 * C + g2 * C2 + g3 * C3 + g4 * C4 + g5 * C5; // Compute H double H = Math.Pow(B - 2.0d / absC / A, 1d / 3d); - return Math.Sign(C) * A * (Math.Pow(Math.Max(H, 1.0d - Math.Pow(G / 6.0d, 2d) + G / 6.0d * Normal.StandardZ(probability)), 3d) - B); + // Reflect the normal score directly; forming 1-p would round a tiny p to one. + return sign * A * (Math.Pow(Math.Max(H, 1.0d - Math.Pow(G / 6.0d, 2d) + G / 6.0d * sign * Normal.StandardZ(probability)), 3d) - B); } } @@ -709,6 +753,8 @@ public static double FrequencyFactorKp(double skewness, double probability) /// The partial derivative of the frequency factor with respect to skewness. public static double PartialKp(double skewness, double probability) { + if (!DistributionNumerics.IsFinite(skewness)) throw new ArgumentOutOfRangeException(nameof(skewness)); + DistributionNumerics.ValidateProbability(probability); double C = skewness; double absC = Math.Abs(C); // Use the Cornish-Fisher derivative limit at zero skew. @@ -760,8 +806,13 @@ public override UnivariateDistributionBase Clone() } /// + /// Uses scale-theta and shape-kappa coordinates. MoM is the sample-mean/sample-variance + /// sandwich; MLE is the expected Fisher inverse. Both require positive sample size and valid parameters. + /// Parameters or sample size are invalid. + /// The estimation method is unsupported. public double[,] ParameterCovariance(int sampleSize, ParameterEstimationMethod estimationMethod) { + DistributionNumerics.ValidateSampleSize(sampleSize); if (estimationMethod != ParameterEstimationMethod.MethodOfMoments && estimationMethod != ParameterEstimationMethod.MaximumLikelihood) { @@ -775,45 +826,22 @@ public override UnivariateDistributionBase Clone() var covar = new double[2, 2]; if (estimationMethod == ParameterEstimationMethod.MethodOfMoments) { - // MoM asymptotic covariance via (DᵀS⁻¹D)⁻¹/n. - // Moment conditions: g₁ = X − κθ, g₂ = (X−κθ)² − κθ². - // D = ∂g/∂(θ,κ) = [[-κ, -θ], [-2κθ, -θ²]]. - // S = E[g·gᵀ] = [[μ₂, μ₃], [μ₃, μ₄−μ₂²]]. - double t2 = t * t, t3 = t2 * t, t4 = t2 * t2; - // Central moments of Gamma(θ, κ) - double mu2 = k * t2; - double mu3 = 2.0 * k * t3; - double mu4 = 3.0 * k * (k + 2.0) * t4; - // S matrix and its inverse - double S00 = mu2, S01 = mu3, S11 = mu4 - mu2 * mu2; - double detS = S00 * S11 - S01 * S01; - double Si00 = S11 / detS, Si01 = -S01 / detS, Si11 = S00 / detS; - // D matrix - double d00 = -k, d01 = -t, d10 = -2.0 * k * t, d11 = -t2; - // DᵀS⁻¹ - double ds00 = d00 * Si00 + d10 * Si01; - double ds01 = d00 * Si01 + d10 * Si11; - double ds10 = d01 * Si00 + d11 * Si01; - double ds11 = d01 * Si01 + d11 * Si11; - // Bread = (DᵀS⁻¹)D - double b00 = ds00 * d00 + ds01 * d10; - double b01 = ds00 * d01 + ds01 * d11; - double b11 = ds10 * d01 + ds11 * d11; - // Bread⁻¹ / n - double detB = b00 * b11 - b01 * b01; - covar[0, 0] = b11 / (detB * sampleSize); - covar[1, 1] = b00 / (detB * sampleSize); - covar[0, 1] = -b01 / (detB * sampleSize); + // Algebraic simplification of the same sample-mean/sample-variance sandwich. + double scaled = t / Math.Sqrt(sampleSize); + covar[0, 0] = (scaled * scaled) * (2 + 3 / k); + covar[1, 1] = 2 * (k / sampleSize) * (k + 1); + covar[0, 1] = -2 * (t / sampleSize) * (k + 1); covar[1, 0] = covar[0, 1]; } else { // MLE: Fisher information inverse in (θ, κ) space. // Transformed from (α=1/θ, κ) via delta method. - double NA = Gamma.Trigamma(k) - 1.0 / k; - covar[0, 0] = t * t * Gamma.Trigamma(k) / (sampleSize * k * NA); // Var(θ̂) - covar[1, 1] = 1.0 / (sampleSize * NA); // Var(κ̂) - covar[0, 1] = -t / (sampleSize * k * NA); // Cov(θ̂, κ̂) — negative + double logResidual = Math.Log(DistributionNumerics.GammaScaledFisherResidual(k)); + double logScale = Math.Log(t), logShape = Math.Log(k), logCount = Math.Log(sampleSize); + covar[0, 0] = Math.Exp(2 * logScale - logCount + DistributionNumerics.LogSum(-logResidual, -logShape)); // Var(θ̂) + covar[1, 1] = Math.Exp(2 * logShape - logCount - logResidual); // Var(κ̂) + covar[0, 1] = -Math.Exp(logScale + logShape - logCount - logResidual); // Cov(θ̂, κ̂) — negative covar[1, 0] = covar[0, 1]; } return covar; @@ -825,86 +853,70 @@ public override UnivariateDistributionBase Clone() /// Probability between 0 and 1. /// The sample size. /// The distribution parameter estimation method. + /// The covariance quadratic form for the actual inverse CDF. + /// Both supported methods differentiate the actual Gamma quantile. The named + /// Wilson-Hilferty and frequency-factor approximations are not used here. An algebraically + /// equivalent sum of two nonnegative terms preserves the mean-direction variance at large + /// shape; physical scale is restored in logarithms. Probability must be finite and strictly interior. public double QuantileVariance(double probability, int sampleSize, ParameterEstimationMethod estimationMethod) { - if (estimationMethod != ParameterEstimationMethod.MethodOfMoments && - estimationMethod != ParameterEstimationMethod.MaximumLikelihood) + DistributionNumerics.ValidateProbability(probability); + if (!_parametersValid) ValidateParameters(Theta, Kappa, true); + DistributionNumerics.ValidateSampleSize(sampleSize); + double logCoefficient; + if (estimationMethod == ParameterEstimationMethod.MaximumLikelihood) + logCoefficient = -Math.Log(DistributionNumerics.GammaScaledFisherResidual(Kappa)); + else if (estimationMethod == ParameterEstimationMethod.MethodOfMoments) + logCoefficient = Math.Log(2) + DistributionNumerics.LogSum(0, -Math.Log(Kappa)); + else throw new NotImplementedException(); + + double unitQuantile = DistributionNumerics.GammaInverseCDF(Kappa, probability); + double logQuantile, logDifference; + if (unitQuantile < 1E-200) { - throw new NotImplementedException(); + // The same lower-tail log quantile and derivative used by the public gradient, + // with kappa cancelled before forming q-kappa*dq/dkappa. + logQuantile = (Math.Log(probability) + DistributionNumerics.LogGammaOnePlus(Kappa)) / Kappa; + if (double.IsNegativeInfinity(logQuantile)) return 0; + logDifference = logQuantile + Math.Log(Math.Abs(1 - Gamma.Digamma(1 + Kappa) + logQuantile)); } - if (estimationMethod == ParameterEstimationMethod.MethodOfMoments) - { - double CV = CoefficientOfVariation; - double V = Variance; - int N = sampleSize; - return V / N * (Math.Pow(1d + FrequencyFactorKp(Skewness, probability) * CV, 2d) + 0.5d * Math.Pow(FrequencyFactorKp(Skewness, probability) + 2d * CV * PartialKp(Skewness, probability), 2d) * (1d + Math.Pow(CV, 2d))); - } - else if (estimationMethod == ParameterEstimationMethod.MaximumLikelihood) + else { - var covar = ParameterCovariance(sampleSize, estimationMethod); - var grad = QuantileGradient(probability); - double varA = covar[0, 0]; - double varB = covar[1, 1]; - double covAB = covar[1, 0]; - double dQx1 = grad[0]; - double dQx2 = grad[1]; - return Math.Pow(dQx1, 2d) * varA + Math.Pow(dQx2, 2d) * varB + 2d * dQx1 * dQx2 * covAB; + logQuantile = Math.Log(unitQuantile); + double derivative = DistributionNumerics.GammaQuantileShapeDerivative(Kappa, unitQuantile); + logDifference = Math.Log(Math.Abs(unitQuantile - Kappa * derivative)); } - return double.NaN; + // Exactly q^2/kappa + A*(q-kappa*dq/dkappa)^2, before theta^2/n. + double logVariance = DistributionNumerics.LogSum(2 * logQuantile - Math.Log(Kappa), logCoefficient + 2 * logDifference); + return Math.Exp(2 * Math.Log(Theta) - Math.Log(sampleSize) + logVariance); } /// + /// Returns [unit-scale quantile, theta times its implicit shape derivative]. The + /// convergent incomplete-Gamma equations are differentiated at the actual quantile. + /// Parameters are invalid or probability is not finite and strictly interior. public double[] QuantileGradient(double probability) { + DistributionNumerics.ValidateProbability(probability); // Validate parameters if (_parametersValid == false) ValidateParameters(_theta, _kappa, true); - // Q(p) = κθ + √κ·θ·Kp(γ,p) in (θ, κ) parameterization. - var gradient = new double[] + double unitQuantile = DistributionNumerics.GammaInverseCDF(Kappa, probability); + double shapeDerivative; + if (unitQuantile < 1E-200) { - PartialforTheta(probability), // ∂Q/∂θ - PartialforKappa(probability) // ∂Q/∂κ - }; - return gradient; - } - - /// - /// Partial derivative with respect to theta. - /// - /// The probability to evaluate. - private double PartialforTheta(double probability) - { - return FrequencyFactorKp(Skewness, probability) * Math.Sqrt(Kappa) + Kappa; - } - - /// - /// Partial derivative with respect to kappa. - /// - /// The probability to evaluate. - private double PartialforKappa(double probability) - { - return Theta * (FrequencyFactorKp(Skewness, probability) / (2.0d * Math.Sqrt(Kappa)) + 1.0d - PartialKp(Skewness, probability) / Kappa); + double logQuantile = (Math.Log(probability) + DistributionNumerics.LogGammaOnePlus(Kappa)) / Kappa; + double logarithmicDerivative = (Gamma.Digamma(1 + Kappa) - logQuantile) / Kappa; + shapeDerivative = Math.Exp(Math.Log(Theta) + logQuantile + Math.Log(logarithmicDerivative)); + } + else shapeDerivative = Theta * DistributionNumerics.GammaQuantileShapeDerivative(Kappa, unitQuantile); + return [unitQuantile, shapeDerivative]; } /// public double[,] QuantileJacobian(IList probabilities, out double determinant) { - if (probabilities.Count != NumberOfParameters) - { - throw new ArgumentOutOfRangeException(nameof(probabilities), "The number of probabilities must be the same length as the number of distribution parameters."); - } - // Compute determinant - // |a b| - // |c d| - // |A| = ad − bc - double a = PartialforTheta(probabilities[0]); - double b = PartialforKappa(probabilities[0]); - double c = PartialforTheta(probabilities[1]); - double d = PartialforKappa(probabilities[1]); - determinant = a * d - b * c; - // Return Jacobian - var jacobian = new double[,] { { a, b }, { c, d } }; - return jacobian; + return DistributionNumerics.QuantileJacobian(this, probabilities, out determinant); } /// diff --git a/Numerics/Distributions/Univariate/GeneralizedExtremeValue.cs b/Numerics/Distributions/Univariate/GeneralizedExtremeValue.cs index fb9c5f74..93ec18f4 100644 --- a/Numerics/Distributions/Univariate/GeneralizedExtremeValue.cs +++ b/Numerics/Distributions/Univariate/GeneralizedExtremeValue.cs @@ -146,125 +146,89 @@ public override double[] GetParameters } /// + /// The mean exists for kappa > -1. Log-Gamma arithmetic preserves finite scaled + /// values even when a raw Gamma function overflows. public override double Mean { get { - if (Math.Abs(Kappa) <= NearZero) - { - return Xi + Alpha * Tools.Euler; - } - else if (Math.Abs(Kappa) < 1d) - { - return Xi + (Alpha / Kappa * (1d - Gamma.Function(1d + Kappa))); - } - else - { - return double.NaN; - } + if (Kappa <= -1) return double.NaN; + if (Math.Abs(Kappa) <= .05) return Xi + Alpha * SmallShapeStandardizedMoments()[0]; + double logarithm = Gamma.LogGamma(1 + Kappa); + if (logarithm == 0) return Xi; + double magnitude = logarithm > 0 ? logarithm + DistributionNumerics.Log1mExp(-logarithm) + : DistributionNumerics.Log1mExp(logarithm); + return Xi - Math.Sign(Kappa) * Math.Sign(logarithm) + * Math.Exp(Math.Log(Alpha) + magnitude - Math.Log(Math.Abs(Kappa))); } } /// public override double Median { - get - { - if (Math.Abs(Kappa) <= NearZero) - { - return Xi - Alpha * Math.Log(Math.Log(2.0d)); - } - else - { - return Xi + Alpha * (Math.Pow(Math.Log(2.0d), -Kappa) - 1d) / Kappa; - } - } + get { return InverseCDF(.5); } } /// + /// The mode is the finite upper endpoint when kappa is at least one; otherwise it is the interior stationary point. public override double Mode { get { - if (Math.Abs(Kappa) <= NearZero) - { - return Xi; - } - else - { - return Xi + Alpha * (Math.Pow(1d + Kappa, -Kappa) - 1d) / Kappa; - } + if (Kappa >= 1) return Maximum; + double logarithm = Tools.Log1p(-Kappa); + return Xi - Alpha * logarithm * DistributionNumerics.Exprel(Kappa * logarithm); } } /// + /// The variance exists for kappa > -1/2. Nonexistent moments return NaN. public override double StandardDeviation { get { - if (Math.Abs(Kappa) <= NearZero) - { - return Math.Sqrt(Math.Pow(Alpha, 2d) * Math.Pow(Math.PI, 2d) / 6d); - } - else if (Math.Abs(Kappa) < 0.5d) - { - double g1 = Gamma.Function(1d + Kappa); - double g2 = Gamma.Function(1d + 2d * Kappa); - return Math.Sqrt(Math.Pow(Alpha, 2d) * (g2 - Math.Pow(g1, 2d)) / Math.Pow(Kappa, 2d)); - } - else - { - return double.NaN; - } + if (Kappa <= -.5) return double.NaN; + if (Math.Abs(Kappa) <= .05) return Alpha * SmallShapeStandardizedMoments()[1]; + if (Kappa == 1) return Alpha; + double logVariance = LogPowerVariance(Kappa); + return Math.Exp(Math.Log(Alpha) + .5 * logVariance - Math.Log(Math.Abs(Kappa))); } } /// + /// The third moment exists for kappa > -1/3; positive bounded shapes are not excluded. public override double Skewness { get { - if (Math.Abs(Kappa) <= NearZero) - { - return 1.1396d; - } - else if (Math.Abs(Kappa) < 1d / 3d) - { - double U1 = Gamma.Function(1d + Kappa); - double U2 = Gamma.Function(1d + 2d * Kappa); - double U3 = Gamma.Function(1d + 3d * Kappa); - return Math.Sign(Kappa) * (-U3 + 3d * U1 * U2 - 2d * Math.Pow(U1, 3d)) / Math.Pow(U2 - Math.Pow(U1, 2d), 3d / 2d); - } - else - { - return double.NaN; - } + if (Kappa <= -1d / 3d) return double.NaN; + if (Math.Abs(Kappa) <= .05) return SmallShapeStandardizedMoments()[2]; + if (Kappa == 1) return -2; + double l1 = Gamma.LogGamma(1 + Kappa), l2 = Gamma.LogGamma(1 + 2 * Kappa), l3 = Gamma.LogGamma(1 + 3 * Kappa); + if (double.IsPositiveInfinity(l3)) return double.NegativeInfinity; + double largest = Math.Max(l3, Math.Max(l1 + l2, 3 * l1)); + double centered = Math.Exp(l3 - largest) - 3 * Math.Exp(l1 + l2 - largest) + 2 * Math.Exp(3 * l1 - largest); + return centered == 0 ? 0 : -Math.Sign(Kappa) * Math.Sign(centered) + * Math.Exp(largest + Math.Log(Math.Abs(centered)) - 1.5 * LogPowerVariance(Kappa)); } } /// + /// Ordinary kurtosis requires kappa > -1/4. Analytical normalized central moments are used without raw-moment overflow. public override double Kurtosis { get { - if (Math.Abs(Kappa) <= NearZero) - { - return 3 + 12d / 5d; - } - else if (Math.Abs(Kappa) < 0.25d) - { - double U1 = Gamma.Function(1d + Kappa); - double U2 = Gamma.Function(1d + 2d * Kappa); - double U3 = Gamma.Function(1d + 3d * Kappa); - double U4 = Gamma.Function(1d + 4d * Kappa); - double kNum = U4 - 4d * U3 * U1 - 3d * Math.Pow(U2, 2d) + 12d * U2 * Math.Pow(U1, 2d) - 6d * Math.Pow(U1, 4d); - double kDen = Math.Pow(U2 - Math.Pow(U1, 2d), 2d); - return 3 + kNum / kDen; - } - else - { - return double.NaN; - } + if (Kappa <= -.25) return double.NaN; + if (Math.Abs(Kappa) <= .05) return SmallShapeStandardizedMoments()[3]; + if (Kappa == 1) return 9; + double l1 = Gamma.LogGamma(1 + Kappa), l2 = Gamma.LogGamma(1 + 2 * Kappa); + double l3 = Gamma.LogGamma(1 + 3 * Kappa), l4 = Gamma.LogGamma(1 + 4 * Kappa); + if (double.IsPositiveInfinity(l4)) return double.PositiveInfinity; + double largest = Math.Max(Math.Max(l4, l1 + l3), Math.Max(2 * l1 + l2, 4 * l1)); + double centered = Math.Exp(l4 - largest) - 4 * Math.Exp(l1 + l3 - largest) + + 6 * Math.Exp(2 * l1 + l2 - largest) - 3 * Math.Exp(4 * l1 - largest); + return Math.Exp(largest + Math.Log(centered) - 2 * LogPowerVariance(Kappa)); } } @@ -273,7 +237,7 @@ public override double Minimum { get { - if (Kappa >= -NearZero) + if (Kappa >= 0) { return double.NegativeInfinity; } @@ -289,7 +253,7 @@ public override double Maximum { get { - if (Kappa <= NearZero) + if (Kappa <= 0) { return double.PositiveInfinity; } @@ -315,6 +279,7 @@ public override double[] MaximumOfParameters /// public void Estimate(IList sample, ParameterEstimationMethod estimationMethod) { + DistributionNumerics.ValidateSample(sample, 4); if (estimationMethod == ParameterEstimationMethod.MethodOfMoments) { SetParameters(DirectMethodOfMoments(Statistics.ProductMoments(sample))); @@ -361,6 +326,8 @@ public void SetParameters(double location, double scale, double shape) /// public override void SetParameters(IList parameters) { + if (parameters == null || parameters.Count != NumberOfParameters) + throw new ArgumentOutOfRangeException(nameof(parameters), "Exactly three parameters are required."); SetParameters(parameters[0], parameters[1], parameters[2]); } @@ -396,6 +363,12 @@ public override void SetParameters(IList parameters) /// public override ArgumentOutOfRangeException? ValidateParameters(IList parameters, bool throwException) { + if (parameters == null || parameters.Count != NumberOfParameters) + { + var exception = new ArgumentOutOfRangeException(nameof(parameters), "Exactly three parameters are required."); + if (throwException) throw exception; + return exception; + } return ValidateParameters(parameters[0], parameters[1], parameters[2], throwException); } @@ -453,6 +426,7 @@ public double[] ParametersFromMoments(IList moments) /// public double[] MomentsFromParameters(IList parameters) { + ValidateParameters(parameters, true); var dist = new GeneralizedExtremeValue(); dist.SetParameters(parameters); var m1 = dist.Mean; @@ -542,6 +516,7 @@ public double[] ParametersFromLinearMoments(IList moments) /// public double[] LinearMomentsFromParameters(IList parameters) { + ValidateParameters(parameters, true); double xi = parameters[0]; double alpha = parameters[1]; double kappa = parameters[2]; @@ -555,26 +530,28 @@ public double[] LinearMomentsFromParameters(IList parameters) } /// + /// Requires at least four finite, nonconstant observations. Initialization uses + /// the existing linear-moment estimator and preserves the fitting shape bounds. + /// The sample or a finite feasible initialization is invalid. public Tuple GetParameterConstraints(IList sample) { - var initialVals = new double[NumberOfParameters]; + DistributionNumerics.ValidateSample(sample, 4); var lowerVals = new double[NumberOfParameters]; var upperVals = new double[NumberOfParameters]; - // Get initial values - // initialVals = DirectMethodOfMoments(Statistics.ComputeProductMoments(sample)) - initialVals = ParametersFromLinearMoments(Statistics.LinearMoments(sample)); - // Get bounds of location - if (initialVals[0] == 0d) initialVals[0] = Tools.DoubleMachineEpsilon; - lowerVals[0] = -Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); - upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); - // Get bounds of scale - lowerVals[1] = Tools.DoubleMachineEpsilon; - upperVals[1] = Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[1])) + 1d)); + double normalization = DistributionNumerics.InitializationScale(sample); + var normalized = new double[sample.Count]; + for (int i = 0; i < sample.Count; i++) normalized[i] = sample[i] / normalization; + var initialVals = ParametersFromLinearMoments(Statistics.LinearMoments(normalized)); + initialVals[0] *= normalization; + initialVals[1] *= normalization; + DistributionNumerics.LocationParameterBounds(ref initialVals[0], initialVals[1], Statistics.Minimum(sample), + Statistics.Maximum(sample), false, out lowerVals[0], out upperVals[0]); + DistributionNumerics.PositiveParameterBounds(initialVals[1], out lowerVals[1], out upperVals[1]); // Get bounds of shape lowerVals[2] = -10; upperVals[2] = 10d; // Correct initial value of kappa if necessary - if (initialVals[2] <= lowerVals[2] || initialVals[2] >= upperVals[2]) + if (!DistributionNumerics.IsFinite(initialVals[2]) || initialVals[2] <= lowerVals[2] || initialVals[2] >= upperVals[2]) { initialVals[2] = 0d; } @@ -608,30 +585,26 @@ double logLH(double[] x) /// public override double PDF(double x) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Xi, Alpha, Kappa, true); - if (x < Minimum || x > Maximum) return 0.0d; - double y = (x - Xi) / Alpha; - if (Math.Abs(Kappa) > NearZero) - y = -Math.Log(1d - Kappa * y) / Kappa; - return Math.Exp(-(1d - Kappa) * y - Math.Exp(-y)) / Alpha; + return Math.Exp(LogPDF(x)); } /// /// /// Evaluated in log space, so far-tail densities that underflow /// keep a finite log density. + /// The exact shape controls support. At the finite upper endpoint, shape one has density + /// 1/alpha and shapes above one have a genuine integrable infinite density. /// public override double LogPDF(double x) { // Validate parameters if (_parametersValid == false) ValidateParameters(Xi, Alpha, Kappa, true); - if (x < Minimum || x > Maximum) return double.NegativeInfinity; - double y = (x - Xi) / Alpha; - if (Math.Abs(Kappa) > NearZero) - y = -Math.Log(1d - Kappa * y) / Kappa; + if (x < Minimum || x > Maximum || double.IsInfinity(x)) return double.NegativeInfinity; + if (Kappa > 0 && x == Maximum) + return Kappa < 1 ? double.NegativeInfinity : Kappa == 1 ? -Math.Log(Alpha) : double.PositiveInfinity; + if (Kappa < 0 && x == Minimum) return double.NegativeInfinity; + double y = TransformedValue(x); double lf = -(1d - Kappa) * y - Math.Exp(-y) - Math.Log(Alpha); return double.IsNaN(lf) ? double.NegativeInfinity : lf; } @@ -639,22 +612,43 @@ public override double LogPDF(double x) /// public override double CDF(double x) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Xi, Alpha, Kappa, true); - if (x <= Minimum) return 0d; - if (x >= Maximum) return 1d; - double y = (x - Xi) / Alpha; - if (Math.Abs(Kappa) > NearZero) - y = -Math.Log(1d - Kappa * y) / Kappa; - return Math.Exp(-Math.Exp(-y)); + return Math.Exp(LogCDF(x)); + } + + /// + public override double LogCDF(double x) + { + if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); + if (x <= Minimum) return double.NegativeInfinity; + if (x >= Maximum) return 0; + return -Math.Exp(-TransformedValue(x)); + } + + /// + public override double CCDF(double x) => -Tools.Expm1(LogCDF(x)); + + /// + public override double LogCCDF(double x) + { + if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); + if (x <= Minimum) return 0; + if (x >= Maximum) return double.NegativeInfinity; + double y = TransformedValue(x); + double exponential = Math.Exp(-y); + return exponential == 0 ? -y : DistributionNumerics.Log1mExp(-exponential); + } + + /// Maps an interior observation to its Gumbel coordinate using the exact nonzero shape. + private double TransformedValue(double x) + { + return DistributionNumerics.HoskingShapeTransform(x, Xi, Alpha, Kappa); } /// public override double InverseCDF(double probability) { // Validate probability - if (probability < 0.0d || probability > 1.0d) + if (!(probability >= 0.0d && probability <= 1.0d)) throw new ArgumentOutOfRangeException("probability", "Probability must be between 0 and 1."); if (probability == 0.0d) return Minimum; @@ -663,48 +657,43 @@ public override double InverseCDF(double probability) // Validate parameters if (_parametersValid == false) ValidateParameters(Xi, Alpha, Kappa, true); - if (Math.Abs(Kappa) <= NearZero) - { - return Xi - Alpha * Math.Log(-Math.Log(probability)); - } - else - { - return Xi + Alpha / Kappa * (1d - Math.Pow(-Math.Log(probability), Kappa)); - } + double logarithm = Math.Log(-Math.Log(probability)); + double product = Kappa * logarithm; + double unitQuantile = double.IsNegativeInfinity(product) ? 1 / Kappa : DistributionNumerics.ScaledExprelProduct(1, -logarithm, product); + double displacement = double.IsNegativeInfinity(product) ? Alpha / Kappa : DistributionNumerics.ScaledExprelProduct(Alpha, -logarithm, product); + return double.IsInfinity(displacement) && DistributionNumerics.IsFinite(unitQuantile) + ? Alpha * (Xi / Alpha + unitQuantile) : Xi + displacement; } /// /// Gets the expected Fisher information matrix. /// /// The sample size. + /// The full three-parameter expected information in location, scale and shape coordinates. + /// Regular information requires kappa < 1/2. At zero shape the shape parameter + /// remains estimated, so this is not the two-parameter Gumbel information. + /// Sample size, distribution parameters or regularity are invalid. + /// Information cannot be resolved or represented numerically. public Matrix ExpectedInformationMatrix(int sampleSize) { - var _matrix = new double[3, 3]; - int N = sampleSize; - double a = Alpha; - double k = Kappa; - double p = Math.Pow(1d - k, 2d) * Gamma.Function(1d - 2d * k); - double q = (1d - k) * Gamma.Function(1d - k) * (Gamma.Digamma(1d - k) - (1d - k) / k); - double g = Tools.Euler; - double d2du2 = N / (a * a) * p; - double d2da2 = N / (a * a * k * k) * (1d - 2d * (1d - k) * Gamma.Function(1d - k) + p); - double d2dk2 = N / (k * k) * (Math.PI * Math.PI / 6d + Math.Pow(1d - g - 1d / k, 2d) + 2d * q / k + p / (k * k)); - double d2duda = N / (a * a * k) * (p - (1d - k) * Gamma.Function(1d - k)); - double d2dudk = -N / (a * k) * (p / k + q); - double d2dadk = N / (a * k * k) * (1d - g - (1d - (1d - k) * Gamma.Function(1d - k)) / k - p / k - q); - // Row 1 - _matrix[0, 0] = d2du2; - _matrix[0, 1] = d2duda; - _matrix[0, 2] = d2dudk; - // Row 2 - _matrix[1, 0] = d2duda; - _matrix[1, 1] = d2da2; - _matrix[1, 2] = d2dadk; - // Row 3 - _matrix[2, 0] = d2dudk; - _matrix[2, 1] = d2dadk; - _matrix[2, 2] = d2dk2; - return new Matrix(_matrix); + DistributionNumerics.ValidateSampleSize(sampleSize); + if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); + var information = KappaExpectedInformation.ExpectedInformation(Kappa, 0, 3, out _, out _, out _); + for (int i = 0; i < 3; i++) + for (int j = i; j < 3; j++) + { + double value = information[i, j]; + if (value != 0) + { + double logarithm = Math.Log(Math.Abs(value)) + Math.Log(sampleSize) + - (i < 2 ? Math.Log(Alpha) : 0) - (j < 2 ? Math.Log(Alpha) : 0); + value = Math.Sign(value) * Math.Exp(logarithm); + if (!DistributionNumerics.IsFinite(value)) + throw new InvalidOperationException("Expected information is outside the finite floating-point range."); + } + information[i, j] = information[j, i] = value; + } + return new Matrix(information); } /// @@ -714,6 +703,11 @@ public override UnivariateDistributionBase Clone() } /// + /// Full three-parameter MLE covariance requires positive sample size and kappa < 1/2. + /// The zero-shape limit still estimates shape; uncertainty regularity does not restrict the distribution's valid shape domain. + /// Parameters, sample size or information regularity are invalid. + /// The method is not maximum likelihood. + /// The information or covariance is numerically unresolved. public double[,] ParameterCovariance(int sampleSize, ParameterEstimationMethod estimationMethod) { if (estimationMethod != ParameterEstimationMethod.MaximumLikelihood) @@ -723,74 +717,100 @@ public override UnivariateDistributionBase Clone() // Validate parameters if (_parametersValid == false) ValidateParameters(Xi, _alpha, Kappa, true); - // Compute covariance - var matrix = ExpectedInformationMatrix(sampleSize); - return matrix.Inverse().ToArray(); + return KappaExpectedInformation.ParameterCovariance(Alpha, Kappa, 0, sampleSize, 3); } /// + /// Analytical exponential divided differences retain the exact nonzero shape and + /// the full [1,-L,-alpha*L squared/2] gradient at zero shape, where L=log(-log(p)). + /// Parameters are invalid or probability is not finite and strictly interior. public double[] QuantileGradient(double probability) { + DistributionNumerics.ValidateProbability(probability); if (_parametersValid == false) ValidateParameters(Xi, _alpha, Kappa, true); - double a = Alpha; - double k = Kappa; + double logarithm = Math.Log(-Math.Log(probability)); + double product = Kappa * logarithm; var gradient = new double[] { 1.0d, // location - 1d / k * (1d - Math.Pow(-Math.Log(probability), k)), // scale - -(a / (k * k)) * (1d - Math.Pow(-Math.Log(probability), k)) - a / k * Math.Pow(-Math.Log(probability), k) * Math.Log(-Math.Log(probability)) // shape + double.IsNegativeInfinity(product) ? 1 / Kappa : DistributionNumerics.ScaledExprelProduct(1, -logarithm, product), // scale + double.IsNegativeInfinity(product) ? -(Alpha / Kappa) / Kappa : -DistributionNumerics.ScaledExprelDerivativeProduct(Alpha, logarithm, product) // shape }; return gradient; } /// + /// Uses finite physical common-coordinate gradients and unit-scale covariance, + /// permitting scalar variance evaluation independently of the physical covariance matrix's range. public double QuantileVariance(double probability, int sampleSize, ParameterEstimationMethod estimationMethod) { - var covar = ParameterCovariance(sampleSize, estimationMethod); - var grad = QuantileGradient(probability); - double varA = covar[0, 0]; - double varB = covar[1, 1]; - double varG = covar[2, 2]; - double covAB = covar[1, 0]; - double covAG = covar[2, 0]; - double covBG = covar[2, 1]; - double dQx1 = grad[0]; - double dQx2 = grad[1]; - double dQx3 = grad[2]; - return Math.Pow(dQx1, 2d) * varA + Math.Pow(dQx2, 2d) * varB + Math.Pow(dQx3, 2d) * varG + 2d * dQx1 * dQx2 * covAB + 2d * dQx1 * dQx3 * covAG + 2d * dQx2 * dQx3 * covBG; + DistributionNumerics.ValidateProbability(probability); + if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); + var unit = new GeneralizedExtremeValue(0, 1, Kappa); + double logarithm = Math.Log(-Math.Log(probability)), product = Kappa * logarithm; + double scaleGradient = DistributionNumerics.ScaledExprelProduct(Alpha, -logarithm, product); + double shapeGradient = -DistributionNumerics.ScaledExprelDerivativeProduct(Alpha, logarithm, product); + return DistributionNumerics.ScaledQuantileVariance(unit.ParameterCovariance(sampleSize, estimationMethod), + [Alpha, scaleGradient, shapeGradient]); } /// public double[,] QuantileJacobian(IList probabilities, out double determinant) { - if (probabilities.Count != NumberOfParameters) - { - throw new ArgumentOutOfRangeException(nameof(probabilities), "The number of probabilities must be the same length as the number of distribution parameters."); - } - // Get gradients - var dQp1 = QuantileGradient(probabilities[0]); - var dQp2 = QuantileGradient(probabilities[1]); - var dQp3 = QuantileGradient(probabilities[2]); - // Compute determinant - // |a b c| - // |d e f| - // |g h i| - // |A| = a(ei − fh) − b(di − fg) + c(dh − eg) - double a = dQp1[0]; - double b = dQp1[1]; - double c = dQp1[2]; - double d = dQp2[0]; - double e = dQp2[1]; - double f = dQp2[2]; - double g = dQp3[0]; - double h = dQp3[1]; - double i = dQp3[2]; - determinant = a * (e * i - f * h) - b * (d * i - f * g) + c * (d * h - e * g); - // Return Jacobian - var jacobian = new double[,] { { a, b, c }, { d, e, f }, { g, h, i } }; - return jacobian; + return DistributionNumerics.QuantileJacobian(this, probabilities, out determinant); + } + + /// Returns log Var(T^power), T unit exponential, without forming raw gamma moments. + /// The caller establishes power > -1/2 and uses a divided series near zero. + internal static double LogPowerVariance(double power) + { + double first = Gamma.LogGamma(1 + power), second = Gamma.LogGamma(1 + 2 * power); + return double.IsPositiveInfinity(second) ? double.PositiveInfinity + : second + DistributionNumerics.Log1mExp(2 * first - second); + } + + /// Evaluates analytical standardized central moments with log-Gamma divided differences near zero shape. + /// Finite differences of log Gamma remove the cancelling powers before evaluation. + /// Exponential polynomial identities then retain the second through fourth centered moments. + /// The exact zero is the analytical Gumbel limit; every nonzero kappa remains in the series. + private double[] SmallShapeStandardizedMoments() + { + double k = Kappa; + if (k == 0) return [Tools.Euler, Math.PI / Math.Sqrt(6), 1.1395470994046487, 5.4]; + double logarithm = DistributionNumerics.LogGammaOnePlus(k); + double mean = -(logarithm / k) * DistributionNumerics.Exprel(logarithm); + double a = NormalizedLogGammaDifference(k, 2); + double b = NormalizedLogGammaDifference(k, 3); + double c = NormalizedLogGammaDifference(k, 4); + double a2 = k * k * a, b3 = k * k * k * b, c4 = k * k * k * k * c; + double u = Tools.Expm1(a2), v = Tools.Expm1(b3); + double variance = a * DistributionNumerics.Exprel(a2); + double third = b * DistributionNumerics.Exprel(b3); + double fourth = c * DistributionNumerics.Exprel(c4); + double skew = -(k * variance * variance * (3 + u) + Math.Exp(3 * a2) * third) + / (variance * Math.Sqrt(variance)); + double kurtosis = (variance * variance * (3 + u * (16 + u * (15 + u * (6 + u)))) + + 12 * k * third * Math.Exp(3 * a2) * a * DistributionNumerics.Exprel(3 * a2) + + Math.Exp(6 * a2) * (k * k * third * third * (6 + v * (4 + v)) + Math.Exp(4 * b3) * fourth)) + / (variance * variance); + return [mean, Math.Exp(logarithm) * Math.Sqrt(variance), skew, kurtosis]; + } + + /// Returns the order-r forward difference of log Gamma(1+j*kappa), divided by kappa to power r. + private static double NormalizedLogGammaDifference(double kappa, int order) + { + double sum = 0, power = 1; + for (int n = order; n <= 32; n++) + { + double factor = order == 2 ? Math.Pow(2, n) - 2 + : order == 3 ? Math.Pow(3, n) - 3 * Math.Pow(2, n) + 3 + : Math.Pow(4, n) - 4 * Math.Pow(3, n) + 6 * Math.Pow(2, n) - 4; + sum += (n % 2 == 0 ? 1 : -1) * DistributionNumerics.ZetaInteger(n) * factor * power / n; + power *= kappa; + } + return sum; } } -} \ No newline at end of file +} diff --git a/Numerics/Distributions/Univariate/GeneralizedLogistic.cs b/Numerics/Distributions/Univariate/GeneralizedLogistic.cs index 6c1087dc..e55eca2a 100644 --- a/Numerics/Distributions/Univariate/GeneralizedLogistic.cs +++ b/Numerics/Distributions/Univariate/GeneralizedLogistic.cs @@ -1,9 +1,8 @@ -using System; +using System; using System.Collections.Generic; using Numerics.Data.Statistics; using Numerics.Mathematics.Optimization; using Numerics.Mathematics.RootFinding; -using Numerics.Mathematics.SpecialFunctions; namespace Numerics.Distributions { @@ -149,41 +148,26 @@ public override double Mean { get { - if (Math.Abs(Kappa) <= NearZero) - { - return Xi; - } - else if (Math.Abs(Kappa) < 1d) - { - double U1 = Gamma.Function(1d + Kappa) * Gamma.Function(1d - Kappa); - return Xi + Alpha / Kappa * (1d - U1); - } - else - { - return double.NaN; - } + if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); + if (Math.Abs(Kappa) >= 1) return double.NaN; + if (Kappa == 0) return Xi; + return Xi + (Math.Abs(Kappa) <= .05 ? -Alpha * Kappa * Polynomial(ReciprocalCoefficients, Kappa * Kappa, 1) : Alpha * StandardMean(Kappa)); } } /// - public override double Median - { - get { return InverseCDF(0.5d); } - } + public override double Median => InverseCDF(.5); /// public override double Mode { get { - if (Math.Abs(Kappa) <= NearZero) - { - return Xi; - } - else - { - return Xi + Alpha * (Math.Pow(1d + Kappa, -Kappa) - 1d) / Kappa; - } + if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); + if (Kappa <= -1) return Minimum; + if (Kappa >= 1) return Maximum; + double z = Tools.Log1p(Kappa) - Tools.Log1p(-Kappa); + return QuantileAtLatent(z); } } @@ -192,20 +176,9 @@ public override double StandardDeviation { get { - if (Math.Abs(Kappa) <= NearZero) - { - return Alpha * Math.PI / Math.Sqrt(3d); - } - else if (Math.Abs(Kappa) < 0.5d) - { - double U1 = Gamma.Function(1d + Kappa) * Gamma.Function(1d - Kappa); - double U2 = Gamma.Function(1d + 2d * Kappa) * Gamma.Function(1d - 2d * Kappa); - return Math.Sqrt(Math.Pow(Alpha, 2d) / Math.Pow(Kappa, 2d) * (U2 - Math.Pow(U1, 2d))); - } - else - { - return double.NaN; - } + if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); + if (Math.Abs(Kappa) >= .5) return double.NaN; + return Alpha * Math.Sqrt(StandardVariance(Kappa)); } } @@ -214,21 +187,13 @@ public override double Skewness { get { - if (Math.Abs(Kappa) <= NearZero) - { - return 0.0d; - } - else if (Math.Abs(Kappa) < 1d / 3d) - { - double U1 = Gamma.Function(1d + Kappa) * Gamma.Function(1d - Kappa); - double U2 = Gamma.Function(1d + 2d * Kappa) * Gamma.Function(1d - 2d * Kappa); - double U3 = Gamma.Function(1d + 3d * Kappa) * Gamma.Function(1d - 3d * Kappa); - return Math.Sign(Kappa) * (-U3 + 3d * U1 * U2 - 2d * Math.Pow(U1, 3d)) / Math.Pow(U2 - Math.Pow(U1, 2d), 3d / 2d); - } - else - { - return double.NaN; - } + if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); + if (Math.Abs(Kappa) >= 1d / 3) return double.NaN; + if (Kappa == 0) return 0; + if (Math.Abs(Kappa) <= .05) + return Kappa * Polynomial(ThirdCoefficients, Kappa * Kappa, 2) / Math.Pow(StandardVariance(Kappa), 1.5); + double b1 = ReciprocalSinc(Kappa), b2 = ReciprocalSinc(2 * Kappa), b3 = ReciprocalSinc(3 * Kappa); + return Math.Sign(Kappa) * (-b3 + 3 * b1 * b2 - 2 * b1 * b1 * b1) / Math.Pow(b2 - b1 * b1, 1.5); } } @@ -237,39 +202,102 @@ public override double Kurtosis { get { - if (Math.Abs(Kappa) <= NearZero) - { - return 3d + 6d / 5d; - } - else if (Math.Abs(Kappa) < 0.25d) - { - double U1 = Gamma.Function(1d + Kappa) * Gamma.Function(1d - Kappa); - double U2 = Gamma.Function(1d + 2d * Kappa) * Gamma.Function(1d - 2d * Kappa); - double U3 = Gamma.Function(1d + 3d * Kappa) * Gamma.Function(1d - 3d * Kappa); - double U4 = Gamma.Function(1d + 4d * Kappa) * Gamma.Function(1d - 4d * Kappa); - double kNum = U4 - 4d * U3 * U1 - 3d * Math.Pow(U2, 2d) + 12d * U2 * Math.Pow(U1, 2d) - 6d * Math.Pow(U1, 4d); - double kDen = Math.Pow(U2 - Math.Pow(U1, 2d), 2d); - return 3 + kNum / kDen; - } - else - { - return double.NaN; - } + if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); + if (Math.Abs(Kappa) >= .25) return double.NaN; + if (Kappa == 0) return 21d / 5; + double variance = StandardVariance(Kappa); + if (Math.Abs(Kappa) <= .05) + return Polynomial(FourthCoefficients, Kappa * Kappa, 2) / (variance * variance); + double b1 = ReciprocalSinc(Kappa), b2 = ReciprocalSinc(2 * Kappa); + double b3 = ReciprocalSinc(3 * Kappa), b4 = ReciprocalSinc(4 * Kappa); + double v = b2 - b1 * b1; + return (b4 - 4 * b1 * b3 + 6 * b1 * b1 * b2 - 3 * b1 * b1 * b1 * b1) / (v * v); } } + private static readonly double[] ReciprocalCoefficients = BuildReciprocalCoefficients(); + private static readonly double[] VarianceCoefficients = BuildMomentCoefficients(2); + private static readonly double[] ThirdCoefficients = BuildMomentCoefficients(3); + private static readonly double[] FourthCoefficients = BuildMomentCoefficients(4); + + /// Coefficients of pi*k/sin(pi*k) as a power series in k squared. + private static double[] BuildReciprocalCoefficients() + { + var sinc = new double[15]; + var reciprocal = new double[15]; + sinc[0] = reciprocal[0] = 1; + for (int n = 1; n < sinc.Length; n++) + { + sinc[n] = -sinc[n - 1] * Math.PI * Math.PI / (2 * n) / (2 * n + 1); + for (int j = 1; j <= n; j++) reciprocal[n] -= sinc[j] * reciprocal[n - j]; + } + return reciprocal; + } + + /// Multiplies truncated power series used to remove exact central-moment zeros algebraically. + private static double[] Multiply(double[] left, double[] right) + { + var result = new double[left.Length]; + for (int n = 0; n < result.Length; n++) + for (int j = 0; j <= n; j++) result[n] += left[j] * right[n - j]; + return result; + } + + /// Forms central-moment numerator coefficients before evaluation, avoiding cancellation near zero shape. + private static double[] BuildMomentCoefficients(int order) + { + var b1 = ReciprocalCoefficients; + var b2 = new double[b1.Length]; + var b3 = new double[b1.Length]; + var b4 = new double[b1.Length]; + for (int n = 0; n < b1.Length; n++) + { + b2[n] = b1[n] * Math.Pow(4, n); + b3[n] = b1[n] * Math.Pow(9, n); + b4[n] = b1[n] * Math.Pow(16, n); + } + double[] b11 = Multiply(b1, b1), b12 = Multiply(b1, b2), b111 = Multiply(b11, b1); + double[] b13 = Multiply(b1, b3), b112 = Multiply(b11, b2), b1111 = Multiply(b11, b11); + var result = new double[b1.Length]; + for (int n = 0; n < result.Length; n++) + result[n] = order == 2 ? b2[n] - b11[n] + : order == 3 ? -b3[n] + 3 * b12[n] - 2 * b111[n] + : b4[n] - 4 * b13[n] + 6 * b112[n] - 3 * b1111[n]; + return result; + } + + /// Horner evaluation after dividing out the exact leading power of kappa squared. + private static double Polynomial(double[] coefficients, double squaredShape, int first) + { + double value = 0; + for (int n = coefficients.Length - 1; n >= first; n--) value = value * squaredShape + coefficients[n]; + return value; + } + + /// Returns pi*k/sin(pi*k), including its removable singularity. + private static double ReciprocalSinc(double k) => Math.Abs(k) <= .05 + ? 1 + k * k * Polynomial(ReciprocalCoefficients, k * k, 1) : Math.PI * k / Math.Sin(Math.PI * k); + + /// Returns the standardized mean shift without subtracting nearly equal raw moments. + private static double StandardMean(double k) => Math.Abs(k) <= .05 + ? -k * Polynomial(ReciprocalCoefficients, k * k, 1) : (1 - ReciprocalSinc(k)) / k; + + /// Returns standardized variance after dividing out its exact kappa-squared zero. + private static double StandardVariance(double k) => Math.Abs(k) <= .05 + ? Polynomial(VarianceCoefficients, k * k, 1) + : (ReciprocalSinc(2 * k) - Math.Pow(ReciprocalSinc(k), 2)) / k / k; /// public override double Minimum { get { - if (Kappa >= -NearZero) + if (Kappa >= 0) { return double.NegativeInfinity; } else { - return Xi + Alpha / Kappa; + return FiniteShapeEndpoint(); } } } @@ -279,13 +307,13 @@ public override double Maximum { get { - if (Kappa <= NearZero) + if (Kappa <= 0) { return double.PositiveInfinity; } else { - return Xi + Alpha / Kappa; + return FiniteShapeEndpoint(); } } } @@ -305,6 +333,7 @@ public override double[] MaximumOfParameters /// public void Estimate(IList sample, ParameterEstimationMethod estimationMethod) { + DistributionNumerics.ValidateSample(sample, 4); if (estimationMethod == ParameterEstimationMethod.MethodOfMoments) { SetParameters(DirectMethodOfMoments(Statistics.ProductMoments(sample))); @@ -397,26 +426,15 @@ public override void SetParameters(IList parameters) /// The array of sample moments. public double[] DirectMethodOfMoments(IList moments) { - // Solve for kappa + if (moments == null) throw new ArgumentNullException(nameof(moments)); + if (moments.Count < 3 || !DistributionNumerics.IsFinite(moments[0]) || !DistributionNumerics.IsFinite(moments[1]) + || moments[1] <= 0 || !DistributionNumerics.IsFinite(moments[2])) + throw new ArgumentOutOfRangeException(nameof(moments)); double k = SolveForKappa(moments[2]); - double a; - double x; - if (Math.Abs(k) <= NearZero) - { - x = moments[0]; - a = moments[1] * Math.Sqrt(3d) / Math.PI; - } - else - { - double U1 = Gamma.Function(1d + k) * Gamma.Function(1d - k); - double U2 = Gamma.Function(1d + 2d * k) * Gamma.Function(1d - 2d * k); - a = Math.Sqrt(moments[1] * moments[1] * k * k / (U2 - Math.Pow(U1, 2d))); - x = moments[0] - a / k * (1d - U1); - } - // return parameters + double a = moments[1] / Math.Sqrt(StandardVariance(k)); + double x = moments[0] - a * StandardMean(k); return [x, a, k]; } - /// public double[] MomentsFromParameters(IList parameters) { @@ -438,108 +456,61 @@ public double[] MomentsFromParameters(IList parameters) /// public double SolveForKappa(double skew) { - if (Math.Abs(skew) < 10d) - { - // Kappa must be solved for. The Brent method is used here. - return Brent.Solve((x) => - { - double U1 = Gamma.Lanczos(1d + x) * Gamma.Lanczos(1d - x); - double U2 = Gamma.Lanczos(1d + 2d * x) * Gamma.Lanczos(1d - 2d * x); - double U3 = Gamma.Lanczos(1d + 3d * x) * Gamma.Lanczos(1d - 3d * x); - double k = Math.Sign(x) * (-U3 + 3d * U1 * U2 - 2d * Math.Pow(U1, 3d)) / Math.Pow(U2 - Math.Pow(U1, 2d), 3d / 2d); - return k - skew; - - }, -(1d / 3d), 1d / 3d); - } - else - { - return double.NaN; - } + if (!DistributionNumerics.IsFinite(skew)) throw new ArgumentOutOfRangeException(nameof(skew)); + if (skew == 0) return 0; + if (Math.Abs(skew) >= 10) return double.NaN; + // Retain Brent and its convergence settings, evaluating finite moment values inside the open moment domain. + double bound = 1d / 3 - Tools.DoubleMachineEpsilon; + return Brent.Solve(k => new GeneralizedLogistic(0, 1, k).Skewness - skew, -bound, bound); } - /// public double[] ParametersFromLinearMoments(IList moments) { - double L1 = moments[0]; - double L2 = moments[1]; - double T3 = moments[2]; - double T4 = moments[3]; - double kappa = -T3; - double alpha; - double xi; - if (kappa == 0.0d) - { - alpha = L2; - xi = L1; - } - else if (Math.Abs(kappa) <= NearZero) - { - double kappa2 = kappa * kappa; - double pi2 = Math.PI * Math.PI; - double sinc = 1.0d - pi2 * kappa2 / 6.0d + pi2 * pi2 * kappa2 * kappa2 / 120.0d; - double reciprocalDifference = -pi2 * kappa / 6.0d - 7.0d * pi2 * pi2 * kappa * kappa2 / 360.0d; - alpha = L2 * sinc; - xi = L1 - alpha * reciprocalDifference; - } - else - { - alpha = L2 * Math.Sin(kappa * Math.PI) / (kappa * Math.PI); - xi = L1 - alpha * (1.0d / kappa - Math.PI / Math.Sin(kappa * Math.PI)); - } + if (moments == null) throw new ArgumentNullException(nameof(moments)); + if (moments.Count < 3 || !DistributionNumerics.IsFinite(moments[0]) || !DistributionNumerics.IsFinite(moments[1]) + || moments[1] <= 0 || !DistributionNumerics.IsFinite(moments[2]) || Math.Abs(moments[2]) >= 1) + throw new ArgumentOutOfRangeException(nameof(moments)); + double kappa = -moments[2]; + double alpha = moments[1] / ReciprocalSinc(kappa); + double xi = moments[0] - alpha * StandardMean(kappa); return [xi, alpha, kappa]; } - /// public double[] LinearMomentsFromParameters(IList parameters) { - double xi = parameters[0]; - double alpha = parameters[1]; - double kappa = parameters[2]; - if (Math.Abs(kappa) >= 1.0d) - throw new ArgumentOutOfRangeException(nameof(Kappa), "L-moments can only be defined for -1 < kappa < 1."); - double L1; - double L2; - if (kappa == 0.0d) - { - L1 = xi; - L2 = alpha; - } - else if (Math.Abs(kappa) <= NearZero) - { - double kappa2 = kappa * kappa; - double pi2 = Math.PI * Math.PI; - double reciprocalDifference = -pi2 * kappa / 6.0d - 7.0d * pi2 * pi2 * kappa * kappa2 / 360.0d; - double reciprocalSinc = 1.0d + pi2 * kappa2 / 6.0d + 7.0d * pi2 * pi2 * kappa2 * kappa2 / 360.0d; - L1 = xi + alpha * reciprocalDifference; - L2 = alpha * reciprocalSinc; - } - else - { - L1 = xi + alpha * (1.0d / kappa - Math.PI / Math.Sin(kappa * Math.PI)); - L2 = alpha * kappa * Math.PI / Math.Sin(kappa * Math.PI); - } - double T3 = -kappa; - double T4 = (1.0d + 5.0d * Math.Pow(kappa, 2.0d)) / 6.0d; - return [L1, L2, T3, T4]; + double xi = parameters[0], alpha = parameters[1], kappa = parameters[2]; + ValidateParameters(xi, alpha, kappa, true); + if (Math.Abs(kappa) >= 1) throw new ArgumentOutOfRangeException(nameof(parameters), "L-moments require absolute kappa below one."); + return [xi + alpha * StandardMean(kappa), alpha * ReciprocalSinc(kappa), -kappa, (1 + 5 * kappa * kappa) / 6]; } - /// public Tuple GetParameterConstraints(IList sample) { + DistributionNumerics.ValidateSample(sample, 4); // Estimate initial values using the method of moments (a.k.a product moments). var initialVals = new double[NumberOfParameters]; var lowerVals = new double[NumberOfParameters]; var upperVals = new double[NumberOfParameters]; // Get initial values // initialVals = DirectMethodOfMoments(Statistics.ComputeProductMoments(sample)) - initialVals = ParametersFromLinearMoments(Statistics.LinearMoments(sample)); + double magnitude = 0; + foreach (double value in sample) magnitude = Math.Max(magnitude, Math.Abs(value)); + var scaled = new double[sample.Count]; + for (int i = 0; i < sample.Count; i++) scaled[i] = sample[i] / magnitude; + double[] moments = Statistics.LinearMoments(scaled); + initialVals = ParametersFromLinearMoments(moments); + initialVals[0] *= magnitude; + initialVals[1] *= magnitude; + var candidate = new GeneralizedLogistic(initialVals[0], initialVals[1], initialVals[2]); + if (!candidate.ParametersValid || !DistributionNumerics.IsFinite(candidate.LogLikelihood(sample))) + initialVals = [moments[0] * magnitude, moments[1] * magnitude, 0]; // Get bounds of location - if (initialVals[0] == 0d) initialVals[0] = Tools.DoubleMachineEpsilon; - lowerVals[0] = -Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); - upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); + double locationMagnitude = Math.Max(Math.Abs(initialVals[0]), initialVals[1]); + lowerVals[0] = -FiniteDecimalBound(locationMagnitude); + upperVals[0] = -lowerVals[0]; // Get bounds of scale - lowerVals[1] = Tools.DoubleMachineEpsilon; - upperVals[1] = Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[1]))) + 1d); + lowerVals[1] = Math.Min(Tools.DoubleMachineEpsilon, initialVals[1] / 10); + upperVals[1] = FiniteDecimalBound(initialVals[1]); // Get bounds of shape lowerVals[2] = -10; upperVals[2] = 10d; @@ -548,6 +519,11 @@ public Tuple GetParameterConstraints(IList { initialVals[2] = 0d; } + candidate.SetParameters(initialVals); + if (!candidate.ParametersValid || !DistributionNumerics.IsFinite(candidate.LogLikelihood(sample)) + || initialVals[0] <= lowerVals[0] || initialVals[0] >= upperVals[0] + || initialVals[1] <= lowerVals[1] || initialVals[1] >= upperVals[1]) + throw new InvalidOperationException("The sample does not admit a finite supported generalized-logistic initializer within finite bounds."); return new Tuple(initialVals, lowerVals, upperVals); } @@ -570,121 +546,163 @@ double logLH(double[] x) var solver = new NelderMead(logLH, NumberOfParameters, Initials, Lowers, Uppers); solver.ReportFailure = true; solver.Maximize(); + if (solver.Status != OptimizationStatus.Success + || ValidateParameters(solver.BestParameterSet.Values, false) != null + || !DistributionNumerics.IsFinite(new GeneralizedLogistic(solver.BestParameterSet.Values[0], solver.BestParameterSet.Values[1], solver.BestParameterSet.Values[2]).LogLikelihood(sample))) + throw new InvalidOperationException($"Generalized logistic maximum likelihood estimation failed with optimizer status {solver.Status} or a nonfinite fit."); return solver.BestParameterSet.Values; } + /// Retains decimal-order fitting bounds without overflow or a zero-centered collapse. + private static double FiniteDecimalBound(double value) + { + double bound = Math.Pow(10, Math.Ceiling(Math.Log10(value)) + 1); + return double.IsPositiveInfinity(bound) ? double.MaxValue : bound; + } + + /// Retains a finite support endpoint when an intermediate scale/shape quotient overflows. + private double FiniteShapeEndpoint() + { + double shift = Alpha / Kappa; + return double.IsInfinity(shift) && Math.Sign(Xi) != Math.Sign(shift) + ? (Xi * Kappa + Alpha) / Kappa : Xi + shift; + } + + /// Inverts the exact Hosking transformation, including compensated endpoint residuals. + private double LatentLogistic(double x) + { + double y = DistributionNumerics.Standardize(x, Xi, Alpha); + if (Kappa == 0) return y; + double product = -Kappa * y; + if (product < -.9) return -KappaFourBoundary.LogT(x, Xi, Alpha, Kappa); + return DistributionNumerics.HoskingShapeTransform(x, Xi, Alpha, Kappa); + } + + /// + public override double PDF(double x) => Math.Exp(LogPDF(x)); + /// - public override double PDF(double x) + /// Evaluates log density directly and preserves the one-sided infinite density at singular support endpoints. + public override double LogPDF(double x) { - if (_parametersValid == false) - ValidateParameters(Xi, Alpha, Kappa, true); - if (x < Minimum || x > Maximum) return 0.0d; - double y = (x - Xi) / Alpha; - if (Math.Abs(Kappa) > NearZero) - y = -Math.Log(1d - Kappa * y) / Kappa; - return 1d / Alpha * Math.Exp(-(1d - Kappa) * y) / Math.Pow(1d + Math.Exp(-y), 2d); + if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); + if (double.IsNaN(x)) return double.NaN; + if (double.IsInfinity(x) || x < Minimum || x > Maximum) return double.NegativeInfinity; + if (x == Minimum || x == Maximum) + return Math.Abs(Kappa) < 1 ? double.NegativeInfinity : Math.Abs(Kappa) == 1 ? -Math.Log(Alpha) : double.PositiveInfinity; + double z = LatentLogistic(x); + return (z >= 0 ? (Kappa - 1) * z - 2 * Tools.Log1p(Math.Exp(-z)) + : (Kappa + 1) * z - 2 * Tools.Log1p(Math.Exp(z))) - Math.Log(Alpha); } /// - public override double CDF(double x) + public override double CDF(double x) => Math.Exp(LogCDF(x)); + + /// + public override double LogCDF(double x) + { + if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); + if (x <= Minimum) return double.NegativeInfinity; + if (x >= Maximum) return 0; + double z = LatentLogistic(x); + return z <= 0 ? z - Tools.Log1p(Math.Exp(z)) : -Tools.Log1p(Math.Exp(-z)); + } + + /// + public override double CCDF(double x) => Math.Exp(LogCCDF(x)); + + /// + public override double LogCCDF(double x) { - if (_parametersValid == false) - ValidateParameters(Xi, Alpha, Kappa, true); - if (x <= Minimum) - return 0d; - if (x >= Maximum) - return 1d; - double y = (x - Xi) / Alpha; - if (Math.Abs(Kappa) > NearZero) - y = -Math.Log(1d - Kappa * y) / Kappa; - return 1d / (1d + Math.Exp(-y)); + if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); + if (x <= Minimum) return 0; + if (x >= Maximum) return double.NegativeInfinity; + double z = LatentLogistic(x); + return z >= 0 ? -z - Tools.Log1p(Math.Exp(-z)) : -Tools.Log1p(Math.Exp(z)); } /// public override double InverseCDF(double probability) { - // Validate probability - if (probability < 0.0d || probability > 1.0d) - throw new ArgumentOutOfRangeException("probability", "Probability must be between 0 and 1."); - if (probability == 0.0d) - return Minimum; - if (probability == 1.0d) - return Maximum; - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Xi, Alpha, Kappa, true); - if (Math.Abs(Kappa) <= NearZero) - { - return Xi - Alpha * Math.Log((1d - probability) / probability); - } - else + if (!(probability >= 0 && probability <= 1)) + throw new ArgumentOutOfRangeException(nameof(probability), "Probability must be between zero and one."); + if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); + if (probability == 0) return Minimum; + if (probability == 1) return Maximum; + double z = Math.Log(probability) - Tools.Log1p(-probability); + return QuantileAtLatent(z); + } + + /// Combines shape exponentials and physical scale before exponentiation or affine addition. + private double QuantileAtLatent(double z) + { + double v = -Kappa * z; + double standard = v > 50 ? -Math.Sign(Kappa) * Math.Exp(v - Math.Log(Math.Abs(Kappa))) + : v < -50 ? 1 / Kappa : z * DistributionNumerics.Exprel(v); + double offset = v > 50 ? -Math.Sign(Kappa) * Math.Exp(Math.Log(Alpha) + v - Math.Log(Math.Abs(Kappa))) + : Alpha * standard; + double value = Xi + offset; + if (double.IsInfinity(value) && DistributionNumerics.IsFinite(standard)) { - return Xi + Alpha / Kappa * (1d - Math.Pow((1d - probability) / probability, Kappa)); + double combined = Xi / Alpha + standard; + if (DistributionNumerics.IsFinite(combined)) return Alpha * combined; } + return value; } /// - public override UnivariateDistributionBase Clone() - { - return new GeneralizedLogistic(Xi, Alpha, Kappa); - } + public override UnivariateDistributionBase Clone() => new GeneralizedLogistic(Xi, Alpha, Kappa); /// + /// + /// Local asymptotic MLE covariance in xi, alpha, kappa order. Regular information + /// requires absolute kappa below one half; this condition does not restrict distribution + /// validity or assert existence of a finite global MLE. + /// + /// Sample size, scale or information regularity is invalid. + /// Numerical information or its inversion cannot be resolved. + /// The requested estimator is not maximum likelihood. public double[,] ParameterCovariance(int sampleSize, ParameterEstimationMethod estimationMethod) { - throw new NotImplementedException(); + DistributionNumerics.ValidateSampleSize(sampleSize); + if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); + if (estimationMethod != ParameterEstimationMethod.MaximumLikelihood) + throw new NotImplementedException("Generalized-logistic covariance is implemented only for local maximum-likelihood uncertainty."); + return KappaExpectedInformation.ParameterCovariance(Alpha, Kappa, -1, sampleSize, 3); } /// + /// Applies the local-MLE delta method in common physical quantile coordinates, + /// avoiding underflow or overflow from forming physical covariance entries first. public double QuantileVariance(double probability, int sampleSize, ParameterEstimationMethod estimationMethod) { - throw new NotImplementedException(); + DistributionNumerics.ValidateProbability(probability); + if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); + var unit = new GeneralizedLogistic(0, 1, Kappa); + double[,] covariance = unit.ParameterCovariance(sampleSize, estimationMethod); + double z = Math.Log(probability) - Tools.Log1p(-probability), argument = -Kappa * z; + double scaleGradient = DistributionNumerics.ScaledExprelProduct(Alpha, z, argument); + double shapeGradient = -DistributionNumerics.ScaledExprelDerivativeProduct(Alpha, z, argument); + return DistributionNumerics.ScaledQuantileVariance(covariance, [Alpha, scaleGradient, shapeGradient]); } /// + /// The analytic kappa derivative is continuous at zero and equals -alpha*logit(p)^2/2 there. public double[] QuantileGradient(double probability) { - if (_parametersValid == false) - ValidateParameters(Xi, _alpha, Kappa, true); - double a = Alpha; - double k = Kappa; - var gradient = new double[3]; - gradient[0] = 1.0d; // location - gradient[1] = 1d / k * (1d - Math.Pow((1d - probability) / probability, k)); // scale - gradient[2] = -(a / (k * k)) * (1d - Math.Pow((1d - probability) / probability, k)) - a / k * Math.Pow((1d - probability) / probability, k) * Math.Log((1d - probability) / probability); // shape - return gradient; + DistributionNumerics.ValidateProbability(probability); + if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); + double z = Math.Log(probability) - Tools.Log1p(-probability), v = -Kappa * z; + double shape = v < -50 ? -Math.Exp(Math.Log(Alpha) - 2 * Math.Log(Math.Abs(Kappa))) + : v > 50 ? -Math.Exp(Math.Log(Alpha) + v + Math.Log(v - 1) - 2 * Math.Log(Math.Abs(Kappa))) + : -Alpha * (z * z * DistributionNumerics.ExprelDerivative(v)); + double scale = v > 50 ? -Math.Sign(Kappa) * Math.Exp(v - Math.Log(Math.Abs(Kappa))) + : v < -50 ? 1 / Kappa : z * DistributionNumerics.Exprel(v); + return [1, scale, shape]; } /// public double[,] QuantileJacobian(IList probabilities, out double determinant) - { - if (probabilities.Count != NumberOfParameters) - { - throw new ArgumentOutOfRangeException(nameof(probabilities), "The number of probabilities must be the same length as the number of distribution parameters."); - } - // Get gradients - var dQp1 = QuantileGradient(probabilities[0]); - var dQp2 = QuantileGradient(probabilities[1]); - var dQp3 = QuantileGradient(probabilities[2]); - // Compute determinant - // |a b c| - // |d e f| - // |g h i| - // |A| = a(ei − fh) − b(di − fg) + c(dh − eg) - double a = dQp1[0]; - double b = dQp1[1]; - double c = dQp1[2]; - double d = dQp2[0]; - double e = dQp2[1]; - double f = dQp2[2]; - double g = dQp3[0]; - double h = dQp3[1]; - double i = dQp3[2]; - determinant = a * (e * i - f * h) - b * (d * i - f * g) + c * (d * h - e * g); - // Return Jacobian - var jacobian = new double[,] { { a, b, c }, { d, e, f }, { g, h, i } }; - return jacobian; - } - + => DistributionNumerics.QuantileJacobian(this, probabilities, out determinant); } -} \ No newline at end of file +} diff --git a/Numerics/Distributions/Univariate/GeneralizedNormal.cs b/Numerics/Distributions/Univariate/GeneralizedNormal.cs index 8a62cee5..9d8fd375 100644 --- a/Numerics/Distributions/Univariate/GeneralizedNormal.cs +++ b/Numerics/Distributions/Univariate/GeneralizedNormal.cs @@ -1,7 +1,6 @@ -using System; +using System; using System.Collections.Generic; using Numerics.Data.Statistics; -using Numerics.Mathematics; using Numerics.Mathematics.Optimization; namespace Numerics.Distributions @@ -152,7 +151,7 @@ public override double Mean { if (!_momentsComputed) { - u = CentralMoments(1000); + u = AnalyticalMoments(); _momentsComputed = true; } return u[0]; @@ -170,9 +169,8 @@ public override double Mode { get { - var brent = new BrentSearch(PDF, InverseCDF(0.001), InverseCDF(0.999)); - brent.Maximize(); - return brent.BestParameterSet.Values[0]; + if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); + return QuantileAtLatent(Kappa); } } @@ -183,7 +181,7 @@ public override double StandardDeviation { if (!_momentsComputed) { - u = CentralMoments(1000); + u = AnalyticalMoments(); _momentsComputed = true; } return u[1]; @@ -197,7 +195,7 @@ public override double Skewness { if (!_momentsComputed) { - u = CentralMoments(1000); + u = AnalyticalMoments(); _momentsComputed = true; } return u[2]; @@ -211,7 +209,7 @@ public override double Kurtosis { if (!_momentsComputed) { - u = CentralMoments(1000); + u = AnalyticalMoments(); _momentsComputed = true; } return u[3]; @@ -223,13 +221,13 @@ public override double Minimum { get { - if (Kappa >= -NearZero) + if (Kappa >= 0) { return double.NegativeInfinity; } else { - return Xi + Alpha / Kappa; + return FiniteShapeEndpoint(); } } } @@ -239,13 +237,13 @@ public override double Maximum { get { - if (Kappa <= NearZero) + if (Kappa <= 0) { return double.PositiveInfinity; } else { - return Xi + Alpha / Kappa; + return FiniteShapeEndpoint(); } } } @@ -265,6 +263,7 @@ public override double[] MaximumOfParameters /// public void Estimate(IList sample, ParameterEstimationMethod estimationMethod) { + DistributionNumerics.ValidateSample(sample, 4); if (estimationMethod == ParameterEstimationMethod.MethodOfLinearMoments) { SetParameters(ParametersFromLinearMoments(Statistics.LinearMoments(sample))); @@ -350,6 +349,10 @@ public override void SetParameters(IList parameters) /// public double[] ParametersFromLinearMoments(IList moments) { + if (moments == null) throw new ArgumentNullException(nameof(moments)); + if (moments.Count < 3 || !DistributionNumerics.IsFinite(moments[0]) || !DistributionNumerics.IsFinite(moments[1]) + || moments[1] <= 0 || !DistributionNumerics.IsFinite(moments[2]) || Math.Abs(moments[2]) >= 1) + throw new ArgumentOutOfRangeException(nameof(moments), "Finite L-moments require positive L-scale and absolute L-skewness below one."); double L1 = moments[0]; double L2 = moments[1]; double T3 = moments[2]; @@ -363,8 +366,8 @@ public double[] ParametersFromLinearMoments(IList moments) double F3 = -0.21741801; double kappa = -T3 * (E0 + E1 * Math.Pow(T3, 2d) + E2 * Math.Pow(T3, 4d) + E3 * Math.Pow(T3, 6d)) / (1d + F1 * Math.Pow(T3, 2d) + F2 * Math.Pow(T3, 4d) + F3 * Math.Pow(T3, 6d)); - double alpha = (L2 * kappa * Math.Exp(-(kappa * kappa) / 2d)) / (1d - 2 * Normal.StandardCDF(-kappa / Tools.Sqrt2)); - double xi = L1 - alpha * (1.0d - Math.Exp(kappa * kappa / 2d)) / kappa; + double alpha = L2 / NormalLScale(kappa); + double xi = L1 - new GeneralizedNormal(0, alpha, kappa).Mean; return [xi, alpha, kappa]; } @@ -374,6 +377,7 @@ public double[] LinearMomentsFromParameters(IList parameters) double xi = parameters[0]; double alpha = parameters[1]; double kappa = parameters[2]; + ValidateParameters(xi, alpha, kappa, true); double A0 = 4.8860251 * Math.Pow(10, -1); double A1 = 4.4493076 * Math.Pow(10, -3); @@ -389,10 +393,10 @@ public double[] LinearMomentsFromParameters(IList parameters) double D1 = 8.2325617 * Math.Pow(10, -2); double D2 = 4.2681448 * Math.Pow(10, -3); double D3 = 1.1653690 * Math.Pow(10, -4); - double tau40 = 1.2260172 * Math.Pow(10, -1); + double tau40 = 0.12260171954089095; // 30*asin(1/3)/pi - 9, the exact normal L-kurtosis limit. - double L1 = xi + alpha * (1.0d - Math.Exp(kappa * kappa / 2d)) / kappa; - double L2 = (alpha / kappa) * Math.Exp(kappa * kappa / 2d) * (1d - 2d * Normal.StandardCDF(-kappa / Tools.Sqrt2)); + double L1 = new GeneralizedNormal(xi, alpha, kappa).Mean; + double L2 = alpha * NormalLScale(kappa); double T3 = -kappa * (A0 + A1 * Math.Pow(kappa, 2d) + A2 * Math.Pow(kappa, 4d) + A3 * Math.Pow(kappa, 6d)) / (1d + B1 * Math.Pow(kappa, 2d) + B2 * Math.Pow(kappa, 4d) + B3 * Math.Pow(kappa, 6d)); double T4 = tau40 + Math.Pow(kappa, 2d) * (C0 + C1 * Math.Pow(kappa, 2d) + C2 * Math.Pow(kappa, 4d) + C3 * Math.Pow(kappa, 6d)) / (1d + D1 * Math.Pow(kappa, 2d) + D2 * Math.Pow(kappa, 4d) + D3 * Math.Pow(kappa, 6d)); return [L1, L2, T3, T4]; @@ -401,19 +405,30 @@ public double[] LinearMomentsFromParameters(IList parameters) /// public Tuple GetParameterConstraints(IList sample) { + DistributionNumerics.ValidateSample(sample, 4); // Estimate initial values using the method of moments (a.k.a product moments). var initialVals = new double[NumberOfParameters]; var lowerVals = new double[NumberOfParameters]; var upperVals = new double[NumberOfParameters]; // Get initial values - initialVals = ParametersFromLinearMoments(Statistics.LinearMoments(sample)); + double magnitude = 0; + foreach (double value in sample) magnitude = Math.Max(magnitude, Math.Abs(value)); + var scaled = new double[sample.Count]; + for (int i = 0; i < sample.Count; i++) scaled[i] = sample[i] / magnitude; + double[] moments = Statistics.LinearMoments(scaled); + initialVals = ParametersFromLinearMoments(moments); + initialVals[0] *= magnitude; + initialVals[1] *= magnitude; + var candidate = new GeneralizedNormal(initialVals[0], initialVals[1], initialVals[2]); + if (!candidate.ParametersValid || !DistributionNumerics.IsFinite(candidate.LogLikelihood(sample))) + initialVals = [moments[0] * magnitude, moments[1] * Math.Sqrt(Math.PI) * magnitude, 0]; // Get bounds of location - if (initialVals[0] == 0d) initialVals[0] = Tools.DoubleMachineEpsilon; - lowerVals[0] = -Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); - upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); + double locationMagnitude = Math.Max(Math.Abs(initialVals[0]), initialVals[1]); + lowerVals[0] = -FiniteDecimalBound(locationMagnitude); + upperVals[0] = -lowerVals[0]; // Get bounds of scale - lowerVals[1] = Tools.DoubleMachineEpsilon; - upperVals[1] = Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[1]))) + 1d); + lowerVals[1] = Math.Min(Tools.DoubleMachineEpsilon, initialVals[1] / 10); + upperVals[1] = FiniteDecimalBound(initialVals[1]); // Get bounds of shape lowerVals[2] = -10d; upperVals[2] = 10d; @@ -422,6 +437,11 @@ public Tuple GetParameterConstraints(IList { initialVals[2] = 0d; } + candidate.SetParameters(initialVals); + if (!candidate.ParametersValid || !DistributionNumerics.IsFinite(candidate.LogLikelihood(sample)) + || initialVals[0] <= lowerVals[0] || initialVals[0] >= upperVals[0] + || initialVals[1] <= lowerVals[1] || initialVals[1] >= upperVals[1]) + throw new InvalidOperationException("The sample does not admit a finite supported generalized-normal initializer within finite bounds."); return new Tuple(initialVals, lowerVals, upperVals); } @@ -444,121 +464,277 @@ double logLH(double[] x) var solver = new NelderMead(logLH, NumberOfParameters, Initials, Lowers, Uppers); solver.ReportFailure = true; solver.Maximize(); + if (solver.Status != OptimizationStatus.Success + || ValidateParameters(solver.BestParameterSet.Values, false) != null + || !DistributionNumerics.IsFinite(new GeneralizedNormal(solver.BestParameterSet.Values[0], solver.BestParameterSet.Values[1], solver.BestParameterSet.Values[2]).LogLikelihood(sample))) + throw new InvalidOperationException($"Generalized normal maximum likelihood estimation failed with optimizer status {solver.Status} or a nonfinite fit."); return solver.BestParameterSet.Values; } + /// Analytical shifted-lognormal moments, preserving representable scale products. + private double[] AnalyticalMoments() + { + if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); + if (Kappa == 0) return [Xi, Alpha, 0, 3]; + double v = Kappa * Kappa; + double t = Tools.Expm1(v); + double mean, sd, skew; + if (v <= .5) + { + double relative = DistributionNumerics.Exprel(v); + mean = Xi - Alpha * Kappa * .5 * DistributionNumerics.Exprel(.5 * v); + sd = Alpha * Math.Exp(.5 * v) * Math.Sqrt(relative); + skew = -Kappa * (t + 3) * Math.Sqrt(relative); + } + else + { + double logt = v > 36 ? v + Tools.Log1p(-Math.Exp(-v)) : Math.Log(t); + double loghalf = v > 72 ? v / 2 + Tools.Log1p(-Math.Exp(-v / 2)) : Math.Log(Tools.Expm1(v / 2)); + mean = Xi - Math.Sign(Kappa) * Math.Exp(Math.Log(Alpha) + loghalf - Math.Log(Math.Abs(Kappa))); + sd = Math.Exp(Math.Log(Alpha) + v / 2 + logt / 2 - Math.Log(Math.Abs(Kappa))); + double logsum = v > 36 ? v + Tools.Log1p(2 * Math.Exp(-v)) : Math.Log(t + 3); + skew = -Math.Sign(Kappa) * Math.Exp(logsum + logt / 2); + } + return [mean, sd, skew, 3 + t * (16 + t * (15 + t * (6 + t)))]; + } + + /// Returns unit-scale L-scale with the exact kappa=0 normal limit. + private static double NormalLScale(double k) + { + double v = k * k; + if (Math.Abs(k) < .5) + { + double sum = 1, power = 1; + for (int n = 1; n < 24; n++) + { + power *= -v / (4 * n); + double term = power / (2 * n + 1); + sum += term; + if (Math.Abs(term) < Math.Abs(sum) * 1E-17) break; + } + return Math.Exp(v / 2) * sum / Math.Sqrt(Math.PI); + } + return Math.Exp(v / 2) * (1 - 2 * Normal.StandardCDF(-Math.Abs(k) / Tools.Sqrt2)) / Math.Abs(k); + } + + /// Retains the existing decimal-order bound construction while preventing overflow. + private static double FiniteDecimalBound(double value) + { + double bound = Math.Pow(10, Math.Ceiling(Math.Log10(value)) + 1); + return double.IsPositiveInfinity(bound) ? double.MaxValue : bound; + } + + /// Retains a finite support endpoint when an intermediate scale/shape quotient overflows. + private double FiniteShapeEndpoint() + { + double shift = Alpha / Kappa; + return double.IsInfinity(shift) && Math.Sign(Xi) != Math.Sign(shift) + ? (Xi * Kappa + Alpha) / Kappa : Xi + shift; + } + + /// Inverts the Hosking shape transform, retaining its exact nonzero shape and support residual. + private double LatentNormal(double x) + { + double y = DistributionNumerics.Standardize(x, Xi, Alpha); + if (Kappa == 0) return y; + double product = -Kappa * y; + if (product < -.9) return -KappaFourBoundary.LogT(x, Xi, Alpha, Kappa); + return DistributionNumerics.HoskingShapeTransform(x, Xi, Alpha, Kappa); + } + + /// + public override double PDF(double x) => Math.Exp(LogPDF(x)); + /// - public override double PDF(double x) + /// Evaluates the transformed-normal log density directly, including underflowing densities. + public override double LogPDF(double x) { - if (_parametersValid == false) - ValidateParameters(Xi, Alpha, Kappa, true); - if (x < Minimum || x > Maximum) return 0.0d; - double y = (x - Xi) / Alpha; - if (Math.Abs(Kappa) > NearZero) - y = -Math.Log(1d - Kappa * y) / Kappa; - return 1d / Alpha * Math.Exp(Kappa * y - y * y / 2d) / Tools.Sqrt2PI; + if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); + if (double.IsNaN(x)) return double.NaN; + if (double.IsInfinity(x) || x <= Minimum || x >= Maximum) return double.NegativeInfinity; + double z = LatentNormal(x); + return -Math.Log(Alpha) - Tools.LogSqrt2PI + z * (Kappa - z / 2); } /// - public override double CDF(double x) + public override double CDF(double x) => Math.Exp(LogCDF(x)); + + /// + public override double LogCDF(double x) { - if (_parametersValid == false) - ValidateParameters(Xi, Alpha, Kappa, true); - if (x <= Minimum) - return 0d; - if (x >= Maximum) - return 1d; - double y = (x - Xi) / Alpha; - if (Math.Abs(Kappa) > NearZero) - y = -Math.Log(1d - Kappa * y) / Kappa; - return Normal.StandardCDF(y); + if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); + if (x <= Minimum) return double.NegativeInfinity; + if (x >= Maximum) return 0; + return DistributionNumerics.NormalLogCDF(LatentNormal(x)); } /// - public override double InverseCDF(double probability) + public override double CCDF(double x) => Math.Exp(LogCCDF(x)); + + /// + public override double LogCCDF(double x) { - // Validate probability - if (probability < 0.0d || probability > 1.0d) - throw new ArgumentOutOfRangeException("probability", "Probability must be between 0 and 1."); - if (probability == 0.0d) - return Minimum; - if (probability == 1.0d) - return Maximum; - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Xi, Alpha, Kappa, true); - if (Math.Abs(Kappa) <= NearZero) - { - return Xi + Alpha * Normal.StandardZ(probability); - } - else - { - return Xi - Alpha / Kappa * (Math.Exp(-Kappa * Normal.StandardZ(probability)) - 1d); - } + if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); + if (x <= Minimum) return 0; + if (x >= Maximum) return double.NegativeInfinity; + return DistributionNumerics.NormalLogSurvival(LatentNormal(x)); } /// - public override UnivariateDistributionBase Clone() + public override double InverseCDF(double probability) + { + if (!(probability >= 0 && probability <= 1)) + throw new ArgumentOutOfRangeException(nameof(probability), "Probability must be between zero and one."); + if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); + if (probability == 0) return Minimum; + if (probability == 1) return Maximum; + return QuantileAtLatent(Normal.StandardZ(probability)); + } + + /// Combines shape exponentials and physical scale before exponentiation or affine addition. + private double QuantileAtLatent(double z) { - return new GeneralizedNormal(Xi, Alpha, Kappa); + double v = -Kappa * z; + double standard = v > 50 ? -Math.Sign(Kappa) * Math.Exp(v - Math.Log(Math.Abs(Kappa))) + : v < -50 ? 1 / Kappa : z * DistributionNumerics.Exprel(v); + double offset = v > 50 ? -Math.Sign(Kappa) * Math.Exp(Math.Log(Alpha) + v - Math.Log(Math.Abs(Kappa))) + : Alpha * standard; + double value = Xi + offset; + if (double.IsInfinity(value) && DistributionNumerics.IsFinite(standard)) + { + double combined = Xi / Alpha + standard; + if (DistributionNumerics.IsFinite(combined)) return Alpha * combined; + } + return value; } /// + public override UnivariateDistributionBase Clone() => new GeneralizedNormal(Xi, Alpha, Kappa); + /// + /// + /// Local asymptotic MLE covariance for all three estimated parameters in xi, alpha, + /// kappa order. Information is finite for every finite kappa. This does not assert + /// existence of a finite global MLE for the three-parameter lognormal likelihood. + /// + /// The sample size or distribution parameters are invalid. + /// The requested estimator is not maximum likelihood. public double[,] ParameterCovariance(int sampleSize, ParameterEstimationMethod estimationMethod) { - throw new NotImplementedException(); + DistributionNumerics.ValidateSampleSize(sampleSize); + if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); + if (estimationMethod != ParameterEstimationMethod.MaximumLikelihood) + throw new NotImplementedException("Generalized-normal covariance is implemented only for local maximum-likelihood uncertainty."); + NormalInformationFactors(Kappa, out double c, out double inverseR, out double logInverseR); + double scale = Alpha / Math.Sqrt(sampleSize), shapedScale = Alpha * Kappa / Math.Sqrt(sampleSize); + double logAlpha = Math.Log(Alpha), logN = Math.Log(sampleSize), logK = Math.Log(Math.Abs(Kappa)); + var covariance = new double[3, 3]; + covariance[0, 0] = scale * scale * (1 + c * c * inverseR); + covariance[1, 1] = shapedScale * shapedScale + scale * scale / 2; + covariance[2, 2] = (Kappa / 2 / sampleSize) * Kappa + inverseR / sampleSize; + covariance[0, 1] = -scale * shapedScale; + if (!DistributionNumerics.IsFinite(covariance[0, 1]) || covariance[0, 1] == 0) + covariance[0, 1] = -Math.Sign(Kappa) * Math.Exp(2 * logAlpha + logK - logN); + covariance[0, 2] = c * inverseR * Alpha / sampleSize; + if (c > 0 && (covariance[0, 2] == 0 || !DistributionNumerics.IsFinite(covariance[0, 2]))) + covariance[0, 2] = Math.Exp(logAlpha + Math.Log(c) + logInverseR - logN); + covariance[1, 2] = Alpha * Kappa / 2 / sampleSize; + if (!DistributionNumerics.IsFinite(covariance[1, 2]) || covariance[1, 2] == 0) + covariance[1, 2] = Math.Sign(Kappa) * Math.Exp(logAlpha + logK - Math.Log(2) - logN); + covariance[1, 0] = covariance[0, 1]; + covariance[2, 0] = covariance[0, 2]; + covariance[2, 1] = covariance[1, 2]; + return covariance; + } + + /// Evaluates the closed-form information factors, retaining log(1/R) after underflow. + private static void NormalInformationFactors(double kappa, out double c, out double inverseR, out double logInverseR) + { + double v = kappa * kappa; + c = .5 * DistributionNumerics.Exprel(-v / 2); + if (v < .1) + { + double sum = 1.5, term = 1.5; + for (int m = 2; m < 24; m++) + { + term *= v * (m + 2d) / (m + 1) / (m + 1); + sum += term; + if (Math.Abs(term) < Math.Abs(sum) * 1E-17) break; + } + inverseR = 1 / sum; + logInverseR = -Math.Log(sum); + } + else + { + logInverseR = double.IsPositiveInfinity(v) ? double.NegativeInfinity : 2 * Math.Log(v) - v + - (v > 350 ? Tools.Log1p(v) : Math.Log(1 + v - (1 + 2 * v) * Math.Exp(-v))); + inverseR = Math.Exp(logInverseR); + } + if (double.IsPositiveInfinity(v)) c = 0; } /// + /// + /// Applies the local-MLE delta method in public parameter coordinates. An exact positive + /// factorization of the covariance avoids cancellation near a finite shape endpoint. + /// Scale and exponential factors are combined before the scalar variance is exponentiated. + /// public double QuantileVariance(double probability, int sampleSize, ParameterEstimationMethod estimationMethod) { - throw new NotImplementedException(); + DistributionNumerics.ValidateProbability(probability); + DistributionNumerics.ValidateSampleSize(sampleSize); + if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); + if (estimationMethod != ParameterEstimationMethod.MaximumLikelihood) + throw new NotImplementedException("Generalized-normal covariance is implemented only for local maximum-likelihood uncertainty."); + NormalInformationFactors(Kappa, out double c, out _, out double logInverseR); + double z = Normal.StandardZ(probability), argument = -Kappa * z; + double logScale = Math.Log(Alpha) - .5 * Math.Log(sampleSize); + // C = a*a' + b*b'/2 + d*d'/R, with a=[1,-k,0], b=[0,1,k], d=[c,0,1]. + // For g=[1,S,T], g'a=exp(-k*z) and g'b=z*exp(-k*z). + double first = 2 * (logScale + argument) + Tools.Log1p(z * z / 2); + if (double.IsPositiveInfinity(first)) return double.PositiveInfinity; + double logResidual; + if (Math.Abs(Kappa) < .1) + { + double residual = c - z * z * DistributionNumerics.ExprelDerivative(argument); + logResidual = Math.Log(Math.Abs(residual)); + } + else + { + // c+T = ((1-argument)*exp(argument)-exp(-k*k/2))/(k*k). + // Retain this difference even when c and T individually round to +/-1/(k*k). + double negative = -(Kappa / 2) * Kappa; + double numerator; + if (argument > 1) + numerator = DistributionNumerics.LogSum(argument + Math.Log(argument - 1), negative); + else + { + double positive = argument == 1 || double.IsNegativeInfinity(argument) + ? double.NegativeInfinity : argument + Tools.Log1p(-argument); + numerator = DistributionNumerics.LogDifference(Math.Max(positive, negative), Math.Min(positive, negative)); + } + logResidual = numerator - 2 * Math.Log(Math.Abs(Kappa)); + } + double second = 2 * (logScale + logResidual) + logInverseR; + return Math.Exp(DistributionNumerics.LogSum(first, second)); } /// + /// The analytical shape derivative is continuous at kappa=0 and equals -alpha*z*z/2 there. public double[] QuantileGradient(double probability) { - if (_parametersValid == false) - ValidateParameters(Xi, _alpha, Kappa, true); - - var gradient = NumericalDerivative.Gradient(x => - { - var gno = new GeneralizedNormal(); - gno.SetParameters(x); - return gno.InverseCDF(probability); - }, GetParameters); - return gradient; + DistributionNumerics.ValidateProbability(probability); + if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); + double z = Normal.StandardZ(probability), v = -Kappa * z; + double shape = v < -50 ? -Math.Exp(Math.Log(Alpha) - 2 * Math.Log(Math.Abs(Kappa))) + : v > 50 ? -Math.Exp(Math.Log(Alpha) + v + Math.Log(v - 1) - 2 * Math.Log(Math.Abs(Kappa))) + : -Alpha * (z * z * DistributionNumerics.ExprelDerivative(v)); + double scale = v > 50 ? -Math.Sign(Kappa) * Math.Exp(v - Math.Log(Math.Abs(Kappa))) + : v < -50 ? 1 / Kappa : z * DistributionNumerics.Exprel(v); + return [1, scale, shape]; } /// public double[,] QuantileJacobian(IList probabilities, out double determinant) - { - if (probabilities.Count != NumberOfParameters) - { - throw new ArgumentOutOfRangeException(nameof(probabilities), "The number of probabilities must be the same length as the number of distribution parameters."); - } - // Get gradients - var dQp1 = QuantileGradient(probabilities[0]); - var dQp2 = QuantileGradient(probabilities[1]); - var dQp3 = QuantileGradient(probabilities[2]); - // Compute determinant - // |a b c| - // |d e f| - // |g h i| - // |A| = a(ei − fh) − b(di − fg) + c(dh − eg) - double a = dQp1[0]; - double b = dQp1[1]; - double c = dQp1[2]; - double d = dQp2[0]; - double e = dQp2[1]; - double f = dQp2[2]; - double g = dQp3[0]; - double h = dQp3[1]; - double i = dQp3[2]; - determinant = a * (e * i - f * h) - b * (d * i - f * g) + c * (d * h - e * g); - // Return Jacobian - var jacobian = new double[,] { { a, b, c }, { d, e, f }, { g, h, i } }; - return jacobian; - } + => DistributionNumerics.QuantileJacobian(this, probabilities, out determinant); } } diff --git a/Numerics/Distributions/Univariate/GeneralizedPareto.cs b/Numerics/Distributions/Univariate/GeneralizedPareto.cs index 9a46d154..104c83db 100644 --- a/Numerics/Distributions/Univariate/GeneralizedPareto.cs +++ b/Numerics/Distributions/Univariate/GeneralizedPareto.cs @@ -154,88 +154,46 @@ public override double[] GetParameters } /// + /// The mean exists for kappa > -1; bounded positive shapes are not excluded. public override double Mean { - get - { - if (Math.Abs(Kappa) <= NearZero) - { - return Xi + Alpha; - } - else if (Math.Abs(Kappa) < 1d) - { - return Xi + Alpha / (1d + Kappa); - } - else - { - return double.NaN; - } - } + get { return Kappa > -1 ? Xi + Alpha / (1 + Kappa) : double.NaN; } } /// public override double Median { - get - { - if (Math.Abs(Kappa) <= NearZero) - { - return Xi - Math.Log(0.5d) * Alpha; - } - else - { - return Xi + Alpha * (Math.Pow(2.0d, -Kappa) - 1d) / Kappa; - } - } + get { return InverseCDF(.5); } } /// + /// The lower endpoint is the mode for kappa < 1, the uniform kappa=1 case has no + /// unique mode and returns NaN, and kappa > 1 has its mode at the upper endpoint. public override double Mode { - get - { - if (Math.Abs(Kappa) <= NearZero) - { - return Xi; - } - else - { - return Xi + Alpha * (Math.Pow(1d + Kappa, -Kappa) - 1d) / Kappa; - } - } + get { return Kappa < 1 ? Xi : Kappa == 1 ? double.NaN : Maximum; } } /// + /// The variance exists for kappa > -1/2; scale is applied after standardization. public override double StandardDeviation { - get - { - if (Math.Abs(Kappa) <= NearZero) - { - return Alpha; - } - else if (Math.Abs(Kappa) < 0.5d) - { - return Math.Sqrt(Math.Pow(Alpha, 2d) / ((1d + 2d * Kappa) * Math.Pow(1d + Kappa, 2d))); - } - else - { - return double.NaN; - } - } + get { return Kappa > -.5 ? (Alpha / (1 + Kappa)) / Math.Sqrt(1 + 2 * Kappa) : double.NaN; } } /// + /// The third moment exists for kappa > -1/3. public override double Skewness { get { - if (Math.Abs(Kappa) <= NearZero) - { - return 2.0d; - } - else if (Math.Abs(Kappa) < 1d / 3d) + if (Kappa > -1d / 3d) { + if (Kappa > 1) + { + double inverse = 1 / Kappa; + return (2 * (inverse - 1) / (inverse + 3)) * Math.Sqrt(Kappa) * Math.Sqrt(inverse + 2); + } double num = 2d * (1d - Kappa) * Math.Sqrt(1d + 2d * Kappa); double den = 1d + 3d * Kappa; return num / den; @@ -248,16 +206,20 @@ public override double Skewness } /// + /// Ordinary kurtosis exists for kappa > -1/4. public override double Kurtosis { get { - if (Math.Abs(Kappa) <= NearZero) - { - return 9.0d; - } - else if (Math.Abs(Kappa) < 0.25d) + if (Kappa > -.25) { + if (Kappa > 1) + { + double inverse = 1 / Kappa; + double coefficient = 3 * (inverse + 2) * (3 * inverse * inverse - inverse + 2) + / ((inverse + 3) * (inverse + 4)); + return Kappa * coefficient; + } double num = 3d * (1d + 2d * Kappa) * (3d - Kappa + 2d * Math.Pow(Kappa, 2d)); double den = (1d + 3d * Kappa) * (1d + 4d * Kappa); return num / den; @@ -280,7 +242,7 @@ public override double Maximum { get { - if (Kappa <= NearZero) + if (Kappa <= 0) { return double.PositiveInfinity; } @@ -307,6 +269,7 @@ public override double[] MaximumOfParameters /// public void Estimate(IList sample, ParameterEstimationMethod estimationMethod) { + DistributionNumerics.ValidateSample(sample, 4); if (estimationMethod == ParameterEstimationMethod.MethodOfMoments) { SetParameters(DirectMethodOfMoments(Statistics.ProductMoments(sample))); @@ -353,6 +316,8 @@ public void SetParameters(double location, double scale, double shape) /// public override void SetParameters(IList parameters) { + if (parameters == null || parameters.Count != NumberOfParameters) + throw new ArgumentOutOfRangeException(nameof(parameters), "Exactly three parameters are required."); SetParameters(parameters[0], parameters[1], parameters[2]); } @@ -388,6 +353,12 @@ public override void SetParameters(IList parameters) /// public override ArgumentOutOfRangeException? ValidateParameters(IList parameters, bool throwException) { + if (parameters == null || parameters.Count != NumberOfParameters) + { + var exception = new ArgumentOutOfRangeException(nameof(parameters), "Exactly three parameters are required."); + if (throwException) throw exception; + return exception; + } return ValidateParameters(parameters[0], parameters[1], parameters[2], throwException); } @@ -430,6 +401,7 @@ public double[] ParametersFromMoments(IList moments) /// public double[] MomentsFromParameters(IList parameters) { + ValidateParameters(parameters, true); var dist = new GeneralizedPareto(); dist.SetParameters(parameters); var m1 = dist.Mean; @@ -499,6 +471,7 @@ public double[] ParametersFromLinearMoments(IList moments) /// public double[] LinearMomentsFromParameters(IList parameters) { + ValidateParameters(parameters, true); double xi = parameters[0]; double alpha = parameters[1]; double kappa = parameters[2]; @@ -512,36 +485,29 @@ public double[] LinearMomentsFromParameters(IList parameters) } /// + /// Requires at least four finite, nonconstant observations. The existing linear- + /// moment initialization is rescaled algebraically; the upper location bound is the sample minimum. + /// The sample or a finite feasible initialization is invalid. public Tuple GetParameterConstraints(IList sample) { - var initialVals = new double[NumberOfParameters]; + DistributionNumerics.ValidateSample(sample, 4); var lowerVals = new double[NumberOfParameters]; var upperVals = new double[NumberOfParameters]; - // - // Get initial values - initialVals = ParametersFromLinearMoments(Statistics.LinearMoments(sample)); + double normalization = DistributionNumerics.InitializationScale(sample); + var normalized = new double[sample.Count]; + for (int i = 0; i < sample.Count; i++) normalized[i] = sample[i] / normalization; + var initialVals = ParametersFromLinearMoments(Statistics.LinearMoments(normalized)); + initialVals[0] *= normalization; + initialVals[1] *= normalization; double minData = Statistics.Minimum(sample); - // Get bounds of location - if (initialVals[0] == 0d) initialVals[0] = Tools.DoubleMachineEpsilon; - lowerVals[0] = initialVals[0] - Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])))); - upperVals[0] = minData + Tools.DoubleMachineEpsilon; - - // Get bounds of scale - lowerVals[1] = Tools.DoubleMachineEpsilon; - upperVals[1] = Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[1])) + 1d)); + DistributionNumerics.LocationParameterBounds(ref initialVals[0], initialVals[1], minData, + Statistics.Maximum(sample), true, out lowerVals[0], out upperVals[0]); + DistributionNumerics.PositiveParameterBounds(initialVals[1], out lowerVals[1], out upperVals[1]); // Get bounds of shape lowerVals[2] = -10d; upperVals[2] = 10d; // Correct initial values if necessary - if (initialVals[0] <= lowerVals[0] || initialVals[0] >= upperVals[0]) - { - initialVals[0] = Statistics.Mean(new[] { lowerVals[0], upperVals[0] }); - } - if (initialVals[1] <= lowerVals[1] || initialVals[1] >= upperVals[1]) - { - initialVals[1] = Statistics.Mean(new[] { lowerVals[1], upperVals[1] }); - } - if (initialVals[2] <= lowerVals[2] || initialVals[2] >= upperVals[2]) + if (!DistributionNumerics.IsFinite(initialVals[2]) || initialVals[2] <= lowerVals[2] || initialVals[2] >= upperVals[2]) { initialVals[2] = 0d; } @@ -576,37 +542,53 @@ double logLH(double[] x) /// public override double PDF(double x) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Xi, Alpha, Kappa, true); - if (x < Minimum || x > Maximum) return 0.0d; - double y = (x - Xi) / Alpha; - if (Math.Abs(Kappa) > NearZero) - y = -Math.Log(1d - Kappa * y) / Kappa; - return Math.Exp(-(1d - Kappa) * y) / Alpha; + return Math.Exp(LogPDF(x)); + } + + /// + /// Uses the exact nonzero shape and retains one-sided finite-endpoint density limits. + public override double LogPDF(double x) + { + if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); + if (x < Minimum || x > Maximum || double.IsInfinity(x)) return double.NegativeInfinity; + if (Kappa > 0 && x == Maximum) + return Kappa < 1 ? double.NegativeInfinity : Kappa == 1 ? -Math.Log(Alpha) : double.PositiveInfinity; + double value = -(1 - Kappa) * TransformedValue(x) - Math.Log(Alpha); + return double.IsNaN(value) ? double.NegativeInfinity : value; + } + + /// Maps an interior observation to its exponential coordinate without a shape-zero plateau. + private double TransformedValue(double x) + { + return DistributionNumerics.HoskingShapeTransform(x, Xi, Alpha, Kappa); } /// public override double CDF(double x) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Xi, Alpha, Kappa, true); - if (x <= Minimum) - return 0d; - if (x >= Maximum) - return 1d; - double y = (x - Xi) / Alpha; - if (Math.Abs(Kappa) > NearZero) - y = -Math.Log(1d - Kappa * y) / Kappa; - return 1d - Math.Exp(-y); + return -Tools.Expm1(LogCCDF(x)); + } + + /// + public override double LogCDF(double x) => DistributionNumerics.Log1mExp(LogCCDF(x)); + + /// + public override double CCDF(double x) => Math.Exp(LogCCDF(x)); + + /// + public override double LogCCDF(double x) + { + if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); + if (x <= Minimum) return 0; + if (x >= Maximum) return double.NegativeInfinity; + return -TransformedValue(x); } /// public override double InverseCDF(double probability) { // Validate probability - if (probability < 0.0d || probability > 1.0d) + if (!(probability >= 0.0d && probability <= 1.0d)) throw new ArgumentOutOfRangeException("probability", "Probability must be between 0 and 1."); if (probability == 0.0d) return Minimum; @@ -615,14 +597,12 @@ public override double InverseCDF(double probability) // Validate parameters if (_parametersValid == false) ValidateParameters(Xi, Alpha, Kappa, true); - if (Math.Abs(Kappa) <= NearZero) - { - return Xi - Alpha * Math.Log(1d - probability); - } - else - { - return Xi + Alpha / Kappa * (1d - Math.Pow(1d - probability, Kappa)); - } + double logarithm = Tools.Log1p(-probability); + double product = Kappa * logarithm; + double unitQuantile = double.IsNegativeInfinity(product) ? 1 / Kappa : DistributionNumerics.ScaledExprelProduct(1, -logarithm, product); + double displacement = double.IsNegativeInfinity(product) ? Alpha / Kappa : DistributionNumerics.ScaledExprelProduct(Alpha, -logarithm, product); + return double.IsInfinity(displacement) && DistributionNumerics.IsFinite(unitQuantile) + ? Alpha * (Xi / Alpha + unitQuantile) : Xi + displacement; } /// @@ -632,8 +612,13 @@ public override UnivariateDistributionBase Clone() } /// + /// MLE uncertainty requires kappa < 1/2; MoM requires kappa > -1/4. Location + /// covariance additionally requires sampleSize + 2*kappa > 0. These restrictions do not narrow distribution validity or fitting bounds. + /// Parameters, sample size or the requested uncertainty domain are invalid. + /// The requested estimator combination is unsupported. public double[,] ParameterCovariance(int sampleSize, ParameterEstimationMethod estimationMethod) { + DistributionNumerics.ValidateSampleSize(sampleSize); if (estimationMethod != ParameterEstimationMethod.MethodOfMoments && estimationMethod != ParameterEstimationMethod.MaximumLikelihood) { @@ -641,17 +626,26 @@ public override UnivariateDistributionBase Clone() } // Validate parameters if (_parametersValid == false) - ValidateParameters(new[] { Xi, _alpha }, true); + ValidateParameters(Xi, _alpha, Kappa, true); + if (estimationMethod == ParameterEstimationMethod.MaximumLikelihood && Kappa >= .5) + throw new ArgumentOutOfRangeException(nameof(Kappa), "Regular maximum-likelihood uncertainty requires kappa < 1/2."); + if (estimationMethod == ParameterEstimationMethod.MethodOfMoments && Kappa <= -.25) + throw new ArgumentOutOfRangeException(nameof(Kappa), "Method-of-moments uncertainty requires kappa > -1/4."); double a = Alpha; double k = Kappa; - int N = sampleSize; + double N = sampleSize; + if (!(N + 2 * k > 0)) + throw new ArgumentOutOfRangeException(nameof(sampleSize), "Location covariance requires sampleSize + 2*kappa > 0."); var covar = new double[3, 3]; - covar[0, 0] = N * a * a / ((N + 2d * k) * Math.Pow(N + k, 2d)); // location + double locationScale = a / (N + k); + covar[0, 0] = (N / (N + 2 * k)) * (locationScale * locationScale); // location + double scaleVariance = a / Math.Sqrt(N); + scaleVariance *= scaleVariance; if (estimationMethod == ParameterEstimationMethod.MethodOfMoments) { double num = Math.Pow(1d + k, 2d) * (1d + 6d * k + 12d * Math.Pow(k, 2d)); double den = (1d + 2d * k) * (1d + 3d * k) * (1d + 4d * k); - covar[1, 1] = 2d * a * a / N * num / den; // scale + covar[1, 1] = 2d * scaleVariance * (num / den); // scale // num = Math.Pow(1d + k, 2d) * Math.Pow(1d + 2d * k, 2d) * (1d + k + 6d * Math.Pow(k, 2d)); covar[2, 2] = 1d / N * num / den; // shape @@ -668,7 +662,7 @@ public override UnivariateDistributionBase Clone() } else if (estimationMethod == ParameterEstimationMethod.MaximumLikelihood) { - covar[1, 1] = (1d - k) * (2d * a * a) / N; // scale + covar[1, 1] = (1d - k) * (2d * scaleVariance); // scale covar[2, 2] = 1d / N * Math.Pow(1d - k, 2d); // shape // covar[0, 1] = 0.0; @@ -684,34 +678,40 @@ public override UnivariateDistributionBase Clone() } /// + /// Preserves the documented omission of order-n-to-the-minus-two location uncertainty. + /// The scale/shape covariance is contracted in finite physical common coordinates. Probability must be finite and strictly interior. public double QuantileVariance(double probability, int sampleSize, ParameterEstimationMethod estimationMethod) { + DistributionNumerics.ValidateProbability(probability); + if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); // The variance in the location parameter is of order N-2 and thus // can be neglected relative to the variances of scale and shape (order N-1) // to obtain approximate confidence intervals. - var covar = ParameterCovariance(sampleSize, estimationMethod); - var grad = QuantileGradient(probability); - double varA = covar[1, 1]; - double varB = covar[2, 2]; - double covAB = covar[2, 1]; - double dQx1 = grad[1]; - double dQx2 = grad[2]; - return Math.Pow(dQx1, 2d) * varA + Math.Pow(dQx2, 2d) * varB + 2d * dQx1 * dQx2 * covAB; + var unit = new GeneralizedPareto(0, 1, Kappa); + var covar = unit.ParameterCovariance(sampleSize, estimationMethod); + double logarithm = Tools.Log1p(-probability), product = Kappa * logarithm; + double scaleGradient = DistributionNumerics.ScaledExprelProduct(Alpha, -logarithm, product); + double shapeGradient = -DistributionNumerics.ScaledExprelDerivativeProduct(Alpha, logarithm, product); + var scaleShapeCovariance = new[,] { { covar[1, 1], covar[1, 2] }, { covar[2, 1], covar[2, 2] } }; + return DistributionNumerics.ScaledQuantileVariance(scaleShapeCovariance, [scaleGradient, shapeGradient]); } /// + /// Uses analytical exponential divided differences, including the full nonzero + /// shape sensitivity at kappa=0, with L=log(1-p). + /// Parameters are invalid or probability is not finite and strictly interior. public double[] QuantileGradient(double probability) { + DistributionNumerics.ValidateProbability(probability); if (_parametersValid == false) ValidateParameters(Xi, _alpha, Kappa, true); - double p = probability; - double a = Alpha; - double k = Kappa; + double logarithm = Tools.Log1p(-probability); + double product = Kappa * logarithm; var gradient = new double[] { 1.0d, // location - 1d / k * (1d - Math.Pow(1d - p, Kappa)), // scale - -a / (k * k) * (1d - Math.Pow(1d - p, k)) - a / k * Math.Log(1d - p) * Math.Pow(1d - p, k) // shape + double.IsNegativeInfinity(product) ? 1 / Kappa : DistributionNumerics.ScaledExprelProduct(1, -logarithm, product), // scale + double.IsNegativeInfinity(product) ? -(Alpha / Kappa) / Kappa : -DistributionNumerics.ScaledExprelDerivativeProduct(Alpha, logarithm, product) // shape }; return gradient; } @@ -719,34 +719,8 @@ public double[] QuantileGradient(double probability) /// public double[,] QuantileJacobian(IList probabilities, out double determinant) { - if (probabilities.Count != NumberOfParameters) - { - throw new ArgumentOutOfRangeException(nameof(probabilities), "The number of probabilities must be the same length as the number of distribution parameters."); - } - - // Get gradients - var dQp1 = QuantileGradient(probabilities[0]); - var dQp2 = QuantileGradient(probabilities[1]); - var dQp3 = QuantileGradient(probabilities[2]); - // Compute determinant - // |a b c| - // |d e f| - // |g h i| - // |A| = a(ei − fh) − b(di − fg) + c(dh − eg) - double a = dQp1[0]; - double b = dQp1[1]; - double c = dQp1[2]; - double d = dQp2[0]; - double e = dQp2[1]; - double f = dQp2[2]; - double g = dQp3[0]; - double h = dQp3[1]; - double i = dQp3[2]; - determinant = a * (e * i - f * h) - b * (d * i - f * g) + c * (d * h - e * g); - // Return Jacobian - var jacobian = new double[,] { { a, b, c }, { d, e, f }, { g, h, i } }; - return jacobian; + return DistributionNumerics.QuantileJacobian(this, probabilities, out determinant); } } -} \ No newline at end of file +} diff --git a/Numerics/Distributions/Univariate/Gumbel.cs b/Numerics/Distributions/Univariate/Gumbel.cs index 18d662d3..d4d64edf 100644 --- a/Numerics/Distributions/Univariate/Gumbel.cs +++ b/Numerics/Distributions/Univariate/Gumbel.cs @@ -149,7 +149,7 @@ public override double Mode /// public override double StandardDeviation { - get { return Math.Sqrt(Math.Pow(Math.PI, 2.0d) / 6.0d * Math.Pow(Alpha, 2.0d)); } + get { return Alpha * (Math.PI / Math.Sqrt(6)); } } /// @@ -191,6 +191,7 @@ public override double[] MaximumOfParameters /// public void Estimate(IList sample, ParameterEstimationMethod estimationMethod) { + DistributionNumerics.ValidateSample(sample, 4); if (estimationMethod == ParameterEstimationMethod.MethodOfMoments) { SetParameters(ParametersFromMoments(Statistics.ProductMoments(sample))); @@ -235,6 +236,8 @@ public void SetParameters(double location, double scale) /// public override void SetParameters(IList parameters) { + if (parameters == null || parameters.Count != NumberOfParameters) + throw new ArgumentOutOfRangeException(nameof(parameters), "Exactly two parameters are required."); SetParameters(parameters[0], parameters[1]); } @@ -264,6 +267,12 @@ public override void SetParameters(IList parameters) /// public override ArgumentOutOfRangeException? ValidateParameters(IList parameters, bool throwException) { + if (parameters == null || parameters.Count != NumberOfParameters) + { + var exception = new ArgumentOutOfRangeException(nameof(parameters), "Exactly two parameters are required."); + if (throwException) throw exception; + return exception; + } return ValidateParameters(parameters[0], parameters[1], throwException); } @@ -281,6 +290,7 @@ public double[] ParametersFromMoments(IList moments) /// public double[] MomentsFromParameters(IList parameters) { + ValidateParameters(parameters, true); var dist = new Gumbel(); dist.SetParameters(parameters); var m1 = dist.Mean; @@ -303,6 +313,7 @@ public double[] ParametersFromLinearMoments(IList moments) /// public double[] LinearMomentsFromParameters(IList parameters) { + ValidateParameters(parameters, true); double xi = parameters[0]; double alpha = parameters[1]; double L1 = xi + alpha * Tools.Euler; @@ -313,22 +324,23 @@ public double[] LinearMomentsFromParameters(IList parameters) } /// + /// Requires at least four finite, nonconstant observations and returns finite, + /// scale-aware bounds around the existing linear-moment initialization. + /// The sample or a feasible finite initialization is invalid. public Tuple GetParameterConstraints(IList sample) { - var initialVals = new double[NumberOfParameters]; + DistributionNumerics.ValidateSample(sample, 4); var lowerVals = new double[NumberOfParameters]; var upperVals = new double[NumberOfParameters]; - // - // Get initial values - // initialVals = DirectMethodOfMoments(Statistics.ComputeProductMoments(sample)) - initialVals = ParametersFromLinearMoments(Statistics.LinearMoments(sample)); - // Get bounds of location - if (initialVals[0] == 0d) initialVals[0] = Tools.DoubleMachineEpsilon; - lowerVals[0] = -Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); - upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); - // Get bounds of scale - lowerVals[1] = Tools.DoubleMachineEpsilon; - upperVals[1] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[1]) + 1d)); + double normalization = DistributionNumerics.InitializationScale(sample); + var normalized = new double[sample.Count]; + for (int i = 0; i < sample.Count; i++) normalized[i] = sample[i] / normalization; + var initialVals = ParametersFromLinearMoments(Statistics.LinearMoments(normalized)); + initialVals[0] *= normalization; + initialVals[1] *= normalization; + DistributionNumerics.LocationParameterBounds(ref initialVals[0], initialVals[1], Statistics.Minimum(sample), + Statistics.Maximum(sample), false, out lowerVals[0], out upperVals[0]); + DistributionNumerics.PositiveParameterBounds(initialVals[1], out lowerVals[1], out upperVals[1]); return new Tuple(initialVals, lowerVals, upperVals); } @@ -363,6 +375,7 @@ double logLH(double[] x) /// public void SetParametersFromMLE(IList sample) { + DistributionNumerics.ValidateSample(sample, 4); // compute moments from sample var moments = Statistics.ProductMoments(sample); try @@ -425,10 +438,7 @@ public void SetParametersFromMLE(IList sample) /// public override double PDF(double x) { - if (_parametersValid == false) - ValidateParameters(Xi, Alpha, true); - double z = (x - Xi) / Alpha; - return 1d / Alpha * Math.Exp(-(z + Math.Exp(-z))); + return Math.Exp(LogPDF(x)); } /// @@ -440,7 +450,7 @@ public override double LogPDF(double x) { if (_parametersValid == false) ValidateParameters(Xi, Alpha, true); - double z = (x - Xi) / Alpha; + double z = DistributionNumerics.Standardize(x, Xi, Alpha); double lf = -(z + Math.Exp(-z)) - Math.Log(Alpha); return double.IsNaN(lf) ? double.NegativeInfinity : lf; } @@ -448,17 +458,37 @@ public override double LogPDF(double x) /// public override double CDF(double x) { - if (_parametersValid == false) - ValidateParameters(Xi, Alpha, true); - double z = (x - Xi) / Alpha; - return Math.Exp(-Math.Exp(-z)); + return Math.Exp(LogCDF(x)); + } + + /// + public override double LogCDF(double x) + { + if (!_parametersValid) ValidateParameters(Xi, Alpha, true); + return -Math.Exp(-DistributionNumerics.Standardize(x, Xi, Alpha)); + } + + /// + public override double CCDF(double x) + { + return -Tools.Expm1(LogCDF(x)); + } + + /// + /// Retains the upper-tail logarithm even after exp(-z) underflows. + public override double LogCCDF(double x) + { + if (!_parametersValid) ValidateParameters(Xi, Alpha, true); + double z = DistributionNumerics.Standardize(x, Xi, Alpha); + double exponential = Math.Exp(-z); + return exponential == 0 ? -z : DistributionNumerics.Log1mExp(-exponential); } /// public override double InverseCDF(double probability) { // Validate probability - if (probability < 0.0d || probability > 1.0d) + if (!(probability >= 0.0d && probability <= 1.0d)) throw new ArgumentOutOfRangeException("probability", "Probability must be between 0 and 1."); if (probability == 0.0d) return Minimum; @@ -467,7 +497,10 @@ public override double InverseCDF(double probability) // Validate parameters if (_parametersValid == false) ValidateParameters(Xi, _alpha, true); - return Xi - Alpha * Math.Log(-Math.Log(probability)); + double unitQuantile = -Math.Log(-Math.Log(probability)); + double displacement = Alpha * unitQuantile; + return double.IsInfinity(displacement) && DistributionNumerics.IsFinite(unitQuantile) + ? Alpha * (Xi / Alpha + unitQuantile) : Xi + displacement; } /// @@ -477,8 +510,12 @@ public override UnivariateDistributionBase Clone() } /// + /// Retains the published rounded MLE covariance constants. Sample size must be positive. + /// Parameters or sample size are invalid. + /// The method is not maximum likelihood. public double[,] ParameterCovariance(int sampleSize, ParameterEstimationMethod estimationMethod) { + DistributionNumerics.ValidateSampleSize(sampleSize); if (estimationMethod != ParameterEstimationMethod.MaximumLikelihood) { throw new NotImplementedException(); @@ -487,31 +524,32 @@ public override UnivariateDistributionBase Clone() if (_parametersValid == false) ValidateParameters(Xi, _alpha, true); // Compute covariance - double a = Alpha; + double a = Alpha / Math.Sqrt(sampleSize); var covar = new double[2, 2]; - covar[0, 0] = 1.1087d * a * a / sampleSize; // location - covar[1, 1] = 0.6079d * a * a / sampleSize; // scale - covar[0, 1] = 0.257d * a * a / sampleSize; + covar[0, 0] = 1.1087d * (a * a); // location + covar[1, 1] = 0.6079d * (a * a); // scale + covar[0, 1] = 0.257d * (a * a); covar[1, 0] = covar[0, 1]; return covar; } /// + /// Contracts the unchanged covariance in unit coordinates before restoring scale. + /// Probability must be finite and strictly between zero and one. public double QuantileVariance(double probability, int sampleSize, ParameterEstimationMethod estimationMethod) { - var covar = ParameterCovariance(sampleSize, estimationMethod); - var grad = QuantileGradient(probability); - double varA = covar[0, 0]; - double varB = covar[1, 1]; - double covAB = covar[1, 0]; - double dQx1 = grad[0]; - double dQx2 = grad[1]; - return Math.Pow(dQx1, 2d) * varA + Math.Pow(dQx2, 2d) * varB + 2d * dQx1 * dQx2 * covAB; + DistributionNumerics.ValidateProbability(probability); + if (!_parametersValid) ValidateParameters(Xi, Alpha, true); + var unit = new Gumbel(0, 1); + return DistributionNumerics.ScaledQuantileVariance(unit.ParameterCovariance(sampleSize, estimationMethod), + unit.QuantileGradient(probability), Alpha); } /// + /// Parameters are invalid or probability is not finite and strictly interior. public double[] QuantileGradient(double probability) { + DistributionNumerics.ValidateProbability(probability); // Validate parameters if (_parametersValid == false) ValidateParameters(Xi, _alpha, true); @@ -526,26 +564,8 @@ public double[] QuantileGradient(double probability) /// public double[,] QuantileJacobian(IList probabilities, out double determinant) { - if (probabilities.Count != NumberOfParameters) - { - throw new ArgumentOutOfRangeException(nameof(probabilities), "The number of probabilities must be the same length as the number of distribution parameters."); - } - // Get gradients - var dQp1 = QuantileGradient(probabilities[0]); - var dQp2 = QuantileGradient(probabilities[1]); - // Compute determinant - // |a b| - // |c d| - // |A| = ad − bc - double a = dQp1[0]; - double b = dQp1[1]; - double c = dQp2[0]; - double d = dQp2[1]; - determinant = a * d - b * c; - // Return Jacobian - var jacobian = new double[,] { { a, b }, { c, d } }; - return jacobian; + return DistributionNumerics.QuantileJacobian(this, probabilities, out determinant); } } -} \ No newline at end of file +} diff --git a/Numerics/Distributions/Univariate/KappaExpectedInformation.cs b/Numerics/Distributions/Univariate/KappaExpectedInformation.cs new file mode 100644 index 00000000..c6aa3d77 --- /dev/null +++ b/Numerics/Distributions/Univariate/KappaExpectedInformation.cs @@ -0,0 +1,359 @@ +using System; +using System.Collections.Generic; + +namespace Numerics.Distributions +{ + /// Expected fixed-observation score information for regular Kappa Four and fixed-hondo families. + /// + /// Scores are integrated over both complete probability tails. The square-root Jacobian is + /// combined with exponential scores before products are formed. Checked quadrature and + /// diagonally scaled Cholesky inversion report numerical failure without regularization. + /// + internal static class KappaExpectedInformation + { + private const double RelativeTolerance = 1E-10; + private const double AbsoluteTolerance = 1E-12; + private const double Roundoff = 2.2204460492503131E-16; + private const int MaximumIntervals = 2048; + private static readonly double[] Nodes = + { + .995657163025808080735527280689003, .973906528517171720077964012084452, + .930157491355708226001207180059508, .865063366688984510732096688423493, + .780817726586416897063717578345042, .679409568299024406234327365114874, + .562757134668604683339000099272694, .433395394129247190799265943165784, + .294392862701460198131126603103866, .148874338981631210884826001129720, 0 + }; + private static readonly double[] KronrodWeights = + { + .011694638867371874278064396062192, .032558162307964727478818972459390, + .054755896574351996031381300244580, .075039674810919952767043140916190, + .093125454583697605535065465083366, .109387158802297641899210590325805, + .123491976262065851077958109831074, .134709217311473325928054001771707, + .142775938577060080797094273138717, .147739104901338491374841515972068, + .149445554002916905664936468389821 + }; + private static readonly double[] GaussWeights = + { + .066671344308688137593568809893332, .149451349150580593145776339657697, + .219086362515982043995534934228163, .269266719309996355091226921569469, + .295524224714752870173892994651338 + }; + + /// Returns local asymptotic MLE covariance in xi, alpha, kappa[, hondo] coordinates. + /// The finite positive scale. + /// The finite kappa shape. + /// The finite hondo shape, fixed when three parameters are requested. + /// The positive number of independent observations. + /// Three for a fixed-hondo family, or four for full Kappa Four. + /// The inverse expected information, transformed to public scale and divided by sample size. + /// Scale, sample size, dimension or information regularity is invalid. + /// Quadrature, positive definiteness, inversion or representability checks fail. + internal static double[,] ParameterCovariance(double alpha, double k, double h, int sampleSize, int parameterCount) + { + DistributionNumerics.ValidateSampleSize(sampleSize); + if (!DistributionNumerics.IsFinite(alpha) || alpha <= 0) throw new ArgumentOutOfRangeException(nameof(alpha)); + double[,] information = ExpectedInformation(k, h, parameterCount, out _, out _, out _); + double[,] covariance = InvertInformation(information); + for (int i = 0; i < parameterCount; i++) + for (int j = i; j < parameterCount; j++) + { + double value = covariance[i, j]; + if (value != 0) + { + double logarithm = Math.Log(Math.Abs(value)) - Math.Log(sampleSize) + + (i < 2 ? Math.Log(alpha) : 0) + (j < 2 ? Math.Log(alpha) : 0); + value = Math.Sign(value) * Math.Exp(logarithm); + if (!DistributionNumerics.IsFinite(value)) + throw new InvalidOperationException("The local MLE covariance is outside the finite floating-point range."); + } + covariance[i, j] = covariance[j, i] = value; + } + return covariance; + } + + /// Returns standardized information and complete-tail integration diagnostics. + /// The kappa shape. + /// The hondo shape. + /// The estimated parameter dimension. + /// Integrated fixed-observation score means. + /// Estimated absolute errors of the score means. + /// Estimated absolute errors of information entries. + /// The symmetric per-observation standardized information matrix. + internal static double[,] ExpectedInformation(double k, double h, int parameterCount, + out double[] scoreMeans, out double[] scoreMeanErrors, out double[,] informationErrors) + { + ValidateDomain(k, h, parameterCount); + double boundary = Math.Max(k, Math.Max(h, k * h)); + // The stronger map regularizes integrable powers close to the information boundary. + // Both maps cover the entire p interval; neither truncates a probability tail. + int power = boundary > .4 ? 128 : 8; + var integrals = Integrate(k, h, parameterCount, power); + scoreMeans = new double[parameterCount]; + scoreMeanErrors = new double[parameterCount]; + var information = new double[parameterCount, parameterCount]; + informationErrors = new double[parameterCount, parameterCount]; + for (int i = 0; i < parameterCount; i++) + { + scoreMeans[i] = integrals.Values[i]; + scoreMeanErrors[i] = integrals.Errors[i]; + if (Math.Abs(scoreMeans[i]) > 4 * scoreMeanErrors[i] + 32 * Roundoff) + throw new InvalidOperationException("The integrated fixed-observation score does not have zero mean within numerical error."); + } + int index = parameterCount; + for (int i = 0; i < parameterCount; i++) + for (int j = i; j < parameterCount; j++, index++) + { + information[i, j] = information[j, i] = integrals.Values[index]; + informationErrors[i, j] = informationErrors[j, i] = integrals.Errors[index]; + } + return information; + } + + /// Checks information regularity without changing distribution or fitting validity. + private static void ValidateDomain(double k, double h, int count) + { + if (count != 3 && count != 4) throw new ArgumentOutOfRangeException(nameof(count)); + if (!DistributionNumerics.IsFinite(k) || !DistributionNumerics.IsFinite(h)) + throw new ArgumentOutOfRangeException(nameof(k), "Information requires finite shape parameters."); + if (k >= .5 || h >= .5 || k * h >= .5) + throw new ArgumentOutOfRangeException(nameof(k), "Regular information requires kappa < 1/2, hondo < 1/2 and kappa*hondo < 1/2."); + } + + /// Evaluates expm1(a*z)/a and its second divided difference without cancellation in weighted scores. + private static double SecondExponentialRelative(double x) + { + if (Math.Abs(x) >= .1) return (Tools.Expm1(x) - x) / x / x; + double sum = .5, term = .5; + for (int n = 1; n < 24; n++) + { + term *= x / (n + 2); + sum += term; + if (Math.Abs(term) < Math.Abs(sum) * 1E-17) break; + } + return sum; + } + + /// Returns log(abs(expm1(x))) without overflowing the exponential. + private static double LogAbsoluteExpm1(double x) => x > 36 ? x + Tools.Log1p(-Math.Exp(-x)) : Math.Log(Math.Abs(Tools.Expm1(x))); + + /// Forms scores times the square-root integration Jacobian before multiplying exponential terms. + private static double[] WeightedScores(double logp, double logq, double k, double h, double logroot) + { + double u = -logp; + double logt, logA; + double logAbsoluteH = Math.Log(Math.Abs(h)); + if (logq < -36 && logAbsoluteH + logq < -36) + { + // Corrections are below a binary64 rounding unit, including when q underflows. + logt = logA = logq; + } + else if (h == 0) logt = logA = Math.Log(u); + else + { + // Retain h*u through the log survival coordinate even if u is subnormal. + double hu = logq < -36 ? Math.Sign(h) * Math.Exp(logAbsoluteH + logq) : h * u; + logt = LogAbsoluteExpm1(-hu) - logAbsoluteH; + logA = LogAbsoluteExpm1(hu) - logAbsoluteH; + } + double y = -logt; + double root = Math.Exp(logroot); + double logOneMinusK = Tools.Log1p(-k), logOneMinusH = Tools.Log1p(-h); + double cRoot = Math.Exp(logOneMinusK + logroot) - Math.Exp(logOneMinusH + logA + logroot); + double cExponentialRoot = Math.Exp(logOneMinusK + k * y + logroot) + - Math.Exp(logOneMinusH + logA + k * y + logroot); + double scaleScore, kScore; + if (Math.Abs(k * y) < .1 || k == 0) + { + scaleScore = -root + cRoot * y * DistributionNumerics.Exprel(k * y); + kScore = y * root - cRoot * y * y * SecondExponentialRelative(k * y); + } + else + { + scaleScore = -root + (cExponentialRoot - cRoot) / k; + kScore = y * root + ((1 + k * y) * cRoot - cExponentialRoot) / k / k; + } + double hScore; + if (Math.Abs(h * u) < .1 || h == 0) + hScore = u * root - (1 - h) * u * u * root * SecondExponentialRelative(h * u); + else hScore = (u * root - Math.Exp(logOneMinusH + logA + logroot)) / h; + return new[] { cExponentialRoot, scaleScore, kScore, hScore }; + } + + /// Pairs the full lower and upper p tails in a common finite quadrature coordinate. + private static double[] Integrand(double coordinate, double k, double h, int count, int power) + { + double logCoordinate = Math.Log(coordinate); + double logtail = power * logCoordinate - Math.Log(2); + double logother = DistributionNumerics.Log1mExp(logtail); + double logroot = .5 * (Math.Log(power / 2d) + (power - 1) * logCoordinate); + double[] lower = WeightedScores(logtail, logother, k, h, logroot); + double[] upper = WeightedScores(logother, logtail, k, h, logroot); + var values = new double[count + count * (count + 1) / 2]; + double root = Math.Exp(logroot); + for (int i = 0; i < count; i++) values[i] = (lower[i] + upper[i]) * root; + int index = count; + for (int i = 0; i < count; i++) + for (int j = i; j < count; j++, index++) values[index] = lower[i] * lower[j] + upper[i] * upper[j]; + foreach (double value in values) + if (!DistributionNumerics.IsFinite(value)) + throw new InvalidOperationException("A complete-tail information integrand could not be represented finitely."); + return values; + } + + /// Stores a subinterval's Kronrod estimate and conservative local error indicators. + private sealed class Interval + { + internal double Lower, Upper; + internal double[] Values = Array.Empty(); + internal double[] Errors = Array.Empty(); + } + + /// G10K21 rule with absolute-deviation rescaling and a floating-point roundoff floor. + private static Interval Evaluate(double a, double b, double k, double h, int count, int power) + { + double center = .5 * (a + b), half = .5 * (b - a); + var nodes = new double[21][]; + nodes[20] = Integrand(center, k, h, count, power); + int dimension = nodes[20].Length; + var kronrod = new double[dimension]; + var gauss = new double[dimension]; + var absolute = new double[dimension]; + for (int j = 0; j < dimension; j++) + { + kronrod[j] = KronrodWeights[10] * nodes[20][j]; + absolute[j] = KronrodWeights[10] * Math.Abs(nodes[20][j]); + } + for (int i = 0; i < 10; i++) + { + nodes[2 * i] = Integrand(center - half * Nodes[i], k, h, count, power); + nodes[2 * i + 1] = Integrand(center + half * Nodes[i], k, h, count, power); + for (int j = 0; j < dimension; j++) + { + double sum = nodes[2 * i][j] + nodes[2 * i + 1][j]; + kronrod[j] += KronrodWeights[i] * sum; + absolute[j] += KronrodWeights[i] * (Math.Abs(nodes[2 * i][j]) + Math.Abs(nodes[2 * i + 1][j])); + if (i % 2 == 1) gauss[j] += GaussWeights[i / 2] * sum; + } + } + var errors = new double[dimension]; + for (int j = 0; j < dimension; j++) + { + double mean = kronrod[j] / 2; + double deviation = KronrodWeights[10] * Math.Abs(nodes[20][j] - mean); + for (int i = 0; i < 10; i++) + deviation += KronrodWeights[i] * (Math.Abs(nodes[2 * i][j] - mean) + Math.Abs(nodes[2 * i + 1][j] - mean)); + deviation *= half; + double error = Math.Abs(kronrod[j] - gauss[j]) * half; + if (deviation != 0 && error != 0) error = deviation * Math.Min(1, Math.Pow(200 * error / deviation, 1.5)); + errors[j] = Math.Max(error, 50 * Roundoff * half * absolute[j]); + kronrod[j] *= half; + } + return new Interval { Lower = a, Upper = b, Values = kronrod, Errors = errors }; + } + + /// Refines the interval with the largest normalized error until every component converges. + private static Interval Integrate(double k, double h, int count, int power) + { + var initial = Evaluate(0, 1, k, h, count, power); + var intervals = new List { initial }; + var total = new Interval { Values = (double[])initial.Values.Clone(), Errors = (double[])initial.Errors.Clone() }; + while (true) + { + bool success = true; + for (int j = 0; j < total.Values.Length; j++) + success &= total.Errors[j] <= AbsoluteTolerance + RelativeTolerance * Math.Abs(total.Values[j]); + if (success) return total; + if (intervals.Count >= MaximumIntervals) + throw new InvalidOperationException("Complete-tail expected-information quadrature did not meet its error tolerances."); + double worst = -1; + int selected = 0; + for (int i = 0; i < intervals.Count; i++) + for (int j = 0; j < total.Values.Length; j++) + { + double ratio = intervals[i].Errors[j] / (AbsoluteTolerance + RelativeTolerance * Math.Abs(total.Values[j])); + if (ratio > worst) { worst = ratio; selected = i; } + } + Interval old = intervals[selected]; + double center = .5 * (old.Lower + old.Upper); + if (center == old.Lower || center == old.Upper) + throw new InvalidOperationException("Expected-information quadrature exhausted floating-point subdivision resolution."); + Interval left = Evaluate(old.Lower, center, k, h, count, power); + Interval right = Evaluate(center, old.Upper, k, h, count, power); + intervals[selected] = left; + intervals.Add(right); + // Re-sum error estimates to avoid a negative error from subtracting parent estimates. + Array.Clear(total.Values, 0, total.Values.Length); + Array.Clear(total.Errors, 0, total.Errors.Length); + foreach (Interval interval in intervals) + for (int j = 0; j < total.Values.Length; j++) + { + total.Values[j] += interval.Values[j]; + total.Errors[j] += interval.Errors[j]; + } + } + } + + /// Inverts an SPD matrix with diagonal scaling and checked Cholesky solves. + private static double[,] InvertInformation(double[,] information) + { + int count = information.GetLength(0); + var scale = new double[count]; + var normalized = new double[count, count]; + var lower = new double[count, count]; + for (int i = 0; i < count; i++) + { + if (!(information[i, i] > 0) || !DistributionNumerics.IsFinite(information[i, i])) + throw new InvalidOperationException("Expected information has a nonpositive or nonfinite diagonal."); + scale[i] = Math.Sqrt(information[i, i]); + } + for (int i = 0; i < count; i++) + for (int j = 0; j < count; j++) normalized[i, j] = information[i, j] / scale[i] / scale[j]; + for (int i = 0; i < count; i++) + for (int j = 0; j <= i; j++) + { + double value = normalized[i, j]; + for (int m = 0; m < j; m++) value -= lower[i, m] * lower[j, m]; + if (i == j) + { + if (!(value > 0) || !DistributionNumerics.IsFinite(value)) + throw new InvalidOperationException("Expected information is not numerically positive definite."); + lower[i, j] = Math.Sqrt(value); + } + else lower[i, j] = value / lower[j, j]; + } + var inverse = new double[count, count]; + for (int column = 0; column < count; column++) + { + var solution = new double[count]; + for (int i = 0; i < count; i++) + { + double value = i == column ? 1 : 0; + for (int j = 0; j < i; j++) value -= lower[i, j] * solution[j]; + solution[i] = value / lower[i, i]; + } + for (int i = count - 1; i >= 0; i--) + { + double value = solution[i]; + for (int j = i + 1; j < count; j++) value -= lower[j, i] * solution[j]; + solution[i] = value / lower[i, i]; + inverse[i, column] = solution[i]; + } + for (int i = 0; i < count; i++) + { + double value = 0; + for (int j = 0; j < count; j++) value += normalized[i, j] * solution[j]; + if (!DistributionNumerics.IsFinite(value) || Math.Abs(value - (i == column ? 1 : 0)) > 1E-9) + throw new InvalidOperationException("The expected-information inverse failed its residual check."); + } + } + for (int i = 0; i < count; i++) + for (int j = i; j < count; j++) + { + double value = .5 * (inverse[i, j] + inverse[j, i]) / scale[i] / scale[j]; + if (!DistributionNumerics.IsFinite(value)) throw new InvalidOperationException("Expected-information inversion was nonfinite."); + inverse[i, j] = inverse[j, i] = value; + } + return inverse; + } + } +} diff --git a/Numerics/Distributions/Univariate/KappaFour.cs b/Numerics/Distributions/Univariate/KappaFour.cs index e54b6f5d..a3f7f330 100644 --- a/Numerics/Distributions/Univariate/KappaFour.cs +++ b/Numerics/Distributions/Univariate/KappaFour.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Numerics.Data.Statistics; using Numerics.Mathematics; @@ -81,6 +81,8 @@ public KappaFour(double location, double scale, double shape, double shape2) private double _hondo; // shape 2 private bool _momentsComputed = false; private double[] u = [double.NaN, double.NaN, double.NaN, double.NaN]; + [NonSerialized] private bool _compensatedMinimumComputed; + [NonSerialized] private double _compensatedMinimum; /// /// Gets and sets the location parameter ξ (Xi). @@ -93,6 +95,7 @@ public double Xi _parametersValid = ValidateParameters([value, Alpha, Kappa, Hondo], false) is null; _xi = value; _momentsComputed = false; + _compensatedMinimumComputed = false; } } @@ -107,6 +110,7 @@ public double Alpha _parametersValid = ValidateParameters([Xi, value, Kappa, Hondo], false) is null; _alpha = value; _momentsComputed = false; + _compensatedMinimumComputed = false; } } @@ -121,6 +125,7 @@ public double Kappa _parametersValid = ValidateParameters([Xi, Alpha, value, Hondo], false) is null; _kappa = value; _momentsComputed = false; + _compensatedMinimumComputed = false; } } @@ -135,6 +140,7 @@ public double Hondo _parametersValid = ValidateParameters([Xi, Alpha, Kappa, value], false) is null; _hondo = value; _momentsComputed = false; + _compensatedMinimumComputed = false; } } @@ -291,6 +297,12 @@ public override double Minimum { get { + if (Hondo > 0) + { + if (!_compensatedMinimumComputed) + _compensatedMinimumComputed = KappaFourBoundary.TryLowerEndpoint(Xi, Alpha, Kappa, Hondo, out _compensatedMinimum); + if (_compensatedMinimumComputed) return _compensatedMinimum; + } if (Hondo <= 0d && Kappa < 0d) { return LocationPlusScaleOverShape(); @@ -928,8 +940,8 @@ private bool IsUsableInitializer(double[] parameters, double[] lower, double[] u /// /// /// Evaluates interior densities directly in log space, including finite log densities whose - /// ordinary density underflows or overflows. Infinite endpoint densities retain the base - /// likelihood convention of returning negative infinity. + /// ordinary density underflows or overflows. Infinite endpoint density limits remain + /// positive infinity; aggregate likelihood evaluation applies its own contribution convention. /// public override double LogPDF(double x) { @@ -938,7 +950,7 @@ public override double LogPDF(double x) if (x == Minimum || x == Maximum) { double density = PDF(x); - return Tools.IsFinite(density) && density > 0d ? Math.Log(density) : double.NegativeInfinity; + return density > 0d ? Math.Log(density) : double.NegativeInfinity; } return InteriorLogDensity(x); } @@ -1207,17 +1219,40 @@ public override UnivariateDistributionBase Clone() } /// - /// Parameter covariance has not been implemented for Kappa Four. + /// + /// Local asymptotic MLE covariance in xi, alpha, kappa, hondo order. Finite regular + /// information requires kappa < 1/2, hondo < 1/2 and kappa*hondo < 1/2. + /// These conditions do not narrow distribution validity or assert global-MLE existence. + /// + /// Sample size, parameters or information regularity is invalid. + /// Numerical information or its inversion cannot be resolved. + /// The requested estimator is not maximum likelihood. public double[,] ParameterCovariance(int sampleSize, ParameterEstimationMethod estimationMethod) { - throw new NotImplementedException(); + DistributionNumerics.ValidateSampleSize(sampleSize); + EnsureValidParameters(); + if (estimationMethod != ParameterEstimationMethod.MaximumLikelihood) + throw new NotImplementedException("Kappa Four covariance is implemented only for local maximum-likelihood uncertainty."); + return KappaExpectedInformation.ParameterCovariance(Alpha, Kappa, Hondo, sampleSize, 4); } /// - /// Quantile variance has not been implemented for Kappa Four. + /// Applies the local-MLE delta method in common physical quantile coordinates, + /// avoiding underflow or overflow from forming physical covariance entries first. public double QuantileVariance(double probability, int sampleSize, ParameterEstimationMethod estimationMethod) { - throw new NotImplementedException(); + DistributionNumerics.ValidateProbability(probability); + EnsureValidParameters(); + var unit = new KappaFour(0, 1, Kappa, Hondo); + double[,] covariance = unit.ParameterCovariance(sampleSize, estimationMethod); + double logProbability = Math.Log(probability), w = QuantileLogT(logProbability, Hondo); + double argument = Kappa * w, s = Hondo * logProbability; + double dh = Hondo > 0 && s < -.5 ? logProbability * (Math.Exp(s) / Tools.Expm1(s)) - 1 / Hondo + : logProbability * LogExponentialRelativeDerivative(s); + double scaleGradient = DistributionNumerics.ScaledExprelProduct(Alpha, -w, argument); + double shapeGradient = -DistributionNumerics.ScaledExprelDerivativeProduct(Alpha, w, argument); + double hondoGradient = dh == 0 ? 0 : -Math.Sign(dh) * Math.Exp(Math.Log(Alpha) + argument + Math.Log(Math.Abs(dh))); + return DistributionNumerics.ScaledQuantileVariance(covariance, [Alpha, scaleGradient, shapeGradient, hondoGradient]); } /// @@ -1237,28 +1272,7 @@ public double[] QuantileGradient(double probability) /// public double[,] QuantileJacobian(IList probabilities, out double determinant) { - if (probabilities.Count != NumberOfParameters) - { - throw new ArgumentOutOfRangeException(nameof(probabilities), "The number of probabilities must be the same length as the number of distribution parameters."); - } - // |a b c d| - // |e f g h| - // |i j k l| - // |m n o p| - var jacobian = new Matrix(4); - for (int i = 0; i < 4; i++) - { - // Get the gradient - var dFdx = QuantileGradient(probabilities[i]); - // Populate the Jacobian matrix - for (int j = 0; j < 4; j++) - jacobian[i, j] = dFdx[j]; - } - // Solve determinant with LU decomposition - var LU = new LUDecomposition(jacobian); - determinant = LU.Determinant(); - // Return Jacobian - return jacobian.ToArray(); + return DistributionNumerics.QuantileJacobian(this, probabilities, out determinant); } } } diff --git a/Numerics/Distributions/Univariate/KappaFourBoundary.cs b/Numerics/Distributions/Univariate/KappaFourBoundary.cs index 550f3f06..30fe8530 100644 --- a/Numerics/Distributions/Univariate/KappaFourBoundary.cs +++ b/Numerics/Distributions/Univariate/KappaFourBoundary.cs @@ -10,6 +10,53 @@ internal static class KappaFourBoundary { private static readonly Pair LogTwo = new Pair(0.6931471805599453d, 2.3190468138462996E-17d); + /// Computes a positive-hondo lower endpoint with compensated logarithm, exponential and affine arithmetic. + /// The finite location. + /// The positive finite scale. + /// The finite kappa shape. + /// The positive finite hondo shape. + /// The accurately rounded finite endpoint when evaluation succeeds. + /// Whether the compensated finite-range evaluation succeeded. + /// + /// A rounded log(h) multiplied by kappa can move the support by several doubles. + /// Retaining the low parts prevents the declared support from excluding its first + /// interior representable argument. No tolerance or support clipping is used. + /// Extreme exponential or affine ranges remain the caller's responsibility. + /// + internal static bool TryLowerEndpoint(double xi, double alpha, double k, double h, out double endpoint) + { + endpoint = double.NaN; + if (!(h > 0) || !Tools.IsFinite(h)) return false; + Pair logH = Log(new Pair(h)); + Pair exponent = Multiply(new Pair(-k), logH); + if (!Tools.IsFinite(exponent.High) || Math.Abs(exponent.High) > 700) return false; + Pair standard; + if (Math.Abs(exponent.High) < .5) + { + Pair relative = new Pair(1), term = new Pair(1); + for (int n = 1; n <= 32; n++) + { + term = Divide(Multiply(term, exponent), new Pair(n + 1)); + relative = Add(relative, term); + } + standard = Multiply(logH, relative); + } + else + { + int power = (int)Math.Round(exponent.High / LogTwo.High); + Pair reduced = Subtract(exponent, Multiply(new Pair(power), LogTwo)); + Pair sum = new Pair(1), term = new Pair(1); + for (int n = 1; n <= 32; n++) + { + term = Divide(Multiply(term, reduced), new Pair(n)); + sum = Add(sum, term); + } + standard = Divide(Subtract(new Pair(1), Scale(sum, power)), new Pair(k)); + } + endpoint = Add(new Pair(xi), Multiply(new Pair(alpha), standard)).High; + return Tools.IsFinite(endpoint); + } + /// Returns log F close to the lower support endpoint for positive hondo. /// The value being evaluated. /// The finite location parameter. diff --git a/Numerics/Distributions/Univariate/LnNormal.cs b/Numerics/Distributions/Univariate/LnNormal.cs index 6c1246eb..bd15a128 100644 --- a/Numerics/Distributions/Univariate/LnNormal.cs +++ b/Numerics/Distributions/Univariate/LnNormal.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Numerics.Data.Statistics; using Numerics.Mathematics.Optimization; @@ -22,6 +22,14 @@ namespace Numerics.Distributions /// Wikipedia contributors, "Log-normal distribution,". Wikipedia, The Free /// Encyclopedia. Available at: /// + /// + /// and use physical + /// mean and standard deviation; and store the natural-log + /// mean and standard deviation. Quantile derivatives and covariance use the physical coordinates. + /// The method-of-moments estimator is the existing indirect estimator of log observations, + /// so its leading covariance is the transformed Normal covariance, as for maximum likelihood. + /// instead converts explicitly supplied physical moments. + /// /// [Serializable] public sealed class LnNormal : UnivariateDistributionBase, IEstimation, IMaximumLikelihoodEstimation, IMomentEstimation, ILinearMomentEstimation, IStandardError, IBootstrappable @@ -52,22 +60,26 @@ public LnNormal(double mean, double standardDeviation) private double _mu; private double _sigma; + private bool _hasPhysicalMoments; + private double _physicalMean; + private double _physicalStandardDeviation; /// - /// Gets and sets the location parameter µ (Mu). + /// Gets and sets the mean µ (Mu) of the natural logarithm of the observation. /// public double Mu { get { return _mu; } set { - _parametersValid = ValidateParameters(value, Sigma, false) is null; + _parametersValid = ValidateLogParameters(value, Sigma, false) is null; _mu = value; + _hasPhysicalMoments = false; } } /// - /// Gets and sets the scale parameter σ (sigma). + /// Gets and sets the standard deviation σ (sigma) of the natural logarithm of the observation. /// public double Sigma { @@ -75,11 +87,41 @@ public double Sigma set { if (value < 1E-16 && Math.Sign(value) != -1) value = 1E-16; - _parametersValid = ValidateParameters(Mu, value, false) is null; + _parametersValid = ValidateLogParameters(Mu, value, false) is null; _sigma = value; + _hasPhysicalMoments = false; } } + /// Validates the internal natural-log coordinates without applying physical-mean constraints. + private static ArgumentOutOfRangeException? ValidateLogParameters(double mean, double standardDeviation, bool throwException) + { + ArgumentOutOfRangeException? error = null; + if (double.IsNaN(mean) || double.IsInfinity(mean)) + error = new ArgumentOutOfRangeException(nameof(Mu), "The logarithmic mean must be finite."); + else if (!(standardDeviation > 0d) || double.IsInfinity(standardDeviation)) + error = new ArgumentOutOfRangeException(nameof(Sigma), "The logarithmic standard deviation must be finite and positive."); + if (throwException && error != null) throw error; + return error; + } + + /// + public override double LogCDF(double x) + { + if (!_parametersValid) ValidateLogParameters(Mu, Sigma, true); + return x <= 0d ? double.NegativeInfinity : DistributionNumerics.NormalLogCDF(DistributionNumerics.Standardize(Math.Log(x), Mu, Sigma)); + } + + /// + public override double CCDF(double x) => Math.Exp(LogCCDF(x)); + + /// + public override double LogCCDF(double x) + { + if (!_parametersValid) ValidateLogParameters(Mu, Sigma, true); + return x <= 0d ? 0d : DistributionNumerics.NormalLogSurvival(DistributionNumerics.Standardize(Math.Log(x), Mu, Sigma)); + } + /// public override int NumberOfParameters { @@ -139,7 +181,7 @@ public override double[] GetParameters /// public override double Mean { - get { return Math.Exp(Mu + Sigma * Sigma / 2.0d); } + get { return _hasPhysicalMoments ? _physicalMean : Math.Exp(Mu + Sigma * Sigma / 2.0d); } } /// @@ -157,13 +199,19 @@ public override double Mode /// public override double StandardDeviation { - get { return Math.Sqrt((Math.Exp(Sigma * Sigma) - 1.0d) * Math.Exp(2d * Mu + Sigma * Sigma)); } + get + { + if (_hasPhysicalMoments) return _physicalStandardDeviation; + double variance = Sigma * Sigma; + double logExcess = variance > 0.5d ? variance + Tools.Log1p(-Math.Exp(-variance)) : Math.Log(Tools.Expm1(variance)); + return Math.Exp(Mu + 0.5d * variance + 0.5d * logExcess); + } } /// public override double Skewness { - get { return (Math.Exp(Sigma * Sigma) + 2.0d) * Math.Sqrt(Math.Exp(Sigma * Sigma) - 1d); } + get { return (Math.Exp(Sigma * Sigma) + 2d) * Math.Sqrt(Tools.Expm1(Sigma * Sigma)); } } /// @@ -171,8 +219,8 @@ public override double Kurtosis { get { - double siqma2 = Math.Pow(Sigma, 2d); - return 3d + (Math.Exp(4d * siqma2) + 2d * Math.Exp(3d * siqma2) + 3d * Math.Exp(2d * siqma2) - 6d) ; + double variance = Sigma * Sigma; + return 3d + Tools.Expm1(4d * variance) + 2d * Tools.Expm1(3d * variance) + 3d * Tools.Expm1(2d * variance); } } @@ -191,7 +239,7 @@ public override double Maximum /// public override double[] MinimumOfParameters { - get { return [double.NegativeInfinity, 0.0d]; } + get { return [0.0d, 0.0d]; } } /// @@ -203,6 +251,7 @@ public override double[] MaximumOfParameters /// public void Estimate(IList sample, ParameterEstimationMethod estimationMethod) { + DistributionNumerics.ValidateSample(sample, 4, positive: true); if (estimationMethod == ParameterEstimationMethod.MethodOfMoments) { // Estimate using the method of moments(a.k.a product moments). @@ -246,6 +295,8 @@ public IUnivariateDistribution Bootstrap(ParameterEstimationMethod estimationMet /// /// The direct method for setting parameters is used so that users can set the parameters directly /// from real-space data, which is more intuitive. + /// Physical inputs are retained exactly for parameter-vector and XML round trips until a log + /// parameter changes. The legacy minimum log-scale rule still applies when conversion reaches it. /// public void SetParameters(double mean, double standardDeviation) { @@ -253,6 +304,12 @@ public void SetParameters(double mean, double standardDeviation) // Validate parameters Mu = parms[0]; Sigma = parms[1]; + if (_parametersValid && Sigma == parms[1]) + { + _physicalMean = mean; + _physicalStandardDeviation = standardDeviation; + _hasPhysicalMoments = true; + } } /// @@ -262,26 +319,28 @@ public override void SetParameters(IList parameters) } /// - /// Validate the parameters. + /// Validates physical mean and standard deviation supplied through the parameter-vector API. /// /// Mean. /// Standard deviation. /// Determines whether to throw an exception or not. + /// The validation error, or null when both physical moments are finite and positive. + /// A physical moment is invalid and is true. public ArgumentOutOfRangeException? ValidateParameters(double mean, double standardDeviation, bool throwException) { - if (double.IsNaN(mean) || double.IsInfinity(mean)) + if (!(mean > 0d) || double.IsInfinity(mean)) { - if (throwException) - throw new ArgumentOutOfRangeException(nameof(Mu), "Mu must be a number."); - return new ArgumentOutOfRangeException(nameof(Mu), "Mu must be a number."); + var error = new ArgumentOutOfRangeException(nameof(mean), "The physical mean must be finite and positive."); + if (throwException) throw error; + return error; } - if (double.IsNaN(standardDeviation) || double.IsInfinity(standardDeviation) || standardDeviation <= 0.0d) + if (!(standardDeviation > 0d) || double.IsInfinity(standardDeviation)) { - if (throwException) - throw new ArgumentOutOfRangeException(nameof(Sigma), "Sigma must be positive."); - return new ArgumentOutOfRangeException(nameof(Sigma), "Sigma must be positive."); + var error = new ArgumentOutOfRangeException(nameof(standardDeviation), "The physical standard deviation must be finite and positive."); + if (throwException) throw error; + return error; } - return null!; + return null; } /// @@ -297,19 +356,9 @@ public override void SetParameters(IList parameters) /// The array of sample data. public static double[] IndirectMethodOfMoments(IList sample) { - // Transform the sample - var transformedSample = new List(); - for (int i = 0; i < sample.Count; i++) - { - if (sample[i] > 0d) - { - transformedSample.Add(Math.Log(sample[i])); - } - else - { - transformedSample.Add(Math.Log(0.1d)); - } - } + DistributionNumerics.ValidateSample(sample, 4, positive: true); + var transformedSample = new double[sample.Count]; + for (int i = 0; i < sample.Count; i++) transformedSample[i] = Math.Log(sample[i]); return Statistics.ProductMoments(transformedSample); } @@ -320,19 +369,9 @@ public static double[] IndirectMethodOfMoments(IList sample) /// The array of sample data. public double[] IndirectMethodOfLinearMoments(IList sample) { - // Transform the sample - var transformedSample = new List(); - for (int i = 0; i < sample.Count; i++) - { - if (sample[i] > 0d) - { - transformedSample.Add(Math.Log(sample[i])); - } - else - { - transformedSample.Add(Math.Log(0.1d)); - } - } + DistributionNumerics.ValidateSample(sample, 4, positive: true); + var transformedSample = new double[sample.Count]; + for (int i = 0; i < sample.Count; i++) transformedSample[i] = Math.Log(sample[i]); return Statistics.LinearMoments(transformedSample); } @@ -343,27 +382,21 @@ public double[] IndirectMethodOfLinearMoments(IList sample) /// The real-space standard deviation of the data. public static double[] DirectMethodOfMoments(double mean, double standardDeviation) { - if (standardDeviation <= 0) + if (!(mean > 0d) || !(standardDeviation > 0d) || double.IsInfinity(mean) || double.IsInfinity(standardDeviation)) return [double.NaN, double.NaN]; - double variance = Math.Pow(standardDeviation, 2d); - double mu = Math.Log(Math.Pow(mean, 2d) / Math.Sqrt(variance + Math.Pow(mean, 2d))); - double sigma = Math.Sqrt(Math.Log(1.0d + variance / Math.Pow(mean, 2d))); - if (sigma < 1E-16 && Math.Sign(sigma) != -1) sigma = Tools.DoubleMachineEpsilon; - return [mu, sigma]; + double logRatio = Math.Log(standardDeviation) - Math.Log(mean); + double variance = logRatio > 0d + ? 2d * logRatio + Tools.Log1p(Math.Exp(-2d * logRatio)) + : Tools.Log1p(Math.Exp(2d * logRatio)); + return [Math.Log(mean) - 0.5d * variance, Math.Sqrt(variance)]; } /// + /// Returns the supplied physical mean and standard deviation in the same coordinates used by . public double[] ParametersFromMoments(IList moments) { - var mean = moments[0]; - var standardDeviation = moments[1]; - if (standardDeviation <= 0) - return [double.NaN, double.NaN]; - double variance = Math.Pow(standardDeviation, 2d); - double mu = Math.Log(Math.Pow(mean, 2d) / Math.Sqrt(variance + Math.Pow(mean, 2d))); - double sigma = Math.Sqrt(Math.Log(1.0d + variance / Math.Pow(mean, 2d))); - if (sigma < 1E-16 && Math.Sign(sigma) != -1) sigma = Tools.DoubleMachineEpsilon; - return [mu, sigma]; + ValidateParameters(moments[0], moments[1], true); + return [moments[0], moments[1]]; } /// @@ -399,20 +432,10 @@ public double[] LinearMomentsFromParameters(IList parameters) /// public Tuple GetParameterConstraints(IList sample) { - var initialVals = new double[NumberOfParameters]; - var lowerVals = new double[NumberOfParameters]; - var upperVals = new double[NumberOfParameters]; - // Get initial values - var moments = Statistics.ProductMoments(sample); - initialVals[0] = moments[0]; - initialVals[1] = moments[1]; - // Get bounds of mean - lowerVals[0] = Tools.DoubleMachineEpsilon; - upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[0]) + 1d)); - // Get bounds of standard deviation - lowerVals[1] = Tools.DoubleMachineEpsilon; - upperVals[1] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[1]) + 1d)); - return new Tuple(initialVals, lowerVals, upperVals); + DistributionNumerics.ValidateSample(sample, 4, positive: true); + var constraints = new Normal().GetParameterConstraints(sample); + constraints.Item2[0] = Math.Min(Tools.DoubleMachineEpsilon, constraints.Item1[0] / 10d); + return constraints; } /// @@ -440,12 +463,7 @@ double logLH(double[] x) /// public override double PDF(double x) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Mu, Sigma, true); - if (x <= Minimum) return 0.0d; - double d = (Math.Log(x) - Mu) / Sigma; - return Math.Exp(-0.5d * d * d) / (Tools.Sqrt2PI * Sigma * x); + return Math.Exp(LogPDF(x)); } /// @@ -455,31 +473,24 @@ public override double PDF(double x) /// public override double LogPDF(double x) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Mu, Sigma, true); - if (x <= Minimum) return double.NegativeInfinity; - double d = (Math.Log(x) - Mu) / Sigma; - double lf = -0.5d * d * d - Math.Log(Tools.Sqrt2PI * Sigma) - Math.Log(x); - return double.IsNaN(lf) ? double.NegativeInfinity : lf; + if (!_parametersValid) ValidateLogParameters(Mu, Sigma, true); + if (x <= 0d || double.IsPositiveInfinity(x)) return double.NegativeInfinity; + double logX = Math.Log(x); + double z = DistributionNumerics.Standardize(logX, Mu, Sigma); + return -0.5d * z * z - Math.Log(Sigma) - Math.Log(Tools.Sqrt2PI) - logX; } /// public override double CDF(double x) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Mu, Sigma, true); - if (x <= Minimum) - return 0d; - return Normal.StandardCDF((Math.Log(x) - Mu) / Sigma); + return Math.Exp(LogCDF(x)); } /// public override double InverseCDF(double probability) { // Validate probability - if (probability < 0.0d || probability > 1.0d) + if (double.IsNaN(probability) || probability < 0.0d || probability > 1.0d) throw new ArgumentOutOfRangeException("probability", "Probability must be between 0 and 1."); if (probability == 0.0d) return Minimum; @@ -487,89 +498,73 @@ public override double InverseCDF(double probability) return Maximum; // Validate parameters if (_parametersValid == false) - ValidateParameters(Mu, Sigma, true); - return Math.Exp(Mu - Sigma * Math.Sqrt(2.0d) * Erf.InverseErfc(2.0d * probability)); + ValidateLogParameters(Mu, Sigma, true); + return Math.Exp(Mu + Sigma * Normal.StandardZ(probability)); } /// public override UnivariateDistributionBase Clone() { - return new LnNormal() { Mu = Mu, Sigma = Sigma }; + var clone = new LnNormal() { Mu = Mu, Sigma = Sigma }; + clone._hasPhysicalMoments = _hasPhysicalMoments; + clone._physicalMean = _physicalMean; + clone._physicalStandardDeviation = _physicalStandardDeviation; + return clone; } /// + /// The returned covariance is for physical mean and standard deviation. Both supported estimators use log observations; this method applies the complete two-coordinate delta transformation. public double[,] ParameterCovariance(int sampleSize, ParameterEstimationMethod estimationMethod) { - if (estimationMethod != ParameterEstimationMethod.MethodOfMoments && - estimationMethod != ParameterEstimationMethod.MaximumLikelihood) - { + DistributionNumerics.ValidateSampleSize(sampleSize); + if (estimationMethod != ParameterEstimationMethod.MethodOfMoments && estimationMethod != ParameterEstimationMethod.MaximumLikelihood) throw new NotImplementedException(); - } - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Mu, _sigma, true); - // Compute covariance - double u2 = Sigma; - var covar = new double[2, 2]; - covar[0, 0] = Math.Pow(u2, 2d) / sampleSize; // location - covar[1, 1] = 2d * Math.Pow(u2, 4d) / sampleSize; // scale - covar[0, 1] = 0.0; - covar[1, 0] = covar[0, 1]; - return covar; + if (!_parametersValid) ValidateLogParameters(Mu, Sigma, true); + // Both supported estimators operate on log observations. Transform the leading + // Normal covariance diag(sigma^2, sigma^2/2)/n into physical (mean, SD) coordinates. + double mean = Mean, sd = StandardDeviation, variance = Sigma * Sigma; + double excess = Tools.Expm1(variance); + double sdDerivative = excess < 0.5d + ? mean * (Sigma / Math.Sqrt(excess)) * (1d + 2d * excess) + : sd * Sigma * (2d + 1d / excess); + double seMean = Sigma / Math.Sqrt(sampleSize), seSd = Sigma / Math.Sqrt(2d * sampleSize); + double m0 = mean * seMean, m1 = mean * Sigma * seSd; + double s0 = sd * seMean, s1 = sdDerivative * seSd; + return new[,] { { m0 * m0 + m1 * m1, m0 * s0 + m1 * s1 }, { m0 * s0 + m1 * s1, s0 * s0 + s1 * s1 } }; } /// public double QuantileVariance(double probability, int sampleSize, ParameterEstimationMethod estimationMethod) { - var covar = ParameterCovariance(sampleSize, estimationMethod); - var grad = QuantileGradient(probability); - double varA = covar[0, 0]; - double varB = covar[1, 1]; - double covAB = covar[1, 0]; - double dQx1 = grad[0]; - double dQx2 = grad[1]; - return Math.Pow(dQx1, 2d) * varA + Math.Pow(dQx2, 2d) * varB + 2d * dQx1 * dQx2 * covAB; + DistributionNumerics.ValidateProbability(probability); + DistributionNumerics.ValidateSampleSize(sampleSize); + if (estimationMethod != ParameterEstimationMethod.MethodOfMoments && estimationMethod != ParameterEstimationMethod.MaximumLikelihood) + throw new NotImplementedException(); + if (!_parametersValid) ValidateLogParameters(Mu, Sigma, true); + // The two physical-coordinate chain rules cancel to the original log-estimator delta method. + double z = Normal.StandardZ(probability); + double logStandardError = Mu + Sigma * z + Math.Log(Sigma) - .5 * Math.Log(sampleSize); + return Math.Exp(2 * logStandardError + Tools.Log1p(.5 * z * z)); } /// + /// Returns derivatives of the physical quantile with respect to physical mean and standard deviation, including both log-mean and log-variance dependencies. public double[] QuantileGradient(double probability) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Mu, _sigma, true); - double u1 = Mu; - double u2 = Sigma; - double z = Normal.StandardZ(probability); - var gradient = new double[] - { - Math.Exp(u1 + z * u2), // location - z * Math.Exp(u1 + z * u2) / (2d * u2) // scale - }; - return gradient; + DistributionNumerics.ValidateProbability(probability); + if (!_parametersValid) ValidateLogParameters(Mu, Sigma, true); + double z = Normal.StandardZ(probability), variance = Sigma * Sigma; + double weight = -Tools.Expm1(-variance); + double relativeQuantile = Math.Exp(z * Sigma - 0.5d * variance); + double meanDerivative = relativeQuantile * (1d + weight - z * weight / Sigma); + double sdDerivative = relativeQuantile * Math.Sqrt(weight) * Math.Exp(-0.5d * variance) * (z / Sigma - 1d); + return [meanDerivative, sdDerivative]; } /// public double[,] QuantileJacobian(IList probabilities, out double determinant) { - if (probabilities.Count != NumberOfParameters) - { - throw new ArgumentOutOfRangeException(nameof(probabilities), "The number of probabilities must be the same length as the number of distribution parameters."); - } - // Get gradients - var dQp1 = QuantileGradient(probabilities[0]); - var dQp2 = QuantileGradient(probabilities[1]); - // Compute determinant - // |a b| - // |c d| - // |A| = ad − bc - double a = dQp1[0]; - double b = dQp1[1]; - double c = dQp2[0]; - double d = dQp2[1]; - determinant = a * d - b * c; - // Return Jacobian - var jacobian = new double[,] { { a, b }, { c, d } }; - return jacobian; + return DistributionNumerics.QuantileJacobian(this, probabilities, out determinant); } /// diff --git a/Numerics/Distributions/Univariate/LogNormal.cs b/Numerics/Distributions/Univariate/LogNormal.cs index b4da1815..9e3690f6 100644 --- a/Numerics/Distributions/Univariate/LogNormal.cs +++ b/Numerics/Distributions/Univariate/LogNormal.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; using Numerics.Data.Statistics; @@ -80,21 +80,17 @@ public double Sigma } /// - /// Gets and sets the base of the logarithm + /// Gets and sets the finite logarithm base, which must be greater than one. /// + /// The value is nonfinite or no greater than one. public double Base { get { return _base; } set { - if (value < 1d) - { - _base = 1d; - } - else - { - _base = value; - } + if (!(value > 1d) || double.IsInfinity(value)) + throw new ArgumentOutOfRangeException(nameof(Base), "The logarithm base must be finite and greater than one."); + _base = value; } } @@ -106,6 +102,23 @@ private double K get { return 1d / Math.Log(Base); } } + /// + public override double LogCDF(double x) + { + if (!_parametersValid) ValidateParameters(Mu, Sigma, true); + return x <= 0d ? double.NegativeInfinity : DistributionNumerics.NormalLogCDF(DistributionNumerics.Standardize(Math.Log(x, Base), Mu, Sigma)); + } + + /// + public override double CCDF(double x) => Math.Exp(LogCCDF(x)); + + /// + public override double LogCCDF(double x) + { + if (!_parametersValid) ValidateParameters(Mu, Sigma, true); + return x <= 0d ? 0d : DistributionNumerics.NormalLogSurvival(DistributionNumerics.Standardize(Math.Log(x, Base), Mu, Sigma)); + } + /// public override int NumberOfParameters { @@ -183,10 +196,7 @@ public override double Median /// public override double Mode { - get - { - return Math.Exp(Mu / K); - } + get { double logBase = Math.Log(Base); return Math.Exp(Mu * logBase - Math.Pow(Sigma * logBase, 2d)); } } /// @@ -194,13 +204,10 @@ public override double StandardDeviation { get { - double lnB = Math.Log(Base); - double a = Sigma * Sigma * lnB; - double logPrefactor = (2.0 * Mu + a) * lnB; - double expA = Math.Exp(a * lnB); - double variance = Math.Exp(logPrefactor) * (expA - 1.0); - return Math.Sqrt(variance); - } + double logBase = Math.Log(Base), variance = Math.Pow(Sigma * logBase, 2d); + double logExcess = variance > 0.5d ? variance + Tools.Log1p(-Math.Exp(-variance)) : Math.Log(Tools.Expm1(variance)); + return Math.Exp(Mu * logBase + 0.5d * variance + 0.5d * logExcess); + } } /// @@ -208,16 +215,8 @@ public override double Skewness { get { - double lnB = Math.Log(Base); - double a = Sigma * Sigma * lnB; - double mu1 = (Mu + 0.5 * a) * lnB; - double mu2 = (2 * Mu + 2 * a) * lnB; - double mu3 = (3 * Mu + 4.5 * a) * lnB; - double m1 = Math.Exp(mu1); // E[X] - double m2 = Math.Exp(mu2); // E[X²] - double m3 = Math.Exp(mu3); // E[X³] - double thirdCentralMoment = m3 - 3 * m2 * m1 + 2 * m1 * m1 * m1; - return thirdCentralMoment / Math.Pow(StandardDeviation, 3); + double variance = Math.Pow(Sigma * Math.Log(Base), 2d); + return (Math.Exp(variance) + 2d) * Math.Sqrt(Tools.Expm1(variance)); } } @@ -226,18 +225,8 @@ public override double Kurtosis { get { - double lnB = Math.Log(Base); - double a = Sigma * Sigma * lnB; - double mu1 = (Mu + 0.5 * a) * lnB; - double mu2 = (2 * Mu + 2 * a) * lnB; - double mu3 = (3 * Mu + 4.5 * a) * lnB; - double mu4 = (4 * Mu + 8.0 * a) * lnB; - double m1 = Math.Exp(mu1); // E[X] - double m2 = Math.Exp(mu2); // E[X²] - double m3 = Math.Exp(mu3); // E[X³] - double m4 = Math.Exp(mu4); // E[X⁴] - double fourthCentralMoment = m4 - 4.0 * m3 * m1 + 6.0 * m2 * m1 * m1 - 3.0 * m1 * m1 * m1 * m1; - return fourthCentralMoment / Math.Pow(StandardDeviation, 4); + double variance = Math.Pow(Sigma * Math.Log(Base), 2d); + return 3d + Tools.Expm1(4d * variance) + 2d * Tools.Expm1(3d * variance) + 3d * Tools.Expm1(2d * variance); } } @@ -256,7 +245,7 @@ public override double Maximum /// public override double[] MinimumOfParameters { - // The mean of the log10-transformed variable is a location parameter and can be any + // The mean of the base-log observations is a location parameter and can be any // finite value; only the log-space standard deviation is bounded below by zero. get { return [double.NegativeInfinity, 0.0d]; } } @@ -270,6 +259,7 @@ public override double[] MaximumOfParameters /// public void Estimate(IList sample, ParameterEstimationMethod estimationMethod) { + DistributionNumerics.ValidateSample(sample, 4, positive: true); if (estimationMethod == ParameterEstimationMethod.MethodOfMoments) { SetParameters(IndirectMethodOfMoments(sample)); @@ -355,19 +345,9 @@ public override void SetParameters(IList parameters) /// The array of sample data. public double[] IndirectMethodOfMoments(IList sample) { - // Transform the sample - var transformedSample = new List(); - for (int i = 0; i < sample.Count; i++) - { - if (sample[i] > 0d) - { - transformedSample.Add(Math.Log(sample[i], Base)); - } - else - { - transformedSample.Add(Math.Log(0.1d, Base)); - } - } + DistributionNumerics.ValidateSample(sample, 4, positive: true); + var transformedSample = new double[sample.Count]; + for (int i = 0; i < sample.Count; i++) transformedSample[i] = Math.Log(sample[i], Base); return Statistics.ProductMoments(transformedSample); } @@ -378,19 +358,9 @@ public double[] IndirectMethodOfMoments(IList sample) /// The array of sample data. public double[] IndirectMethodOfLinearMoments(IList sample) { - // Transform the sample - var transformedSample = new List(); - for (int i = 0; i < sample.Count; i++) - { - if (sample[i] > 0d) - { - transformedSample.Add(Math.Log(sample[i], Base)); - } - else - { - transformedSample.Add(Math.Log(0.1d, Base)); - } - } + DistributionNumerics.ValidateSample(sample, 4, positive: true); + var transformedSample = new double[sample.Count]; + for (int i = 0; i < sample.Count; i++) transformedSample[i] = Math.Log(sample[i], Base); return Statistics.LinearMoments(transformedSample); } @@ -401,27 +371,21 @@ public double[] IndirectMethodOfLinearMoments(IList sample) /// The real-space standard deviation of the data. public double[] DirectMethodOfMoments(double mean, double standardDeviation) { - double variance = Math.Pow(standardDeviation, 2d); - double mu = Math.Log(Math.Pow(mean, 2d) / Math.Sqrt(variance + Math.Pow(mean, 2d)), Base); - double sigma = Math.Sqrt(Math.Log(1.0d + variance / Math.Pow(mean, 2d), Base)); - return [mu, sigma]; + double[] natural = LnNormal.DirectMethodOfMoments(mean, standardDeviation); + double logBase = Math.Log(Base); + return [natural[0] / logBase, natural[1] / logBase]; } /// public double[] ParametersFromMoments(IList moments) { - var mean = moments[0]; - var standardDeviation = moments[1]; - double variance = Math.Pow(standardDeviation, 2d); - double mu = Math.Log(Math.Pow(mean, 2d) / Math.Sqrt(variance + Math.Pow(mean, 2d)), Base); - double sigma = Math.Sqrt(Math.Log(1.0d + variance / Math.Pow(mean, 2d), Base)); - return [mu, sigma]; + return DirectMethodOfMoments(moments[0], moments[1]); } /// public double[] MomentsFromParameters(IList parameters) { - var dist = new LogNormal(); + var dist = new LogNormal() { Base = Base }; dist.SetParameters(parameters); var m1 = dist.Mean; var m2 = dist.StandardDeviation; @@ -451,26 +415,10 @@ public double[] LinearMomentsFromParameters(IList parameters) /// public Tuple GetParameterConstraints(IList sample) { - var initialVals = new double[NumberOfParameters]; - var lowerVals = new double[NumberOfParameters]; - var upperVals = new double[NumberOfParameters]; - // Estimate initial values using the method of moments (a.k.a product moments). - var mom = IndirectMethodOfMoments(sample); - initialVals = new double[] { mom[0], mom[1] }; - // Get bounds of mean. The mean is a location parameter on the log scale and is - // legitimately negative whenever the data are mostly below 1, so the bounds are - // symmetric about zero from the magnitude of the initial value, matching Normal's - // location bounds. A machine-epsilon floor here would reject any sub-unity sample - // before a fit could start. - if (initialVals[0] == 0d) initialVals[0] = Tools.DoubleMachineEpsilon; - lowerVals[0] = -Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); - upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); - // Get bounds of standard deviation - double real = Math.Exp(initialVals[1] / K); - lowerVals[1] = Tools.DoubleMachineEpsilon; - upperVals[1] = Math.Ceiling(Math.Log(Math.Pow(10d, Math.Ceiling(Math.Log10(real) + 1d)), Base)); - upperVals[1] = double.IsNaN(upperVals[1]) ? 4 : upperVals[1]; - return new Tuple(initialVals, lowerVals, upperVals); + DistributionNumerics.ValidateSample(sample, 4, positive: true); + var transformed = new double[sample.Count]; + for (int i = 0; i < sample.Count; i++) transformed[i] = Math.Log(sample[i], Base); + return new Normal().GetParameterConstraints(transformed); } /// @@ -499,11 +447,7 @@ double logLH(double[] x) /// public override double PDF(double x) { - if (_parametersValid == false) - ValidateParameters(Mu, Sigma, true); - if (x <= Minimum) return 0.0d; - double d = (Math.Log(x, Base) - Mu) / Sigma; - return Math.Exp(-0.5d * d * d) / (Tools.Sqrt2PI * Sigma) * (K / x); + return Math.Exp(LogPDF(x)); } /// @@ -513,29 +457,24 @@ public override double PDF(double x) /// public override double LogPDF(double x) { - if (_parametersValid == false) - ValidateParameters(Mu, Sigma, true); - if (x <= Minimum) return double.NegativeInfinity; - double d = (Math.Log(x, Base) - Mu) / Sigma; - double lf = -0.5d * d * d - Math.Log(Tools.Sqrt2PI * Sigma) + Math.Log(K) - Math.Log(x); - return double.IsNaN(lf) ? double.NegativeInfinity : lf; + if (!_parametersValid) ValidateParameters(Mu, Sigma, true); + if (x <= 0d || double.IsPositiveInfinity(x)) return double.NegativeInfinity; + double logX = Math.Log(x), logBase = Math.Log(Base); + double z = DistributionNumerics.Standardize(logX / logBase, Mu, Sigma); + return -0.5d * z * z - Math.Log(Sigma) - Math.Log(Tools.Sqrt2PI) - Math.Log(logBase) - logX; } /// public override double CDF(double x) { - if (_parametersValid == false) - ValidateParameters(Mu, Sigma, true); - if (x <= Minimum) - return 0d; - return Normal.StandardCDF((Math.Log(x, Base) - Mu) / Sigma); + return Math.Exp(LogCDF(x)); } /// public override double InverseCDF(double probability) { // Validate probability - if (probability < 0.0d || probability > 1.0d) + if (double.IsNaN(probability) || probability < 0.0d || probability > 1.0d) throw new ArgumentOutOfRangeException("probability", "Probability must be between 0 and 1."); if (probability == 0.0d) return Minimum; @@ -544,7 +483,7 @@ public override double InverseCDF(double probability) // Validate parameters if (_parametersValid == false) ValidateParameters(Mu, Sigma, true); - return Math.Exp((Mu - Sigma * Math.Sqrt(2.0d) * Erf.InverseErfc(2.0d * probability)) / K); + return Math.Exp((Mu + Sigma * Normal.StandardZ(probability)) / K); } /// @@ -560,6 +499,8 @@ public override double InverseCDF(double probability) /// public double[,] MonteCarloConfidenceIntervals(int sampleSize, int realizations, IList quantiles, IList percentiles) { + DistributionNumerics.ValidateConfidenceInputs(sampleSize, quantiles, percentiles, 2); + if (realizations <= 0) throw new ArgumentOutOfRangeException(nameof(realizations), "At least one realization is required."); // validate parameters if (_parametersValid == false) ValidateParameters(Mu, _sigma, true); @@ -618,6 +559,7 @@ public override UnivariateDistributionBase Clone() /// public double[,] ParameterCovariance(int sampleSize, ParameterEstimationMethod estimationMethod) { + DistributionNumerics.ValidateSampleSize(sampleSize); if (estimationMethod != ParameterEstimationMethod.MethodOfMoments && estimationMethod != ParameterEstimationMethod.MaximumLikelihood) { @@ -628,70 +570,48 @@ public override UnivariateDistributionBase Clone() ValidateParameters(Mu, _sigma, true); // Compute covariance in (μ, σ) parameterization of log-space. // Var(μ̂) = σ²/n, Var(σ̂) = σ²/(2n), Cov = 0. - // Both MoM and MLE give the same result for Normal (UMVUE). - double s2 = Sigma * Sigma; + // Log-moment and maximum-likelihood estimators share this leading asymptotic covariance. + double scaled = Sigma / Math.Sqrt(sampleSize); + double s2 = scaled * scaled; var covar = new double[2, 2]; - covar[0, 0] = s2 / sampleSize; // Var(μ̂) - covar[1, 1] = s2 / (2.0 * sampleSize); // Var(σ̂) + covar[0, 0] = s2; // Var(μ̂) + covar[1, 1] = s2 / 2d; // Var(σ̂) covar[0, 1] = 0.0; covar[1, 0] = covar[0, 1]; return covar; } /// + /// Combines the physical quantile and parameter standard errors in log space before squaring, so representable variance is retained when the quantile square would overflow. public double QuantileVariance(double probability, int sampleSize, ParameterEstimationMethod estimationMethod) { - var covar = ParameterCovariance(sampleSize, estimationMethod); - var grad = QuantileGradient(probability); - double varA = covar[0, 0]; - double varB = covar[1, 1]; - double covAB = covar[1, 0]; - double dQx1 = grad[0]; - double dQx2 = grad[1]; - double varQ = Math.Pow(dQx1, 2d) * varA + Math.Pow(dQx2, 2d) * varB + 2d * dQx1 * dQx2 * covAB; - return varQ * Math.Pow(InverseCDF(probability) / K, 2d); + DistributionNumerics.ValidateProbability(probability); + DistributionNumerics.ValidateSampleSize(sampleSize); + if (estimationMethod != ParameterEstimationMethod.MethodOfMoments && estimationMethod != ParameterEstimationMethod.MaximumLikelihood) + throw new NotImplementedException(); + if (!_parametersValid) ValidateParameters(Mu, Sigma, true); + double z = Normal.StandardZ(probability), logBase = Math.Log(Base); + double logQuantile = (Mu + Sigma * z) * logBase; + double logStandardError = logQuantile + Math.Log(logBase) + Math.Log(Sigma) - 0.5d * Math.Log(sampleSize); + // For the mean and SD of base-log observations, covariance is zero and the SD variance is half the mean variance. + return Math.Exp(2d * logStandardError + Tools.Log1p(0.5d * z * z)); } /// + /// Returns derivatives of the physical quantile with respect to the configured base-log and . The change-of-base and exponential Jacobian are applied exactly once. public double[] QuantileGradient(double probability) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Mu, _sigma, true); + DistributionNumerics.ValidateProbability(probability); + if (!_parametersValid) ValidateParameters(Mu, Sigma, true); double z = Normal.StandardZ(probability); - // Q(p) = μ + σ·z(p) in log-space, so ∂Q/∂μ = 1, ∂Q/∂σ = z(p). - var gradient = new double[] - { - 1.0d, // ∂Q/∂μ - z // ∂Q/∂σ - }; - return gradient; + double factor = InverseCDF(probability) * Math.Log(Base); + return [factor, factor * z]; } /// public double[,] QuantileJacobian(IList probabilities, out double determinant) { - if (probabilities.Count != NumberOfParameters) - { - throw new ArgumentOutOfRangeException(nameof(probabilities), "The number of probabilities must be the same length as the number of distribution parameters."); - } - // Get gradients - var dQp1 = QuantileGradient(probabilities[0]); - var dQp2 = QuantileGradient(probabilities[1]); - // Compute determinant - // |a b| - // |c d| - // |A| = ad − bc - double p0 = InverseCDF(probabilities[0]) / K; - double p1 = InverseCDF(probabilities[1]) / K; - double a = dQp1[0] * p0; - double b = dQp1[1] * p0; - double c = dQp2[0] * p1; - double d = dQp2[1] * p1; - determinant = a * d - b * c; - // Return Jacobian - var jacobian = new double[,] { { a, b }, { c, d } }; - return jacobian; + return DistributionNumerics.QuantileJacobian(this, probabilities, out determinant); } diff --git a/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs b/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs index 67807936..6724de72 100644 --- a/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs +++ b/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Buffers.Text; using System.Collections.Generic; using System.Linq; @@ -97,7 +97,7 @@ public double Gamma /// public double Xi { - get { return Mu - 2.0d * Sigma / Gamma; } + get { return Mu - Sigma * (2d / Gamma); } } /// @@ -113,25 +113,21 @@ public double Beta /// public double Alpha { - get { return 4d / (Gamma * Gamma); } + get { return Math.Pow(2d / Gamma, 2d); } } /// - /// Gets and sets the base of the logarithm + /// Gets and sets the finite logarithm base, which must be greater than one. /// + /// The value is nonfinite or no greater than one. public double Base { - get { return _base;} + get { return _base; } set { - if (value < 1d) - { - _base = 1d; - } - else - { - _base = value; - } + if (!(value > 1d) || double.IsInfinity(value)) + throw new ArgumentOutOfRangeException(nameof(Base), "The logarithm base must be finite and greater than one."); + _base = value; } } @@ -143,6 +139,75 @@ private double K get { return 1d / Math.Log(Base); } } + /// + public override double LogCDF(double x) + { + if (!_parametersValid) ValidateParameters(Mu, Sigma, Gamma, true); + if (x <= Minimum) return double.NegativeInfinity; + if (x >= Maximum) return 0d; + return new PearsonTypeIII(Mu, Sigma, Gamma).LogCDF(Math.Log(x, Base)); + } + + /// + public override double CCDF(double x) => Math.Exp(LogCCDF(x)); + + /// + public override double LogCCDF(double x) + { + if (!_parametersValid) ValidateParameters(Mu, Sigma, Gamma, true); + if (x <= Minimum) return 0d; + if (x >= Maximum) return double.NegativeInfinity; + return new PearsonTypeIII(Mu, Sigma, Gamma).LogCCDF(Math.Log(x, Base)); + } + + /// Log raw-moment contribution after removing the location, with each moment's existence checked separately. + private double LogMomentShape(int order) + { + double scale = order * Sigma * Math.Log(Base), argument = Gamma * scale / 2d; + if (!(argument < 1d)) return double.PositiveInfinity; + if (Math.Abs(argument) < 0.01d) + { + // [-log(1-u)-u]/u^2 = sum u^k/(k+2), analytic at u=0. + double sum = 0.5d, term = 1d; + for (int k = 1; k <= 12; k++) { term *= argument; sum += term / (k + 2d); } + return scale * scale * sum; + } + return Alpha * (-Tools.Log1p(-argument) - argument); + } + + /// Expands centered exponential moments before evaluating them, avoiding cancellation for tiny log scale. + private void SmallScaleStandardizedMoments(out double skewness, out double kurtosis) + { + const int order = 12; + var raw = new double[4][]; + for (int r = 1; r <= 4; r++) + { + raw[r - 1] = new double[order + 1]; + raw[r - 1][0] = 1d; + for (int n = 2; n <= order; n++) + for (int k = 2; k <= n; k++) + raw[r - 1][n] += Math.Pow(r, k) * Math.Pow(Gamma / 2d, k - 2) * raw[r - 1][n - k] / n; + } + double[] square = MultiplySeries(raw[0], raw[0]), cube = MultiplySeries(square, raw[0]); + double[] fourth = MultiplySeries(square, square), secondFirst = MultiplySeries(raw[1], raw[0]); + double[] thirdFirst = MultiplySeries(raw[2], raw[0]), secondSquare = MultiplySeries(raw[1], square); + double scale = Sigma * Math.Log(Base), variance = 0d, third = 0d, fourthCentral = 0d; + for (int n = order; n >= 2; n--) variance = variance * scale + raw[1][n] - square[n]; + for (int n = order; n >= 3; n--) third = third * scale + raw[2][n] - 3d * secondFirst[n] + 2d * cube[n]; + for (int n = order; n >= 4; n--) fourthCentral = fourthCentral * scale + raw[3][n] - 4d * thirdFirst[n] + 6d * secondSquare[n] - 3d * fourth[n]; + skewness = third / Math.Pow(variance, 1.5d); + kurtosis = fourthCentral / (variance * variance); + } + + /// Multiplies equal-length truncated power series used for centered moment evaluation. + private static double[] MultiplySeries(double[] left, double[] right) + { + var result = new double[left.Length]; + for (int n = 0; n < result.Length; n++) + for (int k = 0; k <= n; k++) result[n] += left[k] * right[n - k]; + return result; + } + /// public override int NumberOfParameters { @@ -201,21 +266,10 @@ public override double[] GetParameters } /// + /// Moment order r exists only when 1-r*Beta*log(Base) is positive. Divergent positive raw moments return positive infinity. public override double Mean { - get - { - double lnB = Math.Log(Base); - if (Math.Abs(Gamma) <= NearZero) - { - return Math.Exp((Mu + 0.5 * Sigma * Sigma * lnB) * lnB); - } - else - { - double lnMean = Xi * lnB - Alpha * Math.Log(1.0 - Beta * lnB); - return Math.Exp(lnMean); - } - } + get { return Math.Exp(Mu * Math.Log(Base) + LogMomentShape(1)); } } /// @@ -229,14 +283,18 @@ public override double Mode { get { - if (Math.Abs(Gamma) <= NearZero) - { - return Math.Exp(Mu / K); - } - else + double logBase = Math.Log(Base), scale = Sigma * logBase; + if (Gamma == 0d) return Math.Exp(Mu * logBase - scale * scale); + double beta = Beta * logBase, shape = Alpha; + if (Gamma > 0d && shape <= 1d) return Minimum; + if (Gamma < 0d) { - return Math.Exp((Xi + (Alpha - 1d) * Beta) / K); + if (shape < 1d) return Maximum; + if (shape == 1d) return beta < -1d ? 0d : Maximum; + if (beta <= -1d) return 0d; } + // The transformation density includes 1/x, moving the gamma mode. + return Math.Exp(Mu * logBase - (scale * scale + beta) / (1d + beta)); } } @@ -245,27 +303,11 @@ public override double StandardDeviation { get { - double lnB = Math.Log(Base); - if (Math.Abs(Gamma) <= NearZero) - { - double a = Sigma * Sigma * lnB; - double logPrefactor = (2.0 * Mu + a) * lnB; - double expA = Math.Exp(a * lnB); - double variance = Math.Exp(logPrefactor) * (expA - 1.0); - return Math.Sqrt(variance); - } - else - { - double t1 = -Alpha * Math.Log(1 - 2.0 * Beta * lnB); - double t2 = -2.0 * Alpha * Math.Log(1 - Beta * lnB); - - // Factor out the larger exponent to keep precision - double maxT = Math.Max(t1, t2); - double diff = Math.Exp(t1 - maxT) - Math.Exp(t2 - maxT); // positive & stable - double logVariance = 2.0 * Xi * lnB + maxT + Math.Log(diff); - - return Math.Sqrt(Math.Exp(logVariance)); - } + double first = LogMomentShape(1), second = LogMomentShape(2); + if (double.IsPositiveInfinity(first) || double.IsPositiveInfinity(second)) return double.PositiveInfinity; + double delta = second - 2d * first; + double logExcess = delta > 0.5d ? delta + Tools.Log1p(-Math.Exp(-delta)) : Math.Log(Tools.Expm1(delta)); + return Math.Exp(Mu * Math.Log(Base) + first + 0.5d * logExcess); } } @@ -274,37 +316,22 @@ public override double Skewness { get { - double lnB = Math.Log(Base); - if (Math.Abs(Gamma) <= NearZero) + if (double.IsPositiveInfinity(LogMomentShape(3))) + return double.IsPositiveInfinity(LogMomentShape(2)) ? double.NaN : double.PositiveInfinity; + double scale = Sigma * Math.Log(Base); + if (Math.Abs(scale) * (1d + Math.Abs(Gamma)) < 0.01d) { - // Log-Normal case - double a = Sigma * Sigma * lnB; // a = σ² ln b - double mu1 = (Mu + 0.5 * a) * lnB; - double mu2 = (2 * Mu + 2 * a) * lnB; - double mu3 = (3 * Mu + 4.5 * a) * lnB; - - double m1 = Math.Exp(mu1); // E[X] - double m2 = Math.Exp(mu2); // E[X²] - double m3 = Math.Exp(mu3); // E[X³] - - double thirdCentralMoment = m3 - 3 * m2 * m1 + 2 * m1 * m1 * m1; - return thirdCentralMoment / Math.Pow(StandardDeviation, 3); - } - else - { - // LP3 case - double t1 = 1 - Beta * lnB; - double t2 = 1 - 2 * Beta * lnB; - double t3 = 1 - 3 * Beta * lnB; - - double m1 = Math.Pow(t1, -Alpha); // E[X] / b^ξ - double m2 = Math.Pow(t2, -Alpha); // E[X²] / b^{2ξ} - double m3 = Math.Pow(t3, -Alpha); // E[X³] / b^{3ξ} - - double thirdCentralMoment = m3 - 3 * m2 * m1 + 2 * m1 * m1 * m1; - double prefactor = Math.Pow(Base, 3 * Xi); - return prefactor * thirdCentralMoment / Math.Pow(StandardDeviation, 3); + SmallScaleStandardizedMoments(out double skewness, out _); + return skewness; } + double first = LogMomentShape(1), d2 = LogMomentShape(2) - 2d * first, d3 = LogMomentShape(3) - 3d * first; + double logVariance = d2 > 0.5d ? d2 + Tools.Log1p(-Math.Exp(-d2)) : Math.Log(Tools.Expm1(d2)); + // Factor exp(d3) out of the signed numerator, then combine it with + // the variance denominator before exponentiating the final ratio. + double normalizedThird = -Tools.Expm1(-d3) + - 3d * Math.Exp(d2 - d3) * -Tools.Expm1(-d2); + if (normalizedThird == 0d) return 0d; + return (normalizedThird < 0d ? -1d : 1d) * Math.Exp(d3 - 1.5d * logVariance + Math.Log(Math.Abs(normalizedThird))); } } @@ -313,87 +340,36 @@ public override double Kurtosis { get { - double lnB = Math.Log(Base); - if (Math.Abs(Gamma) <= NearZero) - { - // Log-Normal case - double a = Sigma * Sigma * lnB; - - double mu1 = (Mu + 0.5 * a) * lnB; - double mu2 = (2 * Mu + 2 * a) * lnB; - double mu3 = (3 * Mu + 4.5 * a) * lnB; - double mu4 = (4 * Mu + 8.0 * a) * lnB; - - double m1 = Math.Exp(mu1); // E[X] - double m2 = Math.Exp(mu2); // E[X²] - double m3 = Math.Exp(mu3); // E[X³] - double m4 = Math.Exp(mu4); // E[X⁴] - - double fourthCentralMoment = m4 - 4.0 * m3 * m1 + 6.0 * m2 * m1 * m1 - 3.0 * m1 * m1 * m1 * m1; - - return fourthCentralMoment / Math.Pow(StandardDeviation, 4); - } - else + if (double.IsPositiveInfinity(LogMomentShape(4))) + return double.IsPositiveInfinity(LogMomentShape(2)) ? double.NaN : double.PositiveInfinity; + double scale = Sigma * Math.Log(Base); + if (Math.Abs(scale) * (1d + Math.Abs(Gamma)) < 0.01d) { - // LP3 case - double t1 = 1 - Beta * lnB; - double t2 = 1 - 2 * Beta * lnB; - double t3 = 1 - 3 * Beta * lnB; - double t4 = 1 - 4 * Beta * lnB; - - double m1 = Math.Pow(t1, -Alpha); // E[X] / b^ξ - double m2 = Math.Pow(t2, -Alpha); // E[X²] / b^{2ξ} - double m3 = Math.Pow(t3, -Alpha); // E[X³] / b^{3ξ} - double m4 = Math.Pow(t4, -Alpha); // E[X⁴] / b^{4ξ} - - double fourthCentralMoment = m4 - 4.0 * m3 * m1 + 6.0 * m2 * m1 * m1 - 3.0 * m1 * m1 * m1 * m1; - - double prefactor = Math.Pow(Base, 4 * Xi); - return prefactor * fourthCentralMoment / Math.Pow(StandardDeviation, 4); + SmallScaleStandardizedMoments(out _, out double kurtosis); + return kurtosis; } + double first = LogMomentShape(1), d2 = LogMomentShape(2) - 2d * first; + double d3 = LogMomentShape(3) - 3d * first, d4 = LogMomentShape(4) - 4d * first; + double logVariance = d2 > 0.5d ? d2 + Tools.Log1p(-Math.Exp(-d2)) : Math.Log(Tools.Expm1(d2)); + // All exponential arguments in the normalized numerator are nonpositive. + // The last exponential alone determines genuine result overflow. + double normalizedFourth = -Tools.Expm1(-d4) + - 4d * Math.Exp(d3 - d4) * -Tools.Expm1(-d3) + + 6d * Math.Exp(d2 - d4) * -Tools.Expm1(-d2); + return Math.Exp(d4 - 2d * logVariance + Math.Log(normalizedFourth)); } } /// public override double Minimum { - get - { - if (Math.Abs(Gamma) <= NearZero) - { - // Use LogNormal - return 0.0d; - } - else if (Beta > 0.0d) - { - return Math.Exp(Xi / K); - } - else - { - return 0.0d; - } - } + get { return Gamma > 0d ? Math.Exp(Xi * Math.Log(Base)) : 0d; } } /// public override double Maximum { - get - { - if (Math.Abs(Gamma) <= NearZero) - { - // Use LogNormal - return double.PositiveInfinity; - } - else if (Beta > 0.0d) - { - return double.PositiveInfinity; - } - else - { - return Math.Exp(Xi / K); - } - } + get { return Gamma < 0d ? Math.Exp(Xi * Math.Log(Base)) : double.PositiveInfinity; } } /// @@ -413,6 +389,7 @@ public override double[] MaximumOfParameters /// public void Estimate(IList sample, ParameterEstimationMethod estimationMethod) { + DistributionNumerics.ValidateSample(sample, 4, positive: true); if (estimationMethod == ParameterEstimationMethod.MethodOfMoments) { SetParameters(IndirectMethodOfMoments(sample)); @@ -518,19 +495,9 @@ public override void SetParameters(IList parameters) /// The array of sample data. public double[] IndirectMethodOfMoments(IList sample) { - // Transform the sample - var transformedSample = new List(); - for (int i = 0; i < sample.Count; i++) - { - if (sample[i] > 0d) - { - transformedSample.Add(Math.Log(sample[i], Base)); - } - else - { - transformedSample.Add(Math.Log(0.01d, Base)); - } - } + DistributionNumerics.ValidateSample(sample, 4, positive: true); + var transformedSample = new double[sample.Count]; + for (int i = 0; i < sample.Count; i++) transformedSample[i] = Math.Log(sample[i], Base); return Statistics.ProductMoments(transformedSample); } @@ -542,19 +509,9 @@ public double[] IndirectMethodOfMoments(IList sample) /// The array of sample data. public double[] IndirectMethodOfLinearMoments(IList sample) { - // Transform the sample - var transformedSample = new List(); - for (int i = 0; i < sample.Count; i++) - { - if (sample[i] > 0d) - { - transformedSample.Add(Math.Log(sample[i], Base)); - } - else - { - transformedSample.Add(Math.Log(0.01d, Base)); - } - } + DistributionNumerics.ValidateSample(sample, 4, positive: true); + var transformedSample = new double[sample.Count]; + for (int i = 0; i < sample.Count; i++) transformedSample[i] = Math.Log(sample[i], Base); return Statistics.LinearMoments(transformedSample); } @@ -567,7 +524,7 @@ public double[] ParametersFromMoments(IList moments) /// public double[] MomentsFromParameters(IList parameters) { - var dist = new LogPearsonTypeIII(); + var dist = new LogPearsonTypeIII() { Base = Base }; dist.SetParameters(parameters); var m1 = dist.Mean; var m2 = dist.StandardDeviation; @@ -686,36 +643,10 @@ public double[] LinearMomentsFromParameters(IList parameters) /// public Tuple GetParameterConstraints(IList sample) { - var initialVals = new double[NumberOfParameters]; - var lowerVals = new double[NumberOfParameters]; - var upperVals = new double[NumberOfParameters]; - // - // Estimate initial values using the method of moments. - var mom = IndirectMethodOfMoments(sample); - initialVals = [mom[0], mom[1], mom[2]]; - // Get bounds of mean. The mean is a location parameter on the log scale and is - // legitimately negative whenever the data are mostly below 1, so the bounds are - // symmetric about zero from the magnitude of the initial value, matching Normal's - // location bounds. A machine-epsilon floor here would reject any sub-unity sample - // before a fit could start. - if (initialVals[0] == 0d) initialVals[0] = Tools.DoubleMachineEpsilon; - lowerVals[0] = -Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); - upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); - // Get bounds of standard deviation - double real = Math.Exp(initialVals[1] / K); - lowerVals[1] = Tools.DoubleMachineEpsilon; - upperVals[1] = Math.Ceiling(Math.Log(Math.Pow(10d, Math.Ceiling(Math.Log10(real) + 1d)), Base)); - upperVals[1] = double.IsNaN(upperVals[1]) ? 4 : upperVals[1]; - - // Get bounds of skew - lowerVals[2] = -6d; - upperVals[2] = 6d; - // Correct initial value of skew if necessary - if (initialVals[2] <= lowerVals[2] || initialVals[2] >= upperVals[2]) - { - initialVals[2] = 0.01; - } - return new Tuple(initialVals, lowerVals, upperVals); + DistributionNumerics.ValidateSample(sample, 4, positive: true); + var transformed = new double[sample.Count]; + for (int i = 0; i < sample.Count; i++) transformed[i] = Math.Log(sample[i], Base); + return new PearsonTypeIII().GetParameterConstraints(transformed); } /// @@ -744,27 +675,7 @@ double logLH(double[] x) /// public override double PDF(double x) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Mu, Sigma, Gamma, true); - // - if (x < Minimum || x > Maximum) return 0.0d; - - if (Math.Abs(Gamma) <= NearZero) - { - double d = (Math.Log(x, Base) - Mu) / Sigma; - return Math.Exp(-0.5d * d * d) / (Tools.Sqrt2PI * Sigma) * (K / x); - } - else if (Beta > 0d) - { - double shiftedX = Math.Log(x, Base) - Xi; - return Math.Exp(-shiftedX / Math.Abs(Beta) + (Alpha - 1.0d) * Math.Log(shiftedX) - Alpha * Math.Log(Math.Abs(Beta)) - Mathematics.SpecialFunctions.Gamma.LogGamma(Alpha)) * (K / x); - } - else - { - double shiftedX = Xi - Math.Log(x, Base); - return Math.Exp(-shiftedX / Math.Abs(Beta) + (Alpha - 1.0d) * Math.Log(shiftedX) - Alpha * Math.Log(Math.Abs(Beta)) - Mathematics.SpecialFunctions.Gamma.LogGamma(Alpha)) * (K / x); - } + return Math.Exp(LogPDF(x)); } /// @@ -777,78 +688,38 @@ public override double PDF(double x) /// public override double LogPDF(double x) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Mu, Sigma, Gamma, true); - if (x < Minimum || x > Maximum) return double.NegativeInfinity; - - double lf; - if (Math.Abs(Gamma) <= NearZero) - { - double d = (Math.Log(x, Base) - Mu) / Sigma; - lf = -0.5d * d * d - Math.Log(Tools.Sqrt2PI * Sigma) + Math.Log(K) - Math.Log(x); - } - else if (Beta > 0d) - { - double shiftedX = Math.Log(x, Base) - Xi; - lf = -shiftedX / Math.Abs(Beta) + (Alpha - 1.0d) * Math.Log(shiftedX) - Alpha * Math.Log(Math.Abs(Beta)) - Mathematics.SpecialFunctions.Gamma.LogGamma(Alpha) + Math.Log(K) - Math.Log(x); - } - else + if (!_parametersValid) ValidateParameters(Mu, Sigma, Gamma, true); + if (x < Minimum || x > Maximum || double.IsPositiveInfinity(x)) return double.NegativeInfinity; + if (x == 0d) { - double shiftedX = Xi - Math.Log(x, Base); - lf = -shiftedX / Math.Abs(Beta) + (Alpha - 1.0d) * Math.Log(shiftedX) - Alpha * Math.Log(Math.Abs(Beta)) - Mathematics.SpecialFunctions.Gamma.LogGamma(Alpha) + Math.Log(K) - Math.Log(x); + if (Gamma >= 0d) return double.NegativeInfinity; + double rate = -1d / (Beta * Math.Log(Base)); + if (rate > 1d) return double.NegativeInfinity; + if (rate < 1d || Alpha > 1d) return double.PositiveInfinity; + return Alpha == 1d ? -Xi * Math.Log(Base) : double.NegativeInfinity; } - return double.IsNaN(lf) ? double.NegativeInfinity : lf; + double logX = Math.Log(x), logBase = Math.Log(Base); + var pearson = new PearsonTypeIII(Mu, Sigma, Gamma); + // Preserve exact transformed endpoint density limits despite logarithm roundoff. + double transformed = (Gamma > 0d && x == Minimum) || (Gamma < 0d && x == Maximum) ? Xi : logX / logBase; + return pearson.LogPDF(transformed) - Math.Log(logBase) - logX; } /// public override double CDF(double x) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Mu, Sigma, Gamma, true); - if (x <= Minimum) return 0d; - if (x >= Maximum) return 1d; - if (Math.Abs(Gamma) <= NearZero) - { - return Normal.StandardCDF((Math.Log(x, Base) - Mu) / Sigma); - } - else if (Beta > 0d) - { - double shiftedX = Math.Log(x, Base) - Xi; - return Mathematics.SpecialFunctions.Gamma.LowerIncomplete(Alpha, shiftedX / Math.Abs(Beta)); - } - else - { - double shiftedX = Xi - Math.Log(x, Base); - return 1.0d - Mathematics.SpecialFunctions.Gamma.LowerIncomplete(Alpha, shiftedX / Math.Abs(Beta)); - } + return Math.Exp(LogCDF(x)); } /// public override double InverseCDF(double probability) { - // Validate probability - if (probability < 0.0d || probability > 1.0d) - throw new ArgumentOutOfRangeException("probability", "Probability must be between 0 and 1."); - if (probability == 0.0d) return Minimum; - if (probability == 1.0d) return Maximum; - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Mu, Sigma, Gamma, true); - // - if (Math.Abs(Gamma) <= NearZero) - { - return Math.Exp((Mu - Sigma * Math.Sqrt(2.0d) * Erf.InverseErfc(2.0d * probability)) / K); - } - else if (Beta > 0d) - { - return Math.Exp((Xi + Mathematics.SpecialFunctions.Gamma.InverseLowerIncomplete(Alpha, probability) * Math.Abs(Beta)) / K); - } - else - { - return Math.Exp((Xi - Mathematics.SpecialFunctions.Gamma.InverseLowerIncomplete(Alpha, 1d - probability) * Math.Abs(Beta)) / K); - } + if (double.IsNaN(probability) || probability < 0d || probability > 1d) + throw new ArgumentOutOfRangeException(nameof(probability), "Probability must be between zero and one."); + if (!_parametersValid) ValidateParameters(Mu, Sigma, Gamma, true); + if (probability == 0d) return Minimum; + if (probability == 1d) return Maximum; + return Math.Exp(new PearsonTypeIII(Mu, Sigma, Gamma).InverseCDF(probability) * Math.Log(Base)); } /// @@ -861,7 +732,7 @@ public override double InverseCDF(double probability) public double WilsonHilfertyInverseCDF(double probability) { // Validate probability - if (probability < 0.0d || probability > 1.0d) + if (double.IsNaN(probability) || probability < 0.0d || probability > 1.0d) throw new ArgumentOutOfRangeException("probability", "Probability must be between 0 and 1."); if (probability == 0.0d) return Minimum; @@ -884,133 +755,38 @@ public override UnivariateDistributionBase Clone() /// + /// Uses public base-log mean, standard deviation and skew coordinates, including the full zero-skew limit. Maximum-likelihood covariance requires absolute skew below sqrt(2). public double[,] ParameterCovariance(int sampleSize, ParameterEstimationMethod estimationMethod) { - if (estimationMethod != ParameterEstimationMethod.MethodOfMoments && - estimationMethod != ParameterEstimationMethod.MaximumLikelihood) - { - throw new NotImplementedException(); - } - // Validate parameters - if (_parametersValid == false) - ValidateParameters(_mu, _sigma, _gamma, true); - // Compute covariance in user-facing (μₗ, σₗ, γₗ) parameterization. - // LP3 parameters are the PT3/Gamma parameters of log(X). - var covar = new double[3, 3]; - if (estimationMethod == ParameterEstimationMethod.MethodOfMoments) - { - // MoM asymptotic covariance via Cov = D⁻¹·S·D⁻ᵀ / n. - // Identical to PT3 since LP3 MoM works in log-space. - // Moment conditions centered on model mean μ(θ): - // g₁ = X−μ, g₂ = (X−μ)²−σ², g₃ = (X−μ)³−γσ³. - // Jacobian D = ∂E[g]/∂(μ,σ,γ) is lower-triangular: - // D = [[-1, 0, 0], [0, -2σ, 0], [-3σ², -3γσ², -σ³]] - // Note: D[2,0] = E[∂g₃/∂μ] = E[-3(X−μ)²] = -3σ² ≠ 0 because - // g₃ centers on μ(θ), not on x̄. - // D⁻¹ = [[-1, 0, 0], [0, -1/(2σ), 0], [3/σ, 3γ/(2σ²), -1/σ³]] - double s = _sigma, g = _gamma; - double s2 = s * s, s3 = s2 * s, s4 = s2 * s2, s5 = s4 * s, s6 = s4 * s2; - double g2 = g * g, g4 = g2 * g2; - // Central moments for Pearson III family - double mu2 = s2; - double mu3 = g * s3; - double mu4 = s4 * (3.0 + 1.5 * g2); - double mu5 = s5 * g * (10.0 + 3.0 * g2); - double mu6 = s6 * (15.0 + 32.5 * g2 + 7.5 * g4); - // S matrix elements: S = E[g·gᵀ] - double S00 = mu2; - double S01 = mu3; - double S02 = mu4; - double S11 = mu4 - mu2 * mu2; - double S12 = mu5 - mu2 * mu3; - double S22 = mu6 - mu3 * mu3; - // D⁻¹ elements (lower-triangular) - double a = -1.0; // D⁻¹[0,0] - double b = -1.0 / (2.0 * s); // D⁻¹[1,1] - double e = 3.0 / s; // D⁻¹[2,0] — from D[2,0] = -3σ² - double c = 3.0 * g / (2.0 * s2); // D⁻¹[2,1] - double d = -1.0 / s3; // D⁻¹[2,2] - // Cov = D⁻¹ · S · D⁻ᵀ / n (exploit triangular structure) - covar[0, 0] = a * a * S00 / sampleSize; - covar[0, 1] = a * b * S01 / sampleSize; - covar[0, 2] = a * (e * S00 + c * S01 + d * S02) / sampleSize; - covar[1, 1] = b * b * S11 / sampleSize; - covar[1, 2] = b * (e * S01 + c * S11 + d * S12) / sampleSize; - covar[2, 2] = (e * e * S00 + 2.0 * e * c * S01 + 2.0 * e * d * S02 - + c * c * S11 + 2.0 * c * d * S12 + d * d * S22) / sampleSize; - covar[1, 0] = covar[0, 1]; - covar[2, 0] = covar[0, 2]; - covar[2, 1] = covar[1, 2]; - } - else - { - // MLE: Fisher information inverse in internal (μ, α=1/β, λ=4/γ²) parameterization. - // This parameterization matches QuantileGradient for MLE QuantileVariance computation. - double alpha = 1d / Beta; - double lambda = Alpha; - double A = 2d * Mathematics.SpecialFunctions.Gamma.Trigamma(lambda) - 2d / (lambda - 1d) + 1d / Math.Pow(lambda - 1d, 2d); - covar[0, 0] = (lambda - 2d) / (sampleSize * A) * (1d / Math.Pow(alpha, 2d)) * (Mathematics.SpecialFunctions.Gamma.Trigamma(lambda) * lambda - 1d); // location - covar[1, 1] = (lambda - 2d) * Math.Pow(alpha, 2d) / (sampleSize * A) * (Mathematics.SpecialFunctions.Gamma.Trigamma(lambda) / (lambda - 2d) - 1d / Math.Pow(lambda - 1d, 2d)); // scale - covar[2, 2] = 2d / (sampleSize * A); // shape - covar[0, 1] = 1d / sampleSize * ((lambda - 2d) / A) * (Mathematics.SpecialFunctions.Gamma.Trigamma(lambda) - 1d / (lambda - 1d)); // location & scale - covar[1, 0] = covar[0, 1]; - covar[0, 2] = (2d - lambda) / (sampleSize * alpha * A * (lambda - 1d)); // location & shape - covar[2, 0] = covar[0, 2]; - covar[1, 2] = alpha / (sampleSize * A * (lambda - 1d)); // scale & shape - covar[2, 1] = covar[1, 2]; - } - return covar; + DistributionNumerics.ValidateSampleSize(sampleSize); + if (!_parametersValid) ValidateParameters(Mu, Sigma, Gamma, true); + return new PearsonTypeIII(Mu, Sigma, Gamma).ParameterCovariance(sampleSize, estimationMethod); } /// public double QuantileVariance(double probability, int sampleSize, ParameterEstimationMethod estimationMethod) { - if (estimationMethod == ParameterEstimationMethod.MethodOfMoments) - { - double u2 = _sigma; - double c = _gamma; - double c2 = Math.Pow(c, 2d); - int N = sampleSize; - double varQ = Math.Pow(u2, 2d) / N * (1d + Math.Pow(GammaDistribution.FrequencyFactorKp(c, probability), 2d) / 2d * (1d + 0.75d * c2) + GammaDistribution.FrequencyFactorKp(c, probability) * c + 6d * (1d + 0.25d * c2) * GammaDistribution.PartialKp(c, probability) * (GammaDistribution.PartialKp(c, probability) * (1d + 5d * c2 / 4d) + GammaDistribution.FrequencyFactorKp(c, probability) / 2d * c)); - return varQ * Math.Pow(InverseCDF(probability) / K, 2d); - } - else if (estimationMethod == ParameterEstimationMethod.MaximumLikelihood) - { - var covar = ParameterCovariance(sampleSize, estimationMethod); - var grad = QuantileGradient(probability); - double varA = covar[0, 0]; - double varB = covar[1, 1]; - double varG = covar[2, 2]; - double covAB = covar[1, 0]; - double covAG = covar[2, 0]; - double covBG = covar[2, 1]; - double dQx1 = grad[0]; - double dQx2 = grad[1]; - double dQx3 = grad[2]; - double varQ = Math.Pow(dQx1, 2d) * varA + Math.Pow(dQx2, 2d) * varB + Math.Pow(dQx3, 2d) * varG + 2d * dQx1 * dQx2 * covAB + 2d * dQx1 * dQx3 * covAG + 2d * dQx2 * dQx3 * covBG; - return varQ * Math.Pow(InverseCDF(probability) / K, 2d); - } - - return double.NaN; + DistributionNumerics.ValidateProbability(probability); + DistributionNumerics.ValidateSampleSize(sampleSize); + if (estimationMethod != ParameterEstimationMethod.MethodOfMoments && estimationMethod != ParameterEstimationMethod.MaximumLikelihood) + return double.NaN; + if (!_parametersValid) ValidateParameters(Mu, Sigma, Gamma, true); + var standardized = new PearsonTypeIII(0, 1, Gamma); + double variance = standardized.QuantileVariance(probability, sampleSize, estimationMethod); + double logBase = Math.Log(Base); + double logQuantile = new PearsonTypeIII(Mu, Sigma, Gamma).InverseCDF(probability) * logBase; + return Math.Exp(2 * (logQuantile + Math.Log(logBase) + Math.Log(Sigma)) + Math.Log(variance)); } /// + /// Transforms the actual Pearson quantile derivatives into physical-quantile derivatives, applying log(Base)*quantile once in each public base-log parameter coordinate. public double[] QuantileGradient(double probability) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Mu, Sigma, Gamma, true); - // Gradient in internal (μ, α=1/β, λ=4/γ²) parameterization. - // Matches ParameterCovariance(MLE) for MLE QuantileVariance computation. - double alpha = 1d / Beta; - double lambda = Alpha; - double eps = Math.Sign(alpha); - var gradient = new double[] - { - 1.0d, // location - -lambda / Math.Pow(alpha, 2d) * (1.0d + eps / Math.Sqrt(lambda) * GammaDistribution.FrequencyFactorKp(Gamma, probability)), // scale - 1.0d / alpha * (1.0d + eps / Math.Sqrt(lambda) * (GammaDistribution.FrequencyFactorKp(Gamma, probability) / 2.0d) - 1.0d / lambda * GammaDistribution.PartialKp(Gamma, probability)) // shape - }; + DistributionNumerics.ValidateProbability(probability); + if (!_parametersValid) ValidateParameters(Mu, Sigma, Gamma, true); + double[] gradient = new PearsonTypeIII(Mu, Sigma, Gamma).QuantileGradient(probability); + double factor = InverseCDF(probability) * Math.Log(Base); + for (int i = 0; i < gradient.Length; i++) gradient[i] *= factor; return gradient; } @@ -1020,47 +796,13 @@ public double[] QuantileGradient(double probability) /// Probability between 0 and 1. public IList QuantileGradientForMoments(double probability) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Mu, Sigma, Gamma, true); - var gradient = new double[] - { - Math.Exp((Mu + Sigma * GammaDistribution.FrequencyFactorKp(Gamma, probability)) / K) / K, - Math.Exp((Mu + Sigma * GammaDistribution.FrequencyFactorKp(Gamma, probability)) / K) * (GammaDistribution.FrequencyFactorKp(Gamma, probability) / K), - Math.Exp((Mu + Sigma * GammaDistribution.FrequencyFactorKp(Gamma, probability)) / K) * (Sigma / K) * GammaDistribution.PartialKp(Gamma, probability) - }; - return gradient; + return QuantileGradient(probability); } /// public double[,] QuantileJacobian(IList probabilities, out double determinant) { - if (probabilities.Count != NumberOfParameters) - { - throw new ArgumentOutOfRangeException(nameof(probabilities), "The number of probabilities must be the same length as the number of distribution parameters."); - } - // Get gradients - var dQp1 = QuantileGradientForMoments(probabilities[0]); - var dQp2 = QuantileGradientForMoments(probabilities[1]); - var dQp3 = QuantileGradientForMoments(probabilities[2]); - // Compute determinant - // |a b c| - // |d e f| - // |g h i| - // |A| = a(ei − fh) − b(di − fg) + c(dh − eg) - double a = dQp1[0]; - double b = dQp1[1]; - double c = dQp1[2]; - double d = dQp2[0]; - double e = dQp2[1]; - double f = dQp2[2]; - double g = dQp3[0]; - double h = dQp3[1]; - double i = dQp3[2]; - determinant = a * (e * i - f * h) - b * (d * i - f * g) + c * (d * h - e * g); - // Return Jacobian - var jacobian = new double[,] { { a, b, c }, { d, e, f }, { g, h, i } }; - return jacobian; + return DistributionNumerics.QuantileJacobian(this, probabilities, out determinant); } } diff --git a/Numerics/Distributions/Univariate/Logistic.cs b/Numerics/Distributions/Univariate/Logistic.cs index ef9d9030..0ec80661 100644 --- a/Numerics/Distributions/Univariate/Logistic.cs +++ b/Numerics/Distributions/Univariate/Logistic.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Numerics.Data.Statistics; using Numerics.Mathematics.Optimization; @@ -72,6 +72,37 @@ public double Alpha } } + /// + /// The symmetric logit form avoids exponential overflow in either tail. + public override double LogPDF(double x) + { + if (!_parametersValid) ValidateParameters([Xi, Alpha], true); + double magnitude = Math.Abs(DistributionNumerics.Standardize(x, Xi, Alpha)); + return -Math.Log(Alpha) - magnitude - 2d * Tools.Log1p(Math.Exp(-magnitude)); + } + + /// + public override double LogCDF(double x) + { + if (!_parametersValid) ValidateParameters([Xi, Alpha], true); + double z = DistributionNumerics.Standardize(x, Xi, Alpha); + return z >= 0d ? -Tools.Log1p(Math.Exp(-z)) : z - Tools.Log1p(Math.Exp(z)); + } + + /// + public override double CCDF(double x) + { + return Math.Exp(LogCCDF(x)); + } + + /// + public override double LogCCDF(double x) + { + if (!_parametersValid) ValidateParameters([Xi, Alpha], true); + double z = DistributionNumerics.Standardize(x, Xi, Alpha); + return z >= 0d ? -z - Tools.Log1p(Math.Exp(-z)) : -Tools.Log1p(Math.Exp(z)); + } + /// public override int NumberOfParameters { @@ -191,6 +222,7 @@ public override double[] MaximumOfParameters /// public void Estimate(IList sample, ParameterEstimationMethod estimationMethod) { + DistributionNumerics.ValidateSample(sample, 4); if (estimationMethod == ParameterEstimationMethod.MethodOfMoments) { SetParameters(ParametersFromMoments(Statistics.ProductMoments(sample))); @@ -276,18 +308,12 @@ public double[] MomentsFromParameters(IList parameters) /// public Tuple GetParameterConstraints(IList sample) { - var initialVals = new double[NumberOfParameters]; - var lowerVals = new double[NumberOfParameters]; - var upperVals = new double[NumberOfParameters]; - // Get initial values - initialVals = ParametersFromMoments(Statistics.ProductMoments(sample)); - // Get bounds of location - lowerVals[0] = -Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[0]) + 1d)); - upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[0]) + 1d)); - // Get bounds of scale - lowerVals[1] = Tools.DoubleMachineEpsilon; - upperVals[1] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[1]) + 1d)); - return new Tuple(initialVals, lowerVals, upperVals); + var normal = new Normal().GetParameterConstraints(sample); + double correction = Math.Sqrt(3d) / Math.PI; + normal.Item1[1] *= correction; + normal.Item2[1] *= correction; + normal.Item3[1] *= correction; + return normal; } /// @@ -315,26 +341,24 @@ double logLH(double[] x) /// public override double PDF(double x) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters([Xi, Alpha], true); - return 1d / Alpha * Math.Exp(-(x - Xi) / Alpha) * Math.Pow(1d + Math.Exp(-(x - Xi) / Alpha), -2); + if (!_parametersValid) ValidateParameters([Xi, Alpha], true); + double magnitude = Math.Abs(DistributionNumerics.Standardize(x, Xi, Alpha)); + if (magnitude > 36d) return Math.Exp(LogPDF(x)); + double tail = Math.Exp(-magnitude); + return (tail / (1d + tail)) / (1d + tail) / Alpha; } /// public override double CDF(double x) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters([Xi, Alpha], true); - return 1d / (1d + Math.Exp(-(x - Xi) / Alpha)); + return Math.Exp(LogCDF(x)); } /// public override double InverseCDF(double probability) { // Validate probability - if (probability < 0.0d || probability > 1.0d) + if (double.IsNaN(probability) || probability < 0.0d || probability > 1.0d) throw new ArgumentOutOfRangeException("probability", "Probability must be between 0 and 1."); if (probability == 0.0d) return Minimum; @@ -343,7 +367,7 @@ public override double InverseCDF(double probability) // Validate parameters if (_parametersValid == false) ValidateParameters([Xi, Alpha], true); - return Xi + Alpha * Math.Log(probability / (1d - probability)); + return Xi + Alpha * (Math.Log(probability) - Tools.Log1p(-probability)); } /// @@ -355,6 +379,7 @@ public override UnivariateDistributionBase Clone() /// public double[,] ParameterCovariance(int sampleSize, ParameterEstimationMethod estimationMethod) { + DistributionNumerics.ValidateSampleSize(sampleSize); if (estimationMethod != ParameterEstimationMethod.MethodOfMoments && estimationMethod != ParameterEstimationMethod.MaximumLikelihood) { @@ -365,19 +390,19 @@ public override UnivariateDistributionBase Clone() ValidateParameters([Xi, _alpha], true); // Compute covariance - double a = Alpha; + double a = Alpha / Math.Sqrt(sampleSize); var covar = new double[2, 2]; if (estimationMethod == ParameterEstimationMethod.MethodOfMoments) { - covar[0, 0] = Math.PI * Math.PI / 3d * (a * a / sampleSize); // location - covar[1, 1] = 4d / 5d * (a * a / sampleSize); // scale + covar[0, 0] = Math.PI * Math.PI / 3d * (a * a); // location + covar[1, 1] = 4d / 5d * (a * a); // scale covar[0, 1] = 0.0; // location & scale covar[1, 0] = covar[0, 1]; } else if (estimationMethod == ParameterEstimationMethod.MaximumLikelihood) { - covar[0, 0] = 3d * (a * a / sampleSize); // location - covar[1, 1] = 9d / (3d + Math.PI * Math.PI) * (a * a / sampleSize); // scale + covar[0, 0] = 3d * (a * a); // location + covar[1, 1] = 9d / (3d + Math.PI * Math.PI) * (a * a); // scale covar[0, 1] = 0.0; // location & scale covar[1, 0] = covar[0, 1]; } @@ -387,26 +412,22 @@ public override UnivariateDistributionBase Clone() /// public double QuantileVariance(double probability, int sampleSize, ParameterEstimationMethod estimationMethod) { - var covar = ParameterCovariance(sampleSize, estimationMethod); var grad = QuantileGradient(probability); - double varA = covar[0, 0]; - double varB = covar[1, 1]; - double covAB = covar[1, 0]; - double dQx1 = grad[0]; - double dQx2 = grad[1]; - return Math.Pow(dQx1, 2d) * varA + Math.Pow(dQx2, 2d) * varB + 2d * dQx1 * dQx2 * covAB; + var covariance = new Logistic(0, 1).ParameterCovariance(sampleSize, estimationMethod); + return DistributionNumerics.ScaledQuantileVariance(covariance, grad, Alpha); } /// public double[] QuantileGradient(double probability) { + DistributionNumerics.ValidateProbability(probability); // Validate parameters if (_parametersValid == false) ValidateParameters([Xi, _alpha], true); var gradient = new double[] { 1.0d, // location - Math.Log(probability / (1d - probability)) // scale + (Math.Log(probability) - Tools.Log1p(-probability)) // scale }; return gradient; } @@ -414,26 +435,8 @@ public double[] QuantileGradient(double probability) /// public double[,] QuantileJacobian(IList probabilities, out double determinant) { - if (probabilities.Count != NumberOfParameters) - { - throw new ArgumentOutOfRangeException(nameof(probabilities), "The number of probabilities must be the same length as the number of distribution parameters."); - } - // Get gradients - var dQp1 = QuantileGradient(probabilities[0]); - var dQp2 = QuantileGradient(probabilities[1]); - // Compute determinant - // |a b| - // |c d| - // |A| = ad − bc - double a = dQp1[0]; - double b = dQp1[1]; - double c = dQp2[0]; - double d = dQp2[1]; - determinant = a * d - b * c; - // Return Jacobian - var jacobian = new double[,] { { a, b }, { c, d } }; - return jacobian; + return DistributionNumerics.QuantileJacobian(this, probabilities, out determinant); } } -} \ No newline at end of file +} diff --git a/Numerics/Distributions/Univariate/Mixture.cs b/Numerics/Distributions/Univariate/Mixture.cs index c45736eb..717bf2fe 100644 --- a/Numerics/Distributions/Univariate/Mixture.cs +++ b/Numerics/Distributions/Univariate/Mixture.cs @@ -1,4 +1,4 @@ -using Numerics.Data; +using Numerics.Data; using Numerics.Mathematics.Optimization; using Numerics.Mathematics.RootFinding; using Numerics.Sampling; @@ -203,89 +203,68 @@ private static double BitDecrement(double value) return BitConverter.Int64BitsToDouble(value > 0.0 ? bits - 1 : bits + 1); } - /// - /// Converts a unit-interval draw to a component CDF probability conditional on a positive value. - /// - /// The zero-based component index. - /// The probability on the positive-conditional scale. - /// A component CDF probability strictly above the CDF at zero and strictly below one. - private double PositiveConditionalQuantileProbability(int componentIndex, double conditionalProbability) - { - TryGetPositiveMass(componentIndex, out double positiveMass); - double cdfAtZero = Distributions[componentIndex].CDF(0.0); - double probability = cdfAtZero + conditionalProbability * positiveMass; - if (probability <= cdfAtZero) probability = BitIncrement(cdfAtZero); - if (probability >= 1.0) probability = BitDecrement(1.0); - return probability; - } - /// - /// Gets the probability that a component produces a strictly positive value. - /// - /// The zero-based component index. - /// The strictly positive probability mass. - /// when the mass is finite and positive; otherwise, . - private bool TryGetPositiveMass(int componentIndex, out double positiveMass) + /// Gets the component log probability above zero without requiring representable probability mass. + private double PositiveLogMass(int componentIndex) { - positiveMass = Distributions[componentIndex].CCDF(0.0); - return IsFinite(positiveMass) && positiveMass > 0.0; + double log = Distributions[componentIndex].LogCCDF(0); + if (!IsFinite(log) || log > 0) throw new InvalidOperationException("The active component must have positive probability above zero."); + return log; } - /// - /// Evaluates a component density conditional on a strictly positive value. - /// - /// The zero-based component index. - /// The value at which to evaluate the density. - /// The positive-conditional density. - private double PositiveConditionalPDF(int componentIndex, double x) - { - return x > 0.0 && TryGetPositiveMass(componentIndex, out double positiveMass) - ? Distributions[componentIndex].PDF(x) / positiveMass - : 0.0; - } + /// Evaluates the positive-conditional component log density. + private double PositiveConditionalLogPDF(int componentIndex, double x) => x > 0 + ? Distributions[componentIndex].LogPDF(x) - PositiveLogMass(componentIndex) : double.NegativeInfinity; - /// - /// Evaluates a component log density conditional on a strictly positive value. - /// - /// The zero-based component index. - /// The value at which to evaluate the log density. - /// The positive-conditional log density. - private double PositiveConditionalLogPDF(int componentIndex, double x) - { - return x > 0.0 && TryGetPositiveMass(componentIndex, out double positiveMass) - ? Distributions[componentIndex].LogPDF(x) - Math.Log(positiveMass) - : double.NegativeInfinity; - } + /// Evaluates the positive-conditional component log CDF through an interval probability. + private double PositiveConditionalLogCDF(int componentIndex, double x) => x <= 0 ? double.NegativeInfinity + : Distributions[componentIndex].LogLikelihood_Intervals(0, x) - PositiveLogMass(componentIndex); - /// - /// Evaluates a component distribution function conditional on a strictly positive value. - /// - /// The zero-based component index. - /// The value at which to evaluate the distribution function. - /// The positive-conditional cumulative probability. - /// - /// Uses the survival ratio 1 - S(x) / S(0) so a component's direct survival - /// evaluation can retain upper-tail probability after its CDF has rounded to one. - /// - private double PositiveConditionalCDF(int componentIndex, double x) + /// Evaluates the positive-conditional component log survival directly. + private double PositiveConditionalLogCCDF(int componentIndex, double x) => x <= 0 ? 0 + : Math.Min(0, Distributions[componentIndex].LogCCDF(x) - PositiveLogMass(componentIndex)); + + /// Inverts a positive-conditional component using a direct log-survival equation. + /// Retains the existing root tolerance and iteration limit; it avoids constructing + /// an unconditional probability that can round to one when positive mass is very small. + private double PositiveConditionalQuantile(int componentIndex, double probability) { - if (x <= 0.0 || !TryGetPositiveMass(componentIndex, out double positiveMass)) return 0.0; - double probability = 1.0 - Distributions[componentIndex].CCDF(x) / positiveMass; - return Clamp(probability, 0.0, 1.0); + var distribution = Distributions[componentIndex]; + double minimum = Math.Max(0, distribution.Minimum); + if (probability == 0) return minimum; + if (probability == 1) return distribution.Maximum; + double target = PositiveLogMass(componentIndex) + Tools.Log1p(-probability); + double scale = distribution.InverseCDF(.75) - distribution.InverseCDF(.25); + if (!(scale > 0) || !IsFinite(scale)) scale = Math.Max(1, Math.Abs(minimum)); + double upper = Math.Min(distribution.Maximum, minimum + scale); + for (int i = 0; distribution.LogCCDF(upper) > target && i < 1024; i++) + { + scale *= 2; + double next = minimum + scale; + upper = Math.Min(distribution.Maximum, IsFinite(next) ? next : double.MaxValue); + } + if (!(upper > minimum) || distribution.LogCCDF(upper) > target) + throw new InvalidOperationException("The positive-conditional quantile could not be bracketed."); + // Solve in a unit interval so the existing tolerance does not erase a tiny physical scale. + double width = upper - minimum; + return minimum + width * Brent.Solve(t => distribution.LogCCDF(minimum + width * t) - target, + 0, 1, 1E-6 / Math.Max(1, width), 100, true); } - /// - /// Evaluates a component survival function conditional on a strictly positive value. - /// - /// The zero-based component index. - /// The value at which to evaluate the survival function. - /// The positive-conditional survival probability. - private double PositiveConditionalCCDF(int componentIndex, double x) + private string? _cachedConfiguration; + + /// Refreshes cached moments and interpolation when public arrays or nested components change. + private void RefreshCachedConfiguration() { - if (x < 0.0) return 1.0; - if (!TryGetPositiveMass(componentIndex, out double positiveMass)) return double.NaN; - double probability = Distributions[componentIndex].CCDF(x) / positiveMass; - return Clamp(probability, 0.0, 1.0); + string configuration = DistributionNumerics.ConfigurationState(this); + if (configuration == _cachedConfiguration) return; + _cachedConfiguration = configuration; + _momentsComputed = false; + _empiricalCDFCreated = false; } + + /// Checks mutable weights and current component validity before evaluation. + private void ValidateEvaluation() => ValidateParameters(GetParameters, true); + /// /// Refreshes validity and cached results after zero-inflation configuration changes. /// @@ -447,11 +426,50 @@ public override string[] GetParameterPropertyNames /// private void ComputeMoments() { - var mom = CentralMoments(1000); - u1 = mom[0]; - u2 = mom[1]; - u3 = mom[2]; - u4 = mom[3]; + ValidateEvaluation(); + var components = new List<(double weight, double mean, double sd, double skew, double kurt)>(); + if (IsZeroInflated && ZeroWeight > 0) components.Add((ZeroWeight, 0, 0, 0, 0)); + for (int i = 0; i < Distributions.Length; i++) + { + if (Weights[i] == 0) continue; + var distribution = Distributions[i]; + if (IsZeroInflated && distribution.LogCDF(0) != double.NegativeInfinity) + { + int index = i; + double center = PositiveConditionalQuantile(i, .5); + double scale = PositiveConditionalQuantile(i, .75) - PositiveConditionalQuantile(i, .25); + var moments = DistributionMomentIntegration.Compute(x => PositiveConditionalLogPDF(index, x), + Math.Max(0, distribution.Minimum), distribution.Maximum, center, scale); + components.Add((Weights[i], moments[0], moments[1], moments[2], moments[3])); + } + else components.Add((Weights[i], distribution.Mean, distribution.StandardDeviation, distribution.Skewness, distribution.Kurtosis)); + } + double reference = components[0].mean, offset = 0, totalWeight = 0; + foreach (var component in components) { offset += component.weight * (component.mean - reference); totalWeight += component.weight; } + u1 = reference * totalWeight + offset; + if (!IsFinite(u1)) + { + double magnitude = components.Max(c => Math.Abs(c.mean)); + u1 = magnitude * components.Sum(c => c.weight * (c.mean / magnitude)); + } + double scaleMoment = components.Max(c => Math.Max(c.sd, double.IsInfinity(c.mean - u1) + && IsFinite(c.mean) && IsFinite(u1) ? Math.Max(Math.Abs(c.mean), Math.Abs(u1)) : Math.Abs(c.mean - u1))); + if (!IsFinite(scaleMoment)) { u2 = scaleMoment; u3 = u4 = double.NaN; _momentsComputed = true; return; } + if (scaleMoment == 0) { u2 = 0; u3 = u4 = double.NaN; _momentsComputed = true; return; } + double m2 = 0, m3 = 0, m4 = 0; + foreach (var component in components) + { + double d = DistributionNumerics.Standardize(component.mean, u1, scaleMoment), sd = component.sd / scaleMoment; + double v = sd * sd, d2 = d * d; + double third = sd == 0 ? 0 : component.skew * v * sd; + double fourth = sd == 0 ? 0 : component.kurt * v * v; + m2 += component.weight * (v + d2); + m3 += component.weight * (third + 3 * d * v + d * d2); + m4 += component.weight * (fourth + 4 * d * third + 6 * d2 * v + d2 * d2); + } + u2 = scaleMoment * Math.Sqrt(m2); + u3 = m3 / m2 / Math.Sqrt(m2); + u4 = m4 / m2 / m2; _momentsComputed = true; } @@ -460,7 +478,8 @@ public override double Mean { get { - if (!_momentsComputed) + RefreshCachedConfiguration(); + if (!_momentsComputed) ComputeMoments(); return u1; } @@ -488,7 +507,8 @@ public override double StandardDeviation { get { - if (!_momentsComputed) + RefreshCachedConfiguration(); + if (!_momentsComputed) ComputeMoments(); return u2; } @@ -499,7 +519,8 @@ public override double Skewness { get { - if (!_momentsComputed) + RefreshCachedConfiguration(); + if (!_momentsComputed) ComputeMoments(); return u3; } @@ -510,7 +531,8 @@ public override double Kurtosis { get { - if (!_momentsComputed) + RefreshCachedConfiguration(); + if (!_momentsComputed) ComputeMoments(); return u4; } @@ -519,13 +541,23 @@ public override double Kurtosis /// public override double Minimum { - get { return IsZeroInflated ? 0.0 : Distributions.Min(p => p.Minimum); } + get + { + ValidateEvaluation(); + if (IsZeroInflated && ZeroWeight > 0) return 0; + double minimum = Distributions.Where((d, i) => Weights[i] > 0).Min(d => d.Minimum); + return IsZeroInflated ? Math.Max(0, minimum) : minimum; + } } /// public override double Maximum { - get { return Distributions.Max(p => p.Maximum); } + get + { + ValidateEvaluation(); + return Distributions.Where((d, i) => Weights[i] > 0).Max(d => d.Maximum); + } } /// @@ -762,62 +794,47 @@ public void SetParameters(ref double[] parameters) /// public override ArgumentOutOfRangeException? ValidateParameters(IList parameters, bool throwException) { - if (IsZeroInflated && (!IsFinite(ZeroWeight) || ZeroWeight < 0.0 || ZeroWeight >= 1.0)) - { - var exception = new ArgumentOutOfRangeException( - nameof(ZeroWeight), - "The zero value weight must be finite and greater than or equal to 0 and less than 1."); - if (throwException) throw exception; - return exception; - } - - for (int i = 0; i < Distributions.Count(); i++) - { - if (!IsFinite(Weights[i]) || Weights[i] < 0.0 || Weights[i] > 1.0) - { - var exception = new ArgumentOutOfRangeException( - nameof(Weights), - "The weights must be finite and between 0 and 1."); - if (throwException) throw exception; - return exception; - } - } - - double totalMass = IsZeroInflated ? ZeroWeight : 0.0; - for (int i = 0; i < Distributions.Count(); i++) totalMass += Weights[i]; - if (!IsFinite(totalMass) || !totalMass.AlmostEquals(1.0, 1E-8)) - { - var exception = new ArgumentOutOfRangeException( - nameof(Weights), - IsZeroInflated - ? "The component weights must sum to 1 minus the zero value weight." - : "The weights must sum to 1.0."); - if (throwException) throw exception; - return exception; - } - - for (int i = 0; i < Distributions.Count(); i++) + ArgumentOutOfRangeException? error = null; + if (_distributions is null || _weights is null || _distributions.Length == 0 + || _distributions.Length != _weights.Length || _distributions.Any(d => d is null)) + error = new ArgumentOutOfRangeException(nameof(Distributions), "At least one non-null component and a matching weight vector are required."); + else if (parameters is null || parameters.Count != _weights.Length + _distributions.Sum(d => d.GetParameters.Length)) + error = new ArgumentOutOfRangeException(nameof(parameters), "The flattened parameter count must match the mixture."); + else if (IsZeroInflated && (!IsFinite(ZeroWeight) || ZeroWeight < 0 || ZeroWeight >= 1)) + error = new ArgumentOutOfRangeException(nameof(ZeroWeight), "The zero weight must be finite and in [0,1)."); + else { - if (!Distributions[i].ParametersValid) + int count = Distributions.Length; + double mass = IsZeroInflated ? ZeroWeight : 0; + for (int i = 0; i < count; i++) { - var exception = new ArgumentOutOfRangeException( - nameof(Distributions), - "Distribution " + (i + 1).ToString() + " has invalid parameters."); - if (throwException) throw exception; - return exception; + double weight = parameters[i]; + if (!IsFinite(weight) || weight < 0 || weight > 1) + { error = new ArgumentOutOfRangeException(nameof(Weights), "Weights must be finite and between zero and one."); break; } + mass += weight; } - - if (IsZeroInflated && !TryGetPositiveMass(i, out _)) + if (error is null && (!IsFinite(mass) || !mass.AlmostEquals(1, 1E-8))) + error = new ArgumentOutOfRangeException(nameof(Weights), "Component and zero weights must sum to one."); + int offset = count; + for (int i = 0; i < count && error is null; i++) { - var exception = new ArgumentOutOfRangeException( - nameof(Distributions), - "Distribution " + (i + 1).ToString() + " must have finite, positive probability above zero."); - if (throwException) throw exception; - return exception; + // Nonparametric components expose no flattened scalar parameters. + var candidate = new double[Distributions[i].GetParameters.Length]; + for (int j = 0; j < candidate.Length; j++) candidate[j] = parameters[offset++]; + error = Distributions[i].ValidateParameters(candidate, false); + if (error is null && IsZeroInflated && parameters[i] > 0) + { + var distribution = Distributions[i]; + if (!candidate.SequenceEqual(distribution.GetParameters)) + { distribution = distribution.Clone(); distribution.SetParameters(candidate); } + double logMass = distribution.LogCCDF(0); + if (!IsFinite(logMass) || logMass > 0) + error = new ArgumentOutOfRangeException(nameof(Distributions), "Each active component must have positive probability above zero."); + } } } - - return null; + if (throwException && error != null) throw error; + return error; } /// @@ -864,6 +881,11 @@ public double[] MLE(IList sample) int distributionParameterCount = Distributions.Sum(x => x.NumberOfParameters); int componentCount = Distributions.Count(); + for (int rowIndex = 0; rowIndex < observationCount; rowIndex++) + if (sample[rowIndex] < 0 && Distributions.All(d => d is GammaDistribution || d is Weibull + || d is LnNormal || d is LogNormal || d is LogPearsonTypeIII)) + throw CreateImpossibleRowException(rowIndex, sample[rowIndex]); + if (IsZeroInflated) { for (int rowIndex = 0; rowIndex < observationCount; rowIndex++) @@ -875,6 +897,8 @@ public double[] MLE(IList sample) " has negative exact value " + sample[rowIndex].ToString("R", CultureInfo.InvariantCulture) + " in a zero-inflated model."); } + if (sample[rowIndex] == 0.0 && ZeroWeight == 0.0) + throw CreateImpossibleRowException(rowIndex, sample[rowIndex]); } } @@ -913,33 +937,20 @@ double EStep(double[] parameters) continue; } - double maximumLogProbability = double.NegativeInfinity; + var componentLogs = new double[componentCount]; for (int componentIndex = 0; componentIndex < componentCount; componentIndex++) { - double componentLogDensity = IsZeroInflated + if (mleWeights[componentIndex] == 0) + { + componentLogs[componentIndex] = double.NegativeInfinity; + continue; + } + componentLogs[componentIndex] = IsZeroInflated ? distribution.PositiveConditionalLogPDF(componentIndex, value) : distribution.Distributions[componentIndex].LogPDF(value); - double logProbability = Math.Log(mleWeights[componentIndex]) + componentLogDensity; - responsibilities[rowIndex, componentIndex] = logProbability; - if (logProbability > maximumLogProbability) maximumLogProbability = logProbability; - } - - if (!IsFinite(maximumLogProbability)) - { - throw CreateImpossibleRowException(rowIndex, value); } - - double scaledProbabilitySum = 0.0; - for (int componentIndex = 0; componentIndex < componentCount; componentIndex++) - { - scaledProbabilitySum += Math.Exp(responsibilities[rowIndex, componentIndex] - maximumLogProbability); - } - if (!IsFinite(scaledProbabilitySum) || scaledProbabilitySum <= 0.0) - { - throw CreateImpossibleRowException(rowIndex, value); - } - - double rowLogProbability = maximumLogProbability + Math.Log(scaledProbabilitySum); + var rowResponsibilities = new double[componentCount]; + double rowLogProbability = MixtureLogWeights.Normalize(componentLogs, mleWeights, rowResponsibilities); if (!IsFinite(rowLogProbability)) { throw CreateImpossibleRowException(rowIndex, value); @@ -947,8 +958,7 @@ double EStep(double[] parameters) for (int componentIndex = 0; componentIndex < componentCount; componentIndex++) { - responsibilities[rowIndex, componentIndex] = - Math.Exp(responsibilities[rowIndex, componentIndex] - rowLogProbability); + responsibilities[rowIndex, componentIndex] = rowResponsibilities[componentIndex]; } logLikelihood += rowLogProbability; } @@ -1020,122 +1030,85 @@ InvalidOperationException CreateImpossibleRowException(int rowIndex, double valu } /// - public override double PDF(double x) - { - if (!_parametersValid) ValidateParameters(GetParameters, true); - - if (IsZeroInflated) - { - if (x < 0.0) return 0.0; - if (x == 0.0) return ZeroWeight; - - double positiveDensity = 0.0; - for (int i = 0; i < Distributions.Count(); i++) - { - positiveDensity += Weights[i] * PositiveConditionalPDF(i, x); - } - return Math.Max(0.0, positiveDensity); - } - - double density = 0.0; - for (int i = 0; i < Distributions.Count(); i++) density += Weights[i] * Distributions[i].PDF(x); - return Math.Max(0.0, density); - } + public override double PDF(double x) => Math.Exp(LogPDF(x)); /// public override double LogPDF(double x) { - if (!_parametersValid) ValidateParameters(GetParameters, true); - - if (IsZeroInflated) - { - if (x < 0.0) return double.NegativeInfinity; - if (x == 0.0) return Math.Log(ZeroWeight); - } - - var logDensities = new List(); - for (int i = 0; i < Distributions.Count(); i++) + ValidateEvaluation(); + if (IsZeroInflated && x <= 0) return x == 0 ? Math.Log(ZeroWeight) : double.NegativeInfinity; + double total = double.NegativeInfinity; + for (int i = 0; i < Distributions.Length; i++) { - double componentLogDensity = IsZeroInflated - ? PositiveConditionalLogPDF(i, x) - : Distributions[i].LogPDF(x); - logDensities.Add(Math.Log(Weights[i]) + componentLogDensity); + if (Weights[i] == 0) continue; + double log = IsZeroInflated ? PositiveConditionalLogPDF(i, x) : Distributions[i].LogPDF(x); + total = DistributionNumerics.LogSum(total, Math.Log(Weights[i]) + log); } - return Tools.LogSumExp(logDensities); + return total; } /// - public override double CDF(double x) - { - if (!_parametersValid) ValidateParameters(GetParameters, true); - - if (IsZeroInflated) - { - if (x < 0.0) return 0.0; - if (x == 0.0) return ZeroWeight; - - double hurdleProbability = ZeroWeight; - for (int i = 0; i < Distributions.Count(); i++) - { - hurdleProbability += Weights[i] * PositiveConditionalCDF(i, x); - } - return Clamp(hurdleProbability, 0.0, 1.0); - } - - double probability = 0.0; - for (int i = 0; i < Distributions.Count(); i++) probability += Weights[i] * Distributions[i].CDF(x); - return Clamp(probability, 0.0, 1.0); - } + public override double CDF(double x) => Math.Exp(LogCDF(x)); /// public override double LogCDF(double x) { - if (!_parametersValid) ValidateParameters(GetParameters, true); - if (IsZeroInflated) return Math.Log(CDF(x)); - - var logProbabilities = new List(); - for (int i = 0; i < Distributions.Count(); i++) + ValidateEvaluation(); + if (IsZeroInflated && x < 0) return double.NegativeInfinity; + double total = IsZeroInflated ? Math.Log(ZeroWeight) : double.NegativeInfinity; + for (int i = 0; i < Distributions.Length; i++) { - logProbabilities.Add(Math.Log(Weights[i]) + Distributions[i].LogCDF(x)); + if (Weights[i] == 0) continue; + double log = IsZeroInflated ? PositiveConditionalLogCDF(i, x) : Distributions[i].LogCDF(x); + total = DistributionNumerics.LogSum(total, Math.Log(Weights[i]) + log); } - return Tools.LogSumExp(logProbabilities); + return Math.Min(0, total); } + /// + public override double CCDF(double x) => Math.Exp(LogCCDF(x)); + /// public override double LogCCDF(double x) { - if (!_parametersValid) ValidateParameters(GetParameters, true); - - if (IsZeroInflated) + ValidateEvaluation(); + if (IsZeroInflated && x < 0) return 0; + double total = double.NegativeInfinity; + for (int i = 0; i < Distributions.Length; i++) { - if (x < 0.0) return 0.0; - - double probability = 0.0; - for (int i = 0; i < Distributions.Count(); i++) - { - probability += Weights[i] * PositiveConditionalCCDF(i, x); - } - return Math.Log(Clamp(probability, 0.0, 1.0)); + if (Weights[i] == 0) continue; + double log = IsZeroInflated ? PositiveConditionalLogCCDF(i, x) : Distributions[i].LogCCDF(x); + total = DistributionNumerics.LogSum(total, Math.Log(Weights[i]) + log); } + return Math.Min(0, total); + } - var logProbabilities = new List(); - for (int i = 0; i < Distributions.Count(); i++) + /// Combines component interval log probabilities while retaining the hurdle atom's endpoint convention. + internal double LogIntervalProbability(double lower, double upper) + { + ValidateEvaluation(); + double total = IsZeroInflated && lower < 0 && upper >= 0 ? Math.Log(ZeroWeight) : double.NegativeInfinity; + if (IsZeroInflated && upper <= 0) return total; + for (int i = 0; i < Distributions.Length; i++) { - logProbabilities.Add(Math.Log(Weights[i]) + Distributions[i].LogCCDF(x)); + if (Weights[i] == 0) continue; + double log = Distributions[i].LogLikelihood_Intervals(IsZeroInflated ? Math.Max(0, lower) : lower, upper); + if (IsZeroInflated) log -= Distributions[i].LogCCDF(0); + total = DistributionNumerics.LogSum(total, Math.Log(Weights[i]) + log); } - return Tools.LogSumExp(logProbabilities); + return total; } /// public override double InverseCDF(double probability) { - if (probability < 0.0 || probability > 1.0) + RefreshCachedConfiguration(); + if (!(probability >= 0.0 && probability <= 1.0)) throw new ArgumentOutOfRangeException(nameof(probability), "Probability must be between 0 and 1."); + ValidateEvaluation(); if (probability == 0.0) return Minimum; if (probability == 1.0) return Maximum; if (IsZeroInflated && probability <= ZeroWeight) return 0.0; - if (!_parametersValid) ValidateParameters(GetParameters, true); - if (Distributions.Count() == 1 && !IsZeroInflated) { return Distributions[0].InverseCDF(probability); @@ -1153,12 +1126,9 @@ public override double InverseCDF(double probability) var componentQuantiles = new List(); for (int i = 0; i < Distributions.Count(); i++) { - double componentCdfProbability = componentProbability; - if (IsZeroInflated) - { - componentCdfProbability = PositiveConditionalQuantileProbability(i, componentProbability); - } - componentQuantiles.Add(Distributions[i].InverseCDF(componentCdfProbability)); + if (Weights[i] == 0) continue; + componentQuantiles.Add(IsZeroInflated ? PositiveConditionalQuantile(i, componentProbability) + : Distributions[i].InverseCDF(componentProbability)); } double lowerBound = componentQuantiles.Min(); @@ -1166,8 +1136,13 @@ public override double InverseCDF(double probability) double value; try { - if (lowerBound.AlmostEquals(upperBound)) return Clamp(lowerBound, Minimum, Maximum); - value = Brent.Solve(y => probability - CDF(y), lowerBound, upperBound, 1E-6, 100, true); + if (lowerBound == upperBound) return Clamp(lowerBound, Minimum, Maximum); + double width = upperBound - lowerBound; + double Argument(double t) => IsFinite(width) ? lowerBound + width * t : (1 - t) * lowerBound + t * upperBound; + double Residual(double t) => probability <= .5 ? LogCDF(Argument(t)) - Math.Log(probability) + : LogCCDF(Argument(t)) - Tools.Log1p(-probability); + double scale = IsFinite(width) ? width : Math.Max(Math.Abs(lowerBound), Math.Abs(upperBound)); + value = Argument(Brent.Solve(Residual, 0, 1, 1E-6 / Math.Max(1, scale), 100, true)); } catch (Exception) { @@ -1186,15 +1161,10 @@ public void CreateEmpiricalCDF() // Get min & max double minP = 1E-16; double maxP = 1 - 1E-16; - double minX = Distributions.Min(d => d.InverseCDF(minP)); - double maxX = IsZeroInflated - ? Distributions.Select((distribution, index) => - { - TryGetPositiveMass(index, out double positiveMass); - double probability = distribution.CDF(0.0) + maxP * positiveMass; - return distribution.InverseCDF(Math.Min(probability, 1.0 - 1E-15)); - }).Max() - : Distributions.Max(d => d.InverseCDF(maxP)); + RefreshCachedConfiguration(); + var activeIndices = Enumerable.Range(0, Distributions.Length).Where(i => Weights[i] > 0).ToArray(); + double minX = IsZeroInflated ? 0 : activeIndices.Min(i => Distributions[i].InverseCDF(minP)); + double maxX = activeIndices.Max(i => IsZeroInflated ? PositiveConditionalQuantile(i, maxP) : Distributions[i].InverseCDF(maxP)); // Get number of bins double shift = 0; if (minX <= 0) shift = Math.Abs(minX) + 1d; @@ -1235,10 +1205,11 @@ public void CreateEmpiricalCDF() /// public override double[] GenerateRandomValues(int sampleSize, int seed = -1) { - if (!_parametersValid) ValidateParameters(GetParameters, true); + ValidateEvaluation(); var random = seed > 0 ? new MersenneTwister(seed) : new MersenneTwister(); var sample = new double[sampleSize]; + int lastActiveComponent = Array.FindLastIndex(Weights, weight => weight > 0); for (int sampleIndex = 0; sampleIndex < sampleSize; sampleIndex++) { double mixtureProbability = random.NextDouble(); @@ -1252,13 +1223,15 @@ public override double[] GenerateRandomValues(int sampleSize, int seed = -1) double cumulativeWeight = IsZeroInflated ? ZeroWeight : 0.0; for (int componentIndex = 0; componentIndex < Distributions.Count(); componentIndex++) { + if (Weights[componentIndex] == 0) continue; cumulativeWeight += Weights[componentIndex]; - if (mixtureProbability <= cumulativeWeight || componentIndex == Distributions.Count() - 1) + if (mixtureProbability <= cumulativeWeight || componentIndex == lastActiveComponent) { double probability = componentProbability; if (IsZeroInflated) { - probability = PositiveConditionalQuantileProbability(componentIndex, componentProbability); + sample[sampleIndex] = PositiveConditionalQuantile(componentIndex, componentProbability); + break; } sample[sampleIndex] = Distributions[componentIndex].InverseCDF(probability); break; diff --git a/Numerics/Distributions/Univariate/Normal.cs b/Numerics/Distributions/Univariate/Normal.cs index db0ce8b7..942ad3de 100644 --- a/Numerics/Distributions/Univariate/Normal.cs +++ b/Numerics/Distributions/Univariate/Normal.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -214,6 +214,7 @@ public override double[] MaximumOfParameters /// public void Estimate(IList sample, ParameterEstimationMethod estimationMethod) { + DistributionNumerics.ValidateSample(sample, 4); if (estimationMethod == ParameterEstimationMethod.MethodOfMoments) { SetParameters(Statistics.ProductMoments(sample)); @@ -331,21 +332,29 @@ public double[] LinearMomentsFromParameters(IList parameters) /// public Tuple GetParameterConstraints(IList sample) { - var initialVals = new double[NumberOfParameters]; - var lowerVals = new double[NumberOfParameters]; - var upperVals = new double[NumberOfParameters]; - // Estimate initial values using the method of moments (a.k.a product moments). - var moments = Statistics.ProductMoments(sample); - initialVals[0] = moments[0]; - initialVals[1] = moments[1]; - // Get bounds of mean - if (initialVals[0] == 0d) initialVals[0] = Tools.DoubleMachineEpsilon; - lowerVals[0] = -Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); - upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); - // Get bounds of standard deviation - lowerVals[1] = Tools.DoubleMachineEpsilon; - upperVals[1] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[1]) + 1d)); - return new Tuple(initialVals, lowerVals, upperVals); + DistributionNumerics.ValidateSample(sample, 4); + // Scale before computing sample moments so squaring large observations cannot overflow. + double magnitude = 0d; + for (int i = 0; i < sample.Count; i++) magnitude = Math.Max(magnitude, Math.Abs(sample[i])); + var scaled = new double[sample.Count]; + for (int i = 0; i < sample.Count; i++) scaled[i] = sample[i] / magnitude; + double[] moments = Statistics.ProductMoments(scaled); + double location = moments[0] * magnitude; + double scale = moments[1] * magnitude; + if (!(scale > 0d) || double.IsInfinity(scale) || double.IsNaN(location) || double.IsInfinity(location)) + throw new ArgumentException("Sample moments must be finite with positive dispersion.", nameof(sample)); + // Preserve the distribution's minimum representable fitted scale. + scale = Math.Max(1E-16, scale); + // Retain the established decade bounds for ordinary nonzero centers; use dispersion + // only when the center is zero, and retain finite bounds when a decade overflows. + double locationMagnitude = location == 0 ? scale : Math.Abs(location); + double locationBound = Math.Pow(10, Math.Ceiling(Math.Log10(locationMagnitude) + 1)); + double scaleBound = Math.Pow(10, Math.Ceiling(Math.Log10(scale) + 1)); + if (double.IsInfinity(locationBound)) locationBound = double.MaxValue; + if (double.IsInfinity(scaleBound)) scaleBound = double.MaxValue; + if (Math.Abs(location) >= locationBound || scale >= scaleBound) + throw new ArgumentException("Sample moments do not admit finite interior parameter bounds.", nameof(sample)); + return Tuple.Create(new[] { location, scale }, new[] { -locationBound, Math.Min(Tools.DoubleMachineEpsilon, scale / 10d) }, new[] { locationBound, scaleBound }); } /// @@ -373,11 +382,7 @@ double logLH(double[] x) /// public override double PDF(double x) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Mu, Sigma, true); - double z = (x - Mu) / Sigma; - return Math.Exp(-0.5d * z * z) / (Tools.Sqrt2PI * Sigma); + return Math.Exp(LogPDF(x)); } /// @@ -387,23 +392,39 @@ public override double PDF(double x) /// public override double LogPDF(double x) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Mu, Sigma, true); - double z = (x - Mu) / Sigma; - double lf = -0.5d * z * z - Math.Log(Tools.Sqrt2PI * Sigma); - return double.IsNaN(lf) ? double.NegativeInfinity : lf; + if (!_parametersValid) ValidateParameters(Mu, Sigma, true); + double z = DistributionNumerics.Standardize(x, Mu, Sigma); + return -0.5d * z * z - Math.Log(Sigma) - Math.Log(Tools.Sqrt2PI); } /// public override double CDF(double x) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Mu, Sigma, true); - // Evaluated through the standard normal routine, which holds relative accuracy deep in - // both tails where the error function complement form loses the probability entirely - return StandardCDF((x - Mu) / Sigma); + if (!_parametersValid) ValidateParameters(Mu, Sigma, true); + return StandardCDF(DistributionNumerics.Standardize(x, Mu, Sigma)); + } + + /// + /// Evaluates the lower tail directly in logarithmic form, including probabilities below floating-point range. + public override double LogCDF(double x) + { + if (!_parametersValid) ValidateParameters(Mu, Sigma, true); + return DistributionNumerics.NormalLogCDF(DistributionNumerics.Standardize(x, Mu, Sigma)); + } + + /// + public override double CCDF(double x) + { + if (!_parametersValid) ValidateParameters(Mu, Sigma, true); + return StandardCDF(-DistributionNumerics.Standardize(x, Mu, Sigma)); + } + + /// + /// Evaluates the upper tail directly without subtracting a rounded CDF from one. + public override double LogCCDF(double x) + { + if (!_parametersValid) ValidateParameters(Mu, Sigma, true); + return DistributionNumerics.NormalLogSurvival(DistributionNumerics.Standardize(x, Mu, Sigma)); } private static readonly double[] a = [3.3871328727963666080, 1.3314166789178437745e+2, 1.9715909503065514427e+3, 1.3731693765509461125e+4, 4.5921953931549871457e+4, 6.7265770927008700853e+4, 3.3430575583588128105e+4, 2.5090809287301226727e+3]; @@ -579,7 +600,7 @@ private static double r8poly_value(int n, double[] a, double x) public override double InverseCDF(double probability) { // Validate probability - if (probability < 0.0d || probability > 1.0d) + if (double.IsNaN(probability) || probability < 0.0d || probability > 1.0d) throw new ArgumentOutOfRangeException("probability", "Probability must be between 0 and 1."); if (probability == 0.0d) return Minimum; @@ -651,7 +672,7 @@ public static double[] StandardCDF(IList zValues) public static double StandardZ(double probability) { // Validate probability - if (probability < 0.0d || probability > 1.0d) + if (double.IsNaN(probability) || probability < 0.0d || probability > 1.0d) throw new ArgumentOutOfRangeException("probability", "Probability must be between 0 and 1."); if (probability == 0) return r8_normal_01_cdf_inverse(double.Epsilon); if (probability == 1) return r8_normal_01_cdf_inverse(1-double.Epsilon); @@ -682,6 +703,7 @@ public static double[] StandardZ(IList probabilities) /// public double[,] NormalConfidenceIntervals(int sampleSize, IList quantiles, IList percentiles) { + DistributionNumerics.ValidateConfidenceInputs(sampleSize, quantiles, percentiles); // validate parameters if (_parametersValid == false) ValidateParameters(Mu, _sigma, true); @@ -716,6 +738,7 @@ public static double[] StandardZ(IList probabilities) /// public double[,] NoncentralTConfidenceIntervals(int sampleSize, IList quantiles, IList percentiles) { + DistributionNumerics.ValidateConfidenceInputs(sampleSize, quantiles, percentiles, 2); // validate parameters if (_parametersValid == false) ValidateParameters(Mu, _sigma, true); @@ -749,6 +772,8 @@ public static double[] StandardZ(IList probabilities) /// public double[,] MonteCarloConfidenceIntervals(int sampleSize, int realizations, IList quantiles, IList percentiles) { + DistributionNumerics.ValidateConfidenceInputs(sampleSize, quantiles, percentiles, 2); + if (realizations <= 0) throw new ArgumentOutOfRangeException(nameof(realizations), "At least one realization is required."); // validate parameters if (_parametersValid == false) ValidateParameters(Mu, _sigma, true); @@ -802,6 +827,8 @@ public static double[] StandardZ(IList probabilities) /// Exceedance probability. public double ExpectedProbability(int sampleSize, double probability) { + DistributionNumerics.ValidateProbability(probability); + if (sampleSize < 2) throw new ArgumentOutOfRangeException(nameof(sampleSize), "At least two observations are required for a positive Student-t degree of freedom."); int N = sampleSize; var T = new StudentT(N - 1); return T.CDF(StandardZ(probability) * Math.Sqrt(N / (double)(N + 1))); @@ -816,6 +843,8 @@ public override UnivariateDistributionBase Clone() /// public double[,] ParameterCovariance(int sampleSize, ParameterEstimationMethod estimationMethod) { + DistributionNumerics.ValidateSampleSize(sampleSize); + if (!_parametersValid) ValidateParameters(Mu, Sigma, true); if (estimationMethod != ParameterEstimationMethod.MethodOfMoments && estimationMethod != ParameterEstimationMethod.MaximumLikelihood) { @@ -823,11 +852,12 @@ public override UnivariateDistributionBase Clone() } // Compute covariance in (μ, σ) parameterization. // Var(μ̂) = σ²/n, Var(σ̂) = σ²/(2n), Cov = 0. - // Both MoM and MLE give the same result for Normal (UMVUE). - double s2 = Sigma * Sigma; + // Both supported estimators have the same leading asymptotic covariance. + double scaled = Sigma / Math.Sqrt(sampleSize); + double s2 = scaled * scaled; var covar = new double[2, 2]; - covar[0, 0] = s2 / sampleSize; // Var(μ̂) - covar[1, 1] = s2 / (2.0 * sampleSize); // Var(σ̂) + covar[0, 0] = s2; // Var(μ̂) + covar[1, 1] = s2 / 2d; // Var(σ̂) covar[0, 1] = 0.0; covar[1, 0] = covar[0, 1]; return covar; @@ -836,19 +866,15 @@ public override UnivariateDistributionBase Clone() /// public double QuantileVariance(double probability, int sampleSize, ParameterEstimationMethod estimationMethod) { - var covar = ParameterCovariance(sampleSize, estimationMethod); var grad = QuantileGradient(probability); - double varA = covar[0, 0]; - double varB = covar[1, 1]; - double covAB = covar[1, 0]; - double dQx1 = grad[0]; - double dQx2 = grad[1]; - return Math.Pow(dQx1, 2d) * varA + Math.Pow(dQx2, 2d) * varB + 2d * dQx1 * dQx2 * covAB; + var covariance = new Normal(0, 1).ParameterCovariance(sampleSize, estimationMethod); + return DistributionNumerics.ScaledQuantileVariance(covariance, grad, Sigma); } /// public double[] QuantileGradient(double probability) { + DistributionNumerics.ValidateProbability(probability); // Validate parameters if (_parametersValid == false) ValidateParameters(Mu, _sigma, true); @@ -865,25 +891,7 @@ public double[] QuantileGradient(double probability) /// public double[,] QuantileJacobian(IList probabilities, out double determinant) { - if (probabilities.Count != NumberOfParameters) - { - throw new ArgumentOutOfRangeException(nameof(probabilities), "The number of probabilities must be the same length as the number of distribution parameters."); - } - // Get gradients - var dQp1 = QuantileGradient(probabilities[0]); - var dQp2 = QuantileGradient(probabilities[1]); - // Compute determinant - // |a b| - // |c d| - // |A| = ad − bc - double a = dQp1[0]; - double b = dQp1[1]; - double c = dQp2[0]; - double d = dQp2[1]; - determinant = a * d - b * c; - // Return Jacobian - var jacobian = new double[,] { { a, b }, { c, d } }; - return jacobian; + return DistributionNumerics.QuantileJacobian(this, probabilities, out determinant); } /// @@ -925,4 +933,4 @@ public override double[] ConditionalMoments(double a, double b) } } -} \ No newline at end of file +} diff --git a/Numerics/Distributions/Univariate/PearsonTypeIII.cs b/Numerics/Distributions/Univariate/PearsonTypeIII.cs index 744164fd..ba9c7a1c 100644 --- a/Numerics/Distributions/Univariate/PearsonTypeIII.cs +++ b/Numerics/Distributions/Univariate/PearsonTypeIII.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Numerics.Data.Statistics; @@ -94,7 +94,7 @@ public double Gamma /// public double Xi { - get { return Mu - 2.0d * Sigma / Gamma; } + get { return Mu - Sigma * (2d / Gamma); } } /// @@ -110,7 +110,72 @@ public double Beta /// public double Alpha { - get { return 4.0d / Math.Pow(Gamma, 2d); } + get { return Math.Pow(2d / Gamma, 2d); } + } + + /// + public override double LogCDF(double x) => LogTail(x, false); + + /// + public override double CCDF(double x) => Math.Exp(LogCCDF(x)); + + /// + public override double LogCCDF(double x) => LogTail(x, true); + + /// Evaluates the signed gamma tail without subtracting a rounded probability from one. + private double LogTail(double x, bool upper) + { + if (!_parametersValid) ValidateParameters(Mu, Sigma, Gamma, true); + if (x <= Minimum) return upper ? 0d : double.NegativeInfinity; + if (x >= Maximum) return upper ? double.NegativeInfinity : 0d; + double z = DistributionNumerics.Standardize(x, Mu, Sigma); + double normal = upper ? DistributionNumerics.NormalLogSurvival(z) : DistributionNumerics.NormalLogCDF(z); + if (Gamma == 0d) return normal; + if (UseLocalTailExpansion(z)) + { + // Integrate the gamma cumulant expansion analytically using probabilists' Hermite polynomials. + double z2 = z * z, g2 = Gamma * Gamma; + double h2 = z2 - 1d, h3 = z * (z2 - 3d), h4 = z2 * z2 - 6d * z2 + 3d; + double h5 = z * (z2 * z2 - 10d * z2 + 15d); + double h6 = z2 * z2 * z2 - 15d * z2 * z2 + 45d * z2 - 15d; + double h8 = z2 * z2 * z2 * z2 - 28d * z2 * z2 * z2 + 210d * z2 * z2 - 420d * z2 + 105d; + double correction = Gamma * h2 / 6d + g2 * (h3 / 16d + h5 / 72d) + + g2 * Gamma * (h4 / 40d + h6 / 96d + h8 / 1296d); + double relative = Math.Exp(-0.5d * z2 - Math.Log(Tools.Sqrt2PI) - normal) * correction; + return normal + Tools.Log1p(upper ? relative : -relative); + } + double unit = UnitGammaValue(x, z); + return upper == (Gamma > 0d) ? DistributionNumerics.GammaLogSurvival(Alpha, unit) : DistributionNumerics.GammaLogCDF(Alpha, unit); + } + + /// Forms the unit gamma coordinate in centered form for large shape. + private double UnitGammaValue(double x, double z) + { + if (x == Xi) return 0d; + return Math.Abs(Gamma) < 1d ? Alpha * (1d + (Gamma / 2d) * z) : DistributionNumerics.Standardize(x, Xi, Beta); + } + + /// Bounds local tail-expansion error while retaining every nonzero skew. + private bool UseLocalTailExpansion(double z) + { + return Math.Abs(Gamma) <= 1E-3 && Math.Abs(Gamma) * Math.Pow(1d + Math.Abs(z), 3d) <= 1E-3; + } + + /// Avoids cancellation in centered gamma quantiles only where the skew expansion is accurate. + private bool UseLocalQuantileExpansion(double z) + { + return Math.Abs(Gamma) <= 1E-3 && Math.Abs(Gamma) * Math.Pow(1d + Math.Abs(z), 3d) <= 0.02d; + } + + /// Evaluates the smooth gamma quantile expansion and its skew derivative through cubic order. + private double LocalStandardQuantile(double z, out double derivative) + { + double z2 = z * z; + double first = (z2 - 1d) / 6d; + double second = z * (z2 - 7d) / 144d; + double third = -(3d * z2 * z2 + 7d * z2 - 16d) / 6480d; + derivative = first + Gamma * (2d * second + 3d * Gamma * third); + return z + Gamma * (first + Gamma * (second + Gamma * third)); } /// @@ -189,18 +254,7 @@ public override double Median /// public override double Mode { - get - { - if (Math.Abs(Gamma) <= NearZero) - { - // Use Normal - return Mu; - } - else - { - return Xi + (Alpha - 1d) * Beta; - } - } + get { return Math.Abs(Gamma) >= 2d ? Xi : Mu - Sigma * (Gamma / 2d); } } /// @@ -233,43 +287,13 @@ public override double Kurtosis /// public override double Minimum { - get - { - if (Math.Abs(Gamma) <= NearZero) - { - // Use Normal - return double.NegativeInfinity; - } - else if (Beta > 0d) - { - return Xi; - } - else - { - return double.NegativeInfinity; - } - } + get { return Gamma > 0d ? Xi : double.NegativeInfinity; } } /// public override double Maximum { - get - { - if (Math.Abs(Skewness) <= NearZero) - { - // Use Normal - return double.PositiveInfinity; - } - else if (Beta > 0d) - { - return double.PositiveInfinity; - } - else - { - return Xi; - } - } + get { return Gamma < 0d ? Xi : double.PositiveInfinity; } } /// @@ -287,6 +311,7 @@ public override double[] MaximumOfParameters /// public void Estimate(IList sample, ParameterEstimationMethod estimationMethod) { + DistributionNumerics.ValidateSample(sample, 4); if (estimationMethod == ParameterEstimationMethod.MethodOfMoments) { SetParameters(Statistics.ProductMoments(sample)); @@ -512,28 +537,13 @@ public double[] LinearMomentsFromParameters(IList parameters) /// public Tuple GetParameterConstraints(IList sample) { - var initialVals = new double[NumberOfParameters]; - var lowerVals = new double[NumberOfParameters]; - var upperVals = new double[NumberOfParameters]; - // Get initial values - var moments = Statistics.ProductMoments(sample); - initialVals = moments.Subset(0, 2); - // Get bounds of mean - lowerVals[0] = -Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[0]) + 1d)); - upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[0]) + 1d)); - // Get bounds of standard deviation - lowerVals[1] = Tools.DoubleMachineEpsilon; - upperVals[1] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[1]) + 1d)); - // Get bounds of skew - lowerVals[2] = -6d; - upperVals[2] = 6d; - - // Correct initial value of skew if necessary - if (initialVals[2] <= lowerVals[2] || initialVals[2] >= upperVals[2]) - { - initialVals[2] = 0.01; - } - return new Tuple(initialVals, lowerVals, upperVals); + DistributionNumerics.ValidateSample(sample, 4); + var normal = new Normal().GetParameterConstraints(sample); + var scaled = new double[sample.Count]; + for (int i = 0; i < sample.Count; i++) scaled[i] = DistributionNumerics.Standardize(sample[i], normal.Item1[0], normal.Item1[1]); + double skew = Statistics.ProductMoments(scaled)[2]; + if (double.IsNaN(skew) || double.IsInfinity(skew) || skew <= -6d || skew >= 6d) skew = 0.01d; + return Tuple.Create(new[] { normal.Item1[0], normal.Item1[1], skew }, new[] { normal.Item2[0], normal.Item2[1], -6d }, new[] { normal.Item3[0], normal.Item3[1], 6d }); } /// @@ -562,28 +572,7 @@ double logLH(double[] x) /// public override double PDF(double x) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Mu, Sigma, Gamma, true); - if (x < Minimum || x > Maximum) return 0.0d; - - if (Math.Abs(Gamma) <= NearZero) - { - // Use Normal distribution - double z = (x - Mu) / Sigma; - return Math.Exp(-0.5d * z * z) / (Tools.Sqrt2PI * Sigma); - } - // Use Gamma distribution - if (Beta > 0d) - { - double shiftedX = x - Xi; - return Math.Exp(-shiftedX / Math.Abs(Beta) + (Alpha - 1.0d) * Math.Log(shiftedX) - Alpha * Math.Log(Math.Abs(Beta)) - Mathematics.SpecialFunctions.Gamma.LogGamma(Alpha)); - } - else - { - double shiftedX = Xi - x; - return Math.Exp(-shiftedX / Math.Abs(Beta) + (Alpha - 1.0d) * Math.Log(shiftedX) - Alpha * Math.Log(Math.Abs(Beta)) - Mathematics.SpecialFunctions.Gamma.LogGamma(Alpha)); - } + return Math.Exp(LogPDF(x)); } /// @@ -596,83 +585,43 @@ public override double PDF(double x) /// public override double LogPDF(double x) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Mu, Sigma, Gamma, true); - if (x < Minimum || x > Maximum) return double.NegativeInfinity; - - double lf; - if (Math.Abs(Gamma) <= NearZero) - { - // Use Normal distribution - double z = (x - Mu) / Sigma; - lf = -0.5d * z * z - Math.Log(Tools.Sqrt2PI * Sigma); - } - else if (Beta > 0d) - { - // Use Gamma distribution - double shiftedX = x - Xi; - lf = -shiftedX / Math.Abs(Beta) + (Alpha - 1.0d) * Math.Log(shiftedX) - Alpha * Math.Log(Math.Abs(Beta)) - Mathematics.SpecialFunctions.Gamma.LogGamma(Alpha); - } - else + if (!_parametersValid) ValidateParameters(Mu, Sigma, Gamma, true); + if (x < Minimum || x > Maximum || double.IsInfinity(x)) return double.NegativeInfinity; + double z = DistributionNumerics.Standardize(x, Mu, Sigma); + if (Gamma == 0d) return -0.5d * z * z - Math.Log(Sigma) - Math.Log(Tools.Sqrt2PI); + if (UseLocalTailExpansion(z)) { - double shiftedX = Xi - x; - lf = -shiftedX / Math.Abs(Beta) + (Alpha - 1.0d) * Math.Log(shiftedX) - Alpha * Math.Log(Math.Abs(Beta)) - Mathematics.SpecialFunctions.Gamma.LogGamma(Alpha); + // Log gamma density expanded in skew; the gate bounds the omitted fourth-order term. + double z2 = z * z, g2 = Gamma * Gamma; + double correction = Gamma * z * (z2 / 6d - 0.5d) + + g2 * (-z2 * z2 / 16d + z2 / 8d - 1d / 48d) + + g2 * Gamma * z * z2 * (z2 / 40d - 1d / 24d) + + g2 * g2 * z2 * z2 * (1d / 64d - z2 / 96d); + return -0.5d * z2 - Math.Log(Sigma) - Math.Log(Tools.Sqrt2PI) + correction; } - return double.IsNaN(lf) ? double.NegativeInfinity : lf; + double unit = UnitGammaValue(x, z); + return DistributionNumerics.GammaLogDensity(Alpha, unit) - Math.Log(Sigma) - Math.Log(Math.Abs(Gamma) / 2d); } /// public override double CDF(double x) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Mu, Sigma, Gamma, true); - if (x <= Minimum) - return 0d; - if (x >= Maximum) - return 1d; - if (Math.Abs(Gamma) <= NearZero) - { - return 0.5d * (1.0d + Mathematics.SpecialFunctions.Erf.Function((x - Mu) / (Sigma * Math.Sqrt(2.0d)))); - } - else if (Beta > 0d) - { - double shiftedX = x - Xi; - return Mathematics.SpecialFunctions.Gamma.LowerIncomplete(Alpha, shiftedX / Math.Abs(Beta)); - } - else - { - double shiftedX = Xi - x; - return 1.0d - Mathematics.SpecialFunctions.Gamma.LowerIncomplete(Alpha, shiftedX / Math.Abs(Beta)); - } + return Math.Exp(LogCDF(x)); } /// public override double InverseCDF(double probability) { - // Validate probability - if (probability < 0.0d || probability > 1.0d) - throw new ArgumentOutOfRangeException("probability", "Probability must be between 0 and 1."); - if (probability == 0.0d) - return Minimum; - if (probability == 1.0d) - return Maximum; - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Mu, Sigma, Gamma, true); - if (Math.Abs(Gamma) <= NearZero) - { - return Mu + Sigma * Normal.StandardZ(probability); - } - else if (Beta > 0d) - { - return Xi + Mathematics.SpecialFunctions.Gamma.InverseLowerIncomplete(Alpha, probability) * Math.Abs(Beta); - } - else - { - return Xi - Mathematics.SpecialFunctions.Gamma.InverseLowerIncomplete(Alpha, 1d - probability) * Math.Abs(Beta); - } + if (double.IsNaN(probability) || probability < 0d || probability > 1d) + throw new ArgumentOutOfRangeException(nameof(probability), "Probability must be between zero and one."); + if (!_parametersValid) ValidateParameters(Mu, Sigma, Gamma, true); + if (probability == 0d) return Minimum; + if (probability == 1d) return Maximum; + double z = Normal.StandardZ(probability); + if (Gamma == 0d) return Mu + Sigma * z; + if (UseLocalQuantileExpansion(z)) return Mu + Sigma * LocalStandardQuantile(z, out _); + double unit = DistributionNumerics.GammaInverseCDF(Alpha, probability, Gamma < 0d); + return Mu + Sigma * ((Gamma / 2d) * (unit - Alpha)); } /// @@ -685,7 +634,7 @@ public override double InverseCDF(double probability) public double WilsonHilfertyInverseCDF(double probability) { // Validate probability - if (probability < 0.0d || probability > 1.0d) + if (double.IsNaN(probability) || probability < 0.0d || probability > 1.0d) throw new ArgumentOutOfRangeException("probability", "Probability must be between 0 and 1."); if (probability == 0.0d) return Minimum; @@ -705,130 +654,85 @@ public override UnivariateDistributionBase Clone() } /// + /// + /// Uses the public mean, standard deviation and skew coordinates for both estimators. + /// Regular maximum-likelihood information exists only for absolute skew below sqrt(2), + /// including the full zero-skew limit diag(sigma^2, sigma^2/2, 6)/n. + /// + /// The sample size is nonpositive, the parameters are invalid, or maximum-likelihood covariance is requested outside its regular domain. public double[,] ParameterCovariance(int sampleSize, ParameterEstimationMethod estimationMethod) { - if (estimationMethod != ParameterEstimationMethod.MethodOfMoments && - estimationMethod != ParameterEstimationMethod.MaximumLikelihood) - { + DistributionNumerics.ValidateSampleSize(sampleSize); + if (estimationMethod != ParameterEstimationMethod.MethodOfMoments && estimationMethod != ParameterEstimationMethod.MaximumLikelihood) throw new NotImplementedException(); - } - // Validate parameters - if (_parametersValid == false) - ValidateParameters(_mu, _sigma, _gamma, true); - // Compute covariance in user-facing (μ, σ, γ) parameterization. - var covar = new double[3, 3]; + if (!_parametersValid) ValidateParameters(Mu, Sigma, Gamma, true); + double h = Gamma * Gamma / 4d; + double scaledSigma = Sigma / Math.Sqrt(sampleSize), s2 = scaledSigma * scaledSigma; + var covariance = new double[3, 3]; + covariance[0, 0] = s2; + covariance[0, 1] = covariance[1, 0] = Gamma * s2 / 2d; if (estimationMethod == ParameterEstimationMethod.MethodOfMoments) { - // MoM asymptotic covariance via Cov = D⁻¹·S·D⁻ᵀ / n. - // Moment conditions centered on model mean μ(θ): - // g₁ = X−μ, g₂ = (X−μ)²−σ², g₃ = (X−μ)³−γσ³. - // Jacobian D = ∂E[g]/∂(μ,σ,γ) is lower-triangular: - // D = [[-1, 0, 0], [0, -2σ, 0], [-3σ², -3γσ², -σ³]] - // Note: D[2,0] = E[∂g₃/∂μ] = E[-3(X−μ)²] = -3σ² ≠ 0 because - // g₃ centers on μ(θ), not on x̄. - // D⁻¹ = [[-1, 0, 0], [0, -1/(2σ), 0], [3/σ, 3γ/(2σ²), -1/σ³]] - double s = _sigma, g = _gamma; - double s2 = s * s, s3 = s2 * s, s4 = s2 * s2, s5 = s4 * s, s6 = s4 * s2; - double g2 = g * g, g4 = g2 * g2; - // Central moments for Pearson III family - double mu2 = s2; - double mu3 = g * s3; - double mu4 = s4 * (3.0 + 1.5 * g2); - double mu5 = s5 * g * (10.0 + 3.0 * g2); - double mu6 = s6 * (15.0 + 32.5 * g2 + 7.5 * g4); - // S matrix elements: S = E[g·gᵀ] - double S00 = mu2; - double S01 = mu3; - double S02 = mu4; - double S11 = mu4 - mu2 * mu2; - double S12 = mu5 - mu2 * mu3; - double S22 = mu6 - mu3 * mu3; - // D⁻¹ elements (lower-triangular) - double a = -1.0; // D⁻¹[0,0] - double b = -1.0 / (2.0 * s); // D⁻¹[1,1] - double e = 3.0 / s; // D⁻¹[2,0] — from D[2,0] = -3σ² - double c = 3.0 * g / (2.0 * s2); // D⁻¹[2,1] - double d = -1.0 / s3; // D⁻¹[2,2] - // Cov = D⁻¹ · S · D⁻ᵀ / n (exploit triangular structure) - covar[0, 0] = a * a * S00 / sampleSize; - covar[0, 1] = a * b * S01 / sampleSize; - covar[0, 2] = a * (e * S00 + c * S01 + d * S02) / sampleSize; - covar[1, 1] = b * b * S11 / sampleSize; - covar[1, 2] = b * (e * S01 + c * S11 + d * S12) / sampleSize; - covar[2, 2] = (e * e * S00 + 2.0 * e * c * S01 + 2.0 * e * d * S02 - + c * c * S11 + 2.0 * c * d * S12 + d * d * S22) / sampleSize; - covar[1, 0] = covar[0, 1]; - covar[2, 0] = covar[0, 2]; - covar[2, 1] = covar[1, 2]; + covariance[1, 1] = s2 * (0.5d + 1.5d * h); + covariance[1, 2] = covariance[2, 1] = 1.5d * Sigma * Gamma * (1d + h) / sampleSize; + covariance[2, 2] = 6d * (1d + h) * (1d + 5d * h) / sampleSize; } else { - // MLE: Fisher information inverse in internal (μ, α=1/β, λ=4/γ²) parameterization. - // This parameterization matches QuantileGradient for MLE QuantileVariance computation. - double alpha = 1d / Beta; - double lambda = Alpha; - double A = 2d * Mathematics.SpecialFunctions.Gamma.Trigamma(lambda) - 2d / (lambda - 1d) + 1d / Math.Pow(lambda - 1d, 2d); - covar[0, 0] = (lambda - 2d) / (sampleSize * A) * (1d / Math.Pow(alpha, 2d)) * (Mathematics.SpecialFunctions.Gamma.Trigamma(lambda) * lambda - 1d); // location - covar[1, 1] = (lambda - 2d) * Math.Pow(alpha, 2d) / (sampleSize * A) * (Mathematics.SpecialFunctions.Gamma.Trigamma(lambda) / (lambda - 2d) - 1d / Math.Pow(lambda - 1d, 2d)); // scale - covar[2, 2] = 2d / (sampleSize * A); // shape - covar[0, 1] = 1d / sampleSize * ((lambda - 2d) / A) * (Mathematics.SpecialFunctions.Gamma.Trigamma(lambda) - 1d / (lambda - 1d)); // location & scale - covar[1, 0] = covar[0, 1]; - covar[0, 2] = (2d - lambda) / (sampleSize * alpha * A * (lambda - 1d)); // location & shape - covar[2, 0] = covar[0, 2]; - covar[1, 2] = alpha / (sampleSize * A * (lambda - 1d)); // scale & shape - covar[2, 1] = covar[1, 2]; + if (!(Math.Abs(Gamma) < Math.Sqrt(2d))) + throw new ArgumentOutOfRangeException(nameof(Gamma), "Regular three-parameter maximum-likelihood covariance requires absolute skew below sqrt(2), equivalently gamma shape above two."); + // T = a^3 [trigamma(a)-1/(a-1)+1/(2(a-1)^2)], a=4/gamma^2. + // Expand only the cancellation-prone trigamma residual; the rational contribution is exact. + double t; + if (h < 0.05d) + { + double h2 = h * h; + t = 1d / 6d + h2 * (-1d / 30d + h2 * (1d / 42d + h2 * (-1d / 30d + h2 * (5d / 66d - h2 * 691d / 2730d)))) + + h / (2d * (1d - h) * (1d - h)); + } + else + { + double shape = 1d / h; + t = (DistributionNumerics.AccurateTrigamma(shape) - 1d / (shape - 1d) + 0.5d / Math.Pow(shape - 1d, 2d)) / (h * h * h); + } + covariance[1, 1] = s2 * (0.5d + h / (4d * (1d - h) * (1d - h) * t)); + covariance[1, 2] = covariance[2, 1] = Sigma * Gamma / (4d * (1d - h) * t * sampleSize); + covariance[2, 2] = 1d / (t * sampleSize); } - return covar; + return covariance; } /// public double QuantileVariance(double probability, int sampleSize, ParameterEstimationMethod estimationMethod) { - if (estimationMethod == ParameterEstimationMethod.MethodOfMoments) - { - double u2 = _sigma; - double c = _gamma; - double c2 = Math.Pow(c, 2d); - int N = sampleSize; - return Math.Pow(u2, 2d) / N * (1d + Math.Pow(GammaDistribution.FrequencyFactorKp(c, probability), 2d) / 2d * (1d + 0.75d * c2) + GammaDistribution.FrequencyFactorKp(c, probability) * c + 6d * (1d + 0.25d * c2) * GammaDistribution.PartialKp(c, probability) * (GammaDistribution.PartialKp(c, probability) * (1d + 5d * c2 / 4d) + GammaDistribution.FrequencyFactorKp(c, probability) / 2d * c)); - } - else if (estimationMethod == ParameterEstimationMethod.MaximumLikelihood) - { - var covar = ParameterCovariance(sampleSize, estimationMethod); - var grad = QuantileGradient(probability); - double varA = covar[0, 0]; - double varB = covar[1, 1]; - double varG = covar[2, 2]; - double covAB = covar[1, 0]; - double covAG = covar[2, 0]; - double covBG = covar[2, 1]; - double dQx1 = grad[0]; - double dQx2 = grad[1]; - double dQx3 = grad[2]; - return Math.Pow(dQx1, 2d) * varA + Math.Pow(dQx2, 2d) * varB + Math.Pow(dQx3, 2d) * varG + 2d * dQx1 * dQx2 * covAB + 2d * dQx1 * dQx3 * covAG + 2d * dQx2 * dQx3 * covBG; - } - - return double.NaN; + DistributionNumerics.ValidateProbability(probability); + DistributionNumerics.ValidateSampleSize(sampleSize); + if (estimationMethod != ParameterEstimationMethod.MethodOfMoments && estimationMethod != ParameterEstimationMethod.MaximumLikelihood) + return double.NaN; + if (!_parametersValid) ValidateParameters(Mu, Sigma, Gamma, true); + var standardized = new PearsonTypeIII(0, 1, Gamma); + double[,] covariance = standardized.ParameterCovariance(sampleSize, estimationMethod); + return DistributionNumerics.ScaledQuantileVariance(covariance, standardized.QuantileGradient(probability), Sigma); } /// + /// Differentiates the actual signed-gamma quantile implicitly in public mean, SD and skew coordinates. A smooth local gamma expansion avoids cancellation at zero skew. public double[] QuantileGradient(double probability) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Mu, Sigma, Gamma, true); - // Gradient in internal (μ, α=1/β, λ=4/γ²) parameterization. - // Matches ParameterCovariance(MLE) for MLE QuantileVariance computation. - double alpha = 1d / Beta; - double lambda = Alpha; - double eps = Math.Sign(alpha); - var gradient = new double[] + DistributionNumerics.ValidateProbability(probability); + if (!_parametersValid) ValidateParameters(Mu, Sigma, Gamma, true); + double z = Normal.StandardZ(probability); + if (Gamma == 0d) return [1d, z, Sigma * (z * z - 1d) / 6d]; + if (UseLocalQuantileExpansion(z)) { - 1.0d, // location - -lambda / Math.Pow(alpha, 2d) * (1.0d + eps / Math.Sqrt(lambda) * GammaDistribution.FrequencyFactorKp(Gamma, probability)), // scale - 1.0d / alpha * (1.0d + eps / Math.Sqrt(lambda) * (GammaDistribution.FrequencyFactorKp(Gamma, probability) / 2.0d) - 1.0d / lambda * GammaDistribution.PartialKp(Gamma, probability)) // shape - }; - return gradient; + double quantile = LocalStandardQuantile(z, out double derivative); + return [1d, quantile, Sigma * derivative]; + } + double unit = DistributionNumerics.GammaInverseCDF(Alpha, probability, Gamma < 0d); + double shapeDerivative = DistributionNumerics.GammaQuantileShapeDerivative(Alpha, unit); + double standardized = Gamma / 2d * (unit - Alpha); + double skewDerivative = (unit + Alpha) / 2d - Alpha * shapeDerivative; + return [1d, standardized, Sigma * skewDerivative]; } /// @@ -837,47 +741,13 @@ public double[] QuantileGradient(double probability) /// Probability between 0 and 1. public double[] QuantileGradientForMoments(double probability) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Mu, Sigma, Gamma, true); - var gradient = new double[] - { - 1.0d, - GammaDistribution.FrequencyFactorKp(Gamma, probability), - Sigma * GammaDistribution.PartialKp(Gamma, probability) - }; - return gradient; + return QuantileGradient(probability); } /// public double[,] QuantileJacobian(IList probabilities, out double determinant) { - if (probabilities.Count != NumberOfParameters) - { - throw new ArgumentOutOfRangeException(nameof(probabilities), "The number of probabilities must be the same length as the number of distribution parameters."); - } - // Get gradients - var dQp1 = QuantileGradientForMoments(probabilities[0]); - var dQp2 = QuantileGradientForMoments(probabilities[1]); - var dQp3 = QuantileGradientForMoments(probabilities[2]); - // Compute determinant - // |a b c| - // |d e f| - // |g h i| - // |A| = a(ei − fh) − b(di − fg) + c(dh − eg) - double a = dQp1[0]; - double b = dQp1[1]; - double c = dQp1[2]; - double d = dQp2[0]; - double e = dQp2[1]; - double f = dQp2[2]; - double g = dQp3[0]; - double h = dQp3[1]; - double i = dQp3[2]; - determinant = a * (e * i - f * h) - b * (d * i - f * g) + c * (d * h - e * g); - // Return Jacobian - var jacobian = new double[,] { { a, b, c }, { d, e, f }, { g, h, i } }; - return jacobian; + return DistributionNumerics.QuantileJacobian(this, probabilities, out determinant); } /// diff --git a/Numerics/Distributions/Univariate/Uncertainty Analysis/StandardErrorExtensions.cs b/Numerics/Distributions/Univariate/Uncertainty Analysis/StandardErrorExtensions.cs new file mode 100644 index 00000000..0bdd2fb1 --- /dev/null +++ b/Numerics/Distributions/Univariate/Uncertainty Analysis/StandardErrorExtensions.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; + +namespace Numerics.Distributions +{ + /// Stable, additive quantile-uncertainty operations for existing distributions. + public static class StandardErrorExtensions + { + /// Returns the logarithm of the absolute quantile Jacobian determinant. + /// A distribution whose gradients use its public parameter coordinates. + /// One finite, strictly interior probability per parameter, in row order. + /// The log absolute determinant, or negative infinity for an exactly singular Jacobian. + /// An argument is null. + /// The probability count or an individual probability is invalid. + /// A derivative is not finite. + /// Equilibrates rows and columns before pivoting and sums log pivots. This avoids forming + /// a determinant that can overflow or underflow while its logarithm remains representable. + /// No interface members are added and no artificial singularity pivots are used. + public static double LogAbsQuantileJacobian(this IStandardError distribution, IList probabilities) + { + var matrix = DistributionNumerics.QuantileGradientMatrix(distribution, probabilities); + return DistributionNumerics.LogAbsDeterminant(matrix, out _); + } + } +} diff --git a/Numerics/Distributions/Univariate/Weibull.cs b/Numerics/Distributions/Univariate/Weibull.cs index c1661afa..cdb9b23e 100644 --- a/Numerics/Distributions/Univariate/Weibull.cs +++ b/Numerics/Distributions/Univariate/Weibull.cs @@ -132,13 +132,17 @@ public override double[] GetParameters /// public override double Mean { - get { return Lambda * Gamma.Function(1.0d + 1.0d / Kappa); } + get + { + if (Kappa == 1) return Lambda; + return Math.Exp(Math.Log(Lambda) + Gamma.LogGamma(1 + 1 / Kappa)); + } } /// public override double Median { - get { return Lambda * Math.Pow(Math.Log(2.0d), 1.0d / Kappa); } + get { return InverseCDF(.5); } } /// @@ -158,9 +162,20 @@ public override double Mode } /// + /// Uses the exact exponential-power relationship to GEV and restores scale in + /// logarithms, without forming lambda squared or overflowing raw Gamma moments. public override double StandardDeviation { - get { return Math.Sqrt(Lambda * Lambda * Gamma.Function(1.0d + 2.0d / Kappa) - Mean * Mean); } + get + { + if (Kappa == 1) return Lambda; + double power = 1 / Kappa; + if (double.IsPositiveInfinity(power)) return double.PositiveInfinity; + double logarithm = power <= .05 + ? Math.Log(power) + Math.Log(new GeneralizedExtremeValue(0, 1, power).StandardDeviation) + : .5 * GeneralizedExtremeValue.LogPowerVariance(power); + return Math.Exp(Math.Log(Lambda) + logarithm); + } } /// @@ -168,9 +183,10 @@ public override double Skewness { get { - double mu = Mean; - double sigma = StandardDeviation; - return (Gamma.Function(1.0d + 3.0d / Kappa) * Math.Pow(Lambda, 3.0d) - 3.0d * mu * sigma * sigma - Math.Pow(mu, 3.0d)) / Math.Pow(sigma, 3.0d); + double power = 1 / Kappa; + // For T unit exponential, T^power = 1 - power*GEV(0,1,power). + return double.IsPositiveInfinity(power) ? double.PositiveInfinity + : -new GeneralizedExtremeValue(0, 1, power).Skewness; } } @@ -179,13 +195,9 @@ public override double Kurtosis { get { - double g1 = Gamma.Function(1d + 1d / Kappa); - double g2 = Gamma.Function(1d + 2d / Kappa); - double g3 = Gamma.Function(1d + 3d / Kappa); - double g4 = Gamma.Function(1d + 4d / Kappa); - double num = -6 * Math.Pow(g1, 4d) + 12d * g2 * Math.Pow(g1, 2d) - 3d * Math.Pow(g2, 2d) - 4d * g1 * g3 + g4; - double den = Math.Pow(g2 - Math.Pow(g1, 2d), 2d); - return 3d + num / den; + double power = 1 / Kappa; + return double.IsPositiveInfinity(power) ? double.PositiveInfinity + : new GeneralizedExtremeValue(0, 1, power).Kurtosis; } } @@ -251,6 +263,8 @@ public void SetParameters(double scale, double shape) /// public override void SetParameters(IList parameters) { + if (parameters == null || parameters.Count != NumberOfParameters) + throw new ArgumentOutOfRangeException(nameof(parameters), "Exactly two parameters are required."); SetParameters(parameters[0], parameters[1]); } @@ -280,23 +294,28 @@ public override void SetParameters(IList parameters) /// public override ArgumentOutOfRangeException? ValidateParameters(IList parameters, bool throwException) { + if (parameters == null || parameters.Count != NumberOfParameters) + { + var exception = new ArgumentOutOfRangeException(nameof(parameters), "Exactly two parameters are required."); + if (throwException) throw exception; + return exception; + } return ValidateParameters(parameters[0], parameters[1], throwException); } /// + /// Requires finite, strictly positive, nonconstant observations. Returned bounds + /// retain a representable small scale and the existing Weibull MLE initialization. + /// The sample or a finite feasible initialization is invalid. public Tuple GetParameterConstraints(IList sample) { - var initialVals = new double[NumberOfParameters]; + DistributionNumerics.ValidateSample(sample, 2, true); var lowerVals = new double[NumberOfParameters]; var upperVals = new double[NumberOfParameters]; // Get initial values - initialVals = SolveMLE(sample); - // Get bounds of scale - lowerVals[0] = Tools.DoubleMachineEpsilon; - upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[0]) + 1d)); - // Get bounds of shape - lowerVals[1] = Tools.DoubleMachineEpsilon; - upperVals[1] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[1]) + 1d)); + var initialVals = SolveMLE(sample); + DistributionNumerics.PositiveParameterBounds(initialVals[0], out lowerVals[0], out upperVals[0]); + DistributionNumerics.PositiveParameterBounds(initialVals[1], out lowerVals[1], out upperVals[1]); return new Tuple(initialVals, lowerVals, upperVals); } @@ -328,6 +347,9 @@ double logLH(double[] x) /// The array of sample data. /// /// Implemented according to: Parameter estimation of the Weibull probability distribution, 1994, Hongzhu Qiao, Chris P. Tsokos + /// All observations must be finite and strictly positive, with at least two distinct + /// observations. The same fixed-point iteration is evaluated with log-relative bounded + /// weights, retaining every observation and the existing convergence threshold. /// /// References: /// This code was copied and modified from the Math.NET Library. @@ -338,12 +360,19 @@ double logLH(double[] x) /// /// /// + /// The initial scale and shape estimates. + /// The observations are invalid, insufficient or constant. + /// The initialization iteration is numerically unresolved. public double[] SolveMLE(IList samples) { + DistributionNumerics.ValidateSample(samples, 2, true); double n = samples.Count; - if (n <= 1d) + double scale = DistributionNumerics.InitializationScale(samples); + var logRatios = new double[samples.Count]; + for (int i = 0; i < samples.Count; i++) { - throw new Exception("Observations not sufficient. There must be more than 1 data point."); + double ratio = samples[i] / scale; + logRatios[i] = ratio > 0 ? Math.Log(ratio) : Math.Log(samples[i]) - Math.Log(scale); } double s1 = 0d; @@ -360,31 +389,28 @@ public double[] SolveMLE(IList samples) s1 = 0d; s2 = 0d; s3 = 0d; - foreach (double x in samples) + foreach (double logarithm in logRatios) { - if (x > 0d) - { - s1 += Math.Log(x); - s2 += Math.Pow(x, c); - s3 += Math.Pow(x, c) * Math.Log(x); - } + double weight = Math.Exp(c * logarithm); + s1 += logarithm; + s2 += weight; + s3 += weight * logarithm; } QofC = n * s2 / (n * s3 - s1 * s2); previousC = c; c = (c + QofC) / 2d; + if (!(c > 0) || !DistributionNumerics.IsFinite(c)) + throw new InvalidOperationException("The Weibull initialization iteration did not produce a finite positive shape."); } // solve for scale - foreach (double x in samples) + foreach (double logarithm in logRatios) { - if (x > 0d) - { - b += Math.Pow(x, c); - } + b += Math.Exp(c * logarithm); } - b = Math.Pow(b / n, 1d / c); + b = scale * Math.Pow(b / n, 1d / c); // return parameters return [b, c]; @@ -393,18 +419,7 @@ public double[] SolveMLE(IList samples) /// public override double PDF(double x) { - // Validate parameters - if (_parametersValid == false) - ValidateParameters(Lambda, Kappa, true); - if (x < Minimum) return 0.0d; - if (x == 0.0d && Kappa == 1.0d) - { - return Kappa / Lambda; - } - else - { - return Kappa / Lambda * Math.Pow(x / Lambda, Kappa - 1.0d) * Math.Exp(-Math.Pow(x / Lambda, Kappa)); - } + return Math.Exp(LogPDF(x)); } /// @@ -419,12 +434,12 @@ public override double LogPDF(double x) // Validate parameters if (_parametersValid == false) ValidateParameters(Lambda, Kappa, true); - if (x < Minimum) return double.NegativeInfinity; - if (x == 0.0d && Kappa == 1.0d) - { - return Math.Log(Kappa / Lambda); - } - double lf = Math.Log(Kappa / Lambda) + (Kappa - 1.0d) * Math.Log(x / Lambda) - Math.Pow(x / Lambda, Kappa); + if (x < Minimum || double.IsPositiveInfinity(x)) return double.NegativeInfinity; + if (x == 0) return Kappa == 1 ? -Math.Log(Lambda) : Kappa < 1 ? double.PositiveInfinity : double.NegativeInfinity; + double logarithm = LogStandardizedValue(x); + double power = Math.Exp(Kappa * logarithm); + if (double.IsPositiveInfinity(power)) return double.NegativeInfinity; + double lf = Math.Log(Kappa) - Math.Log(Lambda) + (Kappa - 1) * logarithm - power; return double.IsNaN(lf) ? double.NegativeInfinity : lf; } @@ -434,16 +449,44 @@ public override double CDF(double x) // Validate parameters if (_parametersValid == false) ValidateParameters(Lambda, Kappa, true); - if (x < Minimum) + if (x <= Minimum) return 0d; - return 1d - Math.Exp(-Math.Pow(x / Lambda, Kappa)); + return -Tools.Expm1(-Math.Exp(Kappa * LogStandardizedValue(x))); + } + + /// + /// Retains the lower-tail logarithm when the positive power underflows. + public override double LogCDF(double x) + { + if (!_parametersValid) ValidateParameters(Lambda, Kappa, true); + if (x <= 0) return double.NegativeInfinity; + double logarithm = Kappa * LogStandardizedValue(x); + double power = Math.Exp(logarithm); + return power == 0 ? logarithm : DistributionNumerics.Log1mExp(-power); + } + + /// + public override double CCDF(double x) => Math.Exp(LogCCDF(x)); + + /// + public override double LogCCDF(double x) + { + if (!_parametersValid) ValidateParameters(Lambda, Kappa, true); + return x <= 0 ? 0 : -Math.Exp(Kappa * LogStandardizedValue(x)); + } + + /// Forms log(x/lambda) without an overflowing or underflowing intermediate ratio. + private double LogStandardizedValue(double x) + { + double ratio = x / Lambda; + return ratio > 0 && !double.IsInfinity(ratio) ? Math.Log(ratio) : Math.Log(x) - Math.Log(Lambda); } /// public override double InverseCDF(double probability) { // Validate probability - if (probability < 0.0d || probability > 1.0d) + if (!(probability >= 0.0d && probability <= 1.0d)) throw new ArgumentOutOfRangeException("probability", "Probability must be between 0 and 1."); if (probability == 0.0d) return Minimum; @@ -453,7 +496,12 @@ public override double InverseCDF(double probability) if (_parametersValid == false) ValidateParameters(Lambda, Kappa, true); // Compute the inverse CDF - return Lambda * Math.Pow(Math.Log(1d / (1d - probability)), 1d / Kappa); + // The exact exponential identity preserves subnormal probabilities on .NET Framework. + if (Kappa == 1) return -Lambda * Tools.Log1p(-probability); + double logarithm = Math.Log(-Tools.Log1p(-probability)) / Kappa; + double unitQuantile = Math.Exp(logarithm); + return unitQuantile < 1E-200 || double.IsInfinity(unitQuantile) + ? Math.Exp(Math.Log(Lambda) + logarithm) : Lambda * unitQuantile; } /// @@ -463,8 +511,12 @@ public override UnivariateDistributionBase Clone() } /// + /// Retains the published rounded MLE covariance constants in scale and shape coordinates. + /// Parameters are invalid or sample size is not positive. + /// The method is not maximum likelihood. public double[,] ParameterCovariance(int sampleSize, ParameterEstimationMethod estimationMethod) { + DistributionNumerics.ValidateSampleSize(sampleSize); if (estimationMethod != ParameterEstimationMethod.MaximumLikelihood) { throw new NotImplementedException(); @@ -477,38 +529,41 @@ public override UnivariateDistributionBase Clone() double a = Lambda; double b = Kappa; var covar = new double[2, 2]; - covar[0, 0] = 1.108665d * a * a / (sampleSize * b * b); // scale - covar[1, 1] = 0.607927d * b * b / sampleSize; // shape + double scaledA = (a / b) / Math.Sqrt(sampleSize); + double scaledB = b / Math.Sqrt(sampleSize); + covar[0, 0] = 1.108665d * (scaledA * scaledA); // scale + covar[1, 1] = 0.607927d * (scaledB * scaledB); // shape covar[0, 1] = 0.257022d * a / sampleSize; covar[1, 0] = covar[0, 1]; return covar; } /// + /// Uses the actual inverse-CDF gradient and restores scale after the normalized covariance contraction. public double QuantileVariance(double probability, int sampleSize, ParameterEstimationMethod estimationMethod) { - var covar = ParameterCovariance(sampleSize, estimationMethod); + DistributionNumerics.ValidateProbability(probability); + if (!_parametersValid) ValidateParameters(Lambda, Kappa, true); + var covar = new Weibull(1, Kappa).ParameterCovariance(sampleSize, estimationMethod); var grad = QuantileGradient(probability); - double varA = covar[0, 0]; - double varB = covar[1, 1]; - double covAB = covar[1, 0]; - double dQx1 = grad[0]; - double dQx2 = grad[1]; - return Math.Pow(dQx1, 2d) * varA + Math.Pow(dQx2, 2d) * varB + 2d * dQx1 * dQx2 * covAB; + return DistributionNumerics.ScaledQuantileVariance(covar, [InverseCDF(probability), grad[1]]); } /// + /// For t=-log(1-p), returns [Q/lambda, -Q*log(t)/kappa squared]. + /// Parameters are invalid or probability is not finite and strictly interior. public double[] QuantileGradient(double probability) { + DistributionNumerics.ValidateProbability(probability); // Validate parameters if (_parametersValid == false) ValidateParameters(_lambda, _kappa, true); - double a = Lambda; - double b = Kappa; + double logT = Math.Log(-Tools.Log1p(-probability)); + double quantile = InverseCDF(probability); var gradient = new double[] { - Math.Log(Math.Pow(1d / (1d - probability), 1d / Kappa)), // scale - a * Math.Log(1d - probability) / (b * b) // shape + Math.Exp(logT / Kappa), // scale + -(quantile / Kappa) * (logT / Kappa) // shape }; return gradient; } @@ -516,25 +571,7 @@ public double[] QuantileGradient(double probability) /// public double[,] QuantileJacobian(IList probabilities, out double determinant) { - if (probabilities.Count != NumberOfParameters) - { - throw new ArgumentOutOfRangeException(nameof(probabilities), "The number of probabilities must be the same length as the number of distribution parameters."); - } - // Get gradients - var dQp1 = QuantileGradient(probabilities[0]); - var dQp2 = QuantileGradient(probabilities[1]); - // Compute determinant - // |a b| - // |c d| - // |A| = ad − bc - double a = dQp1[0]; - double b = dQp1[1]; - double c = dQp2[0]; - double d = dQp2[1]; - determinant = a * d - b * c; - // Return Jacobian - var jacobian = new double[,] { { a, b }, { c, d } }; - return jacobian; + return DistributionNumerics.QuantileJacobian(this, probabilities, out determinant); } } diff --git a/Test_Numerics/Distributions/Univariate/DistributionOracle.cs b/Test_Numerics/Distributions/Univariate/DistributionOracle.cs new file mode 100644 index 00000000..1f1a5ddb --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/DistributionOracle.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; + +namespace Distributions.Univariate +{ + /// Reads frozen, runtime-independent distribution oracle CSV resources. + internal static class DistributionOracle + { + /// Reads the quoted scalar-column fixture format emitted by the standalone R generators. + internal static IEnumerable> Read(string suffix) + { + var assembly = typeof(DistributionOracle).Assembly; + string name = assembly.GetManifestResourceNames().Single(x => x.EndsWith(suffix, StringComparison.Ordinal)); + using var reader = new StreamReader(assembly.GetManifestResourceStream(name)!); + string[] columns = reader.ReadLine()!.Split(',').Select(x => x.Trim('"')).ToArray(); + while (!reader.EndOfStream) + { + string[] cells = reader.ReadLine()!.Split(',').Select(x => x.Trim('"')).ToArray(); + if (cells.Length != columns.Length) throw new InvalidDataException("Unexpected distribution oracle CSV columns."); + yield return columns.Select((key, index) => new { key, value = cells[index] }).ToDictionary(x => x.key, x => x.value); + } + } + + /// Parses invariant R numeric output and explicit nonfinite classifications. + internal static double Number(string text) => text == "Inf" ? double.PositiveInfinity : text == "-Inf" + ? double.NegativeInfinity : text == "NA" || text == "NaN" ? double.NaN : double.Parse(text, CultureInfo.InvariantCulture); + } +} diff --git a/Test_Numerics/Distributions/Univariate/Test_CompositeRobustness.cs b/Test_Numerics/Distributions/Univariate/Test_CompositeRobustness.cs new file mode 100644 index 00000000..633ba4ea --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Test_CompositeRobustness.cs @@ -0,0 +1,208 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Distributions; + +namespace Distributions.Univariate +{ + /// Probability, support, moments and mutable-dependency regressions for composites. + [TestClass] + public class Test_CompositeRobustness + { + /// The minimum of independent unit exponentials is exactly exponential with rate two. + [TestMethod] + public void CompetingExponentialLogDensityHasNoFabricatedFloor() + { + var distribution = new CompetingRisks(new UnivariateDistributionBase[] { new Exponential(0, 1), new Exponential(0, 1) }); + Assert.AreEqual(double.NegativeInfinity, distribution.LogPDF(-1)); + Assert.AreEqual(0, distribution.PDF(-1)); + Assert.AreEqual(Math.Log(2) - 2000, distribution.LogPDF(1000), 3E-12); + Assert.AreEqual(-2000, distribution.LogCCDF(1000), 3E-12); + } + + /// Minima and maxima use the appropriate intersection of finite endpoint bounds. + [TestMethod] + public void CompetingSupportUsesMinMaxOperation() + { + var distribution = new CompetingRisks(new UnivariateDistributionBase[] { new Uniform(0, 1), new Uniform(2, 3) }); + Assert.AreEqual(1, distribution.InverseCDF(1)); + Assert.AreEqual(0, distribution.PDF(1.5)); + distribution.MinimumOfRandomVariables = false; + Assert.AreEqual(2, distribution.InverseCDF(0)); + Assert.AreEqual(0, distribution.PDF(1.5)); + } + + /// Zero-weight singular components cannot contaminate density, tails, or support. + [TestMethod] + public void MixtureIgnoresInactiveSingularComponents() + { + var distribution = new Mixture(new[] { 0d, 1d }, new UnivariateDistributionBase[] { new GammaDistribution(1, .5), new Normal(0, 1) }); + Assert.AreEqual(1 / Math.Sqrt(2 * Math.PI), distribution.PDF(0), 2E-15); + Assert.AreEqual(-.5 * Math.Log(2 * Math.PI), distribution.LogPDF(0), 2E-15); + var bounded = new Mixture(new[] { 1d, 0d }, new UnivariateDistributionBase[] { new Uniform(0, 1), new Uniform(2, 3) }); + Assert.AreEqual(1, bounded.Maximum); + Assert.AreEqual(1, bounded.InverseCDF(1)); + } + + /// Component central moments preserve translation and respond to mutable weights/parameters. + [TestMethod] + public void MixtureMomentsCombineCentrallyAndRefresh() + { + var distribution = new Mixture(new[] { .5, .5 }, new UnivariateDistributionBase[] { new Normal(1E10, 1), new Normal(1E10, 1) }); + Assert.AreEqual(1E10, distribution.Mean); + Assert.AreEqual(1, distribution.StandardDeviation, 1E-14); + Assert.AreEqual(0, distribution.Skewness, 1E-14); + Assert.AreEqual(3, distribution.Kurtosis, 1E-14); + distribution.Distributions[0].SetParameters(new[] { 0d, 1d }); + distribution.Distributions[1].SetParameters(new[] { 4d, 1d }); + distribution.Weights[0] = .25; + distribution.Weights[1] = .75; + Assert.AreEqual(3, distribution.Mean, 1E-14); + Assert.AreEqual(2, distribution.StandardDeviation, 1E-14); + var separated = new Mixture(new[] { .9, .1 }, new UnivariateDistributionBase[] + { new Normal(-1E308, 1E200), new Normal(1E308, 1E200) }); + Assert.AreEqual(-8E307, separated.Mean, 2E292); + Assert.AreEqual(6E307, separated.StandardDeviation, 3E292); + Assert.AreEqual(8d / 3, separated.Skewness, 3E-14); + Assert.AreEqual(73d / 9, separated.Kurtosis, 5E-14); + } + + /// Hurdle probability tails remain logarithmic and interval endpoint atoms are included correctly. + [TestMethod] + public void HurdleLogTailsAndIntervalAtomsRemainCorrect() + { + var distribution = new Mixture(new[] { 1d }, new UnivariateDistributionBase[] { new Exponential(0, 1) }) { IsZeroInflated = true, ZeroWeight = .2 }; + Assert.AreEqual(Math.Log(.8) - 1000, distribution.LogCCDF(1000), 2E-12); + Assert.AreEqual(Math.Log(.2), distribution.LogLikelihood_Intervals(-1, 0), 2E-14); + Assert.AreEqual(Math.Log(.8) + Math.Log(1 - Math.Exp(-1)), distribution.LogLikelihood_Intervals(0, 1), 2E-14); + var remote = new Mixture(new[] { 1d }, new UnivariateDistributionBase[] { new Normal(-40, 1) }) { IsZeroInflated = true, ZeroWeight = .2 }; + Assert.AreEqual(Math.Log(.8) - .5 * 40.1 * 40.1 - .5 * Math.Log(2 * Math.PI) + 804.6084420137538, remote.LogPDF(.1), 3E-12); + } + + /// Candidate validation checks the supplied vector independently of the current valid object. + [TestMethod] + public void CompositeValidationUsesCandidateParameters() + { + var competing = new CompetingRisks(new UnivariateDistributionBase[] { new Normal(), new Normal() }); + Assert.IsNotNull(competing.ValidateParameters(new[] { 0d, -1d, 0d, 1d }, false)); + Assert.IsNotNull(competing.ValidateParameters(new[] { 0d }, false)); + var mixture = new Mixture(new[] { .5, .5 }, new UnivariateDistributionBase[] { new Normal(), new Normal() }); + Assert.IsNotNull(mixture.ValidateParameters(new[] { -1d, 2d, 0d, 1d, 0d, 1d }, false)); + Assert.IsNotNull(mixture.ValidateParameters(new[] { .5, .5, 0d, -1d, 0d, 1d }, false)); + } + + /// Endpoint density uses the joint one-sided limit even when component factors are infinity times zero. + [TestMethod] + public void CompetingEndpointDensityCombinesPowersBeforeTakingLimit() + { + // F_Gamma(1/2,1)(x) ~ 2 sqrt(x/pi), hence d(F squared)/dx -> 4/pi. + var maximum = new CompetingRisks(new UnivariateDistributionBase[] { new GammaDistribution(1, .5), new GammaDistribution(1, .5) }) + { MinimumOfRandomVariables = false }; + Assert.AreEqual(4 / Math.PI, maximum.PDF(0), 2E-14); + maximum.Distributions[1].SetParameters(new[] { 1d, .25 }); + Assert.AreEqual(double.PositiveInfinity, maximum.LogPDF(0)); + maximum.Distributions[1].SetParameters(new[] { 1d, .75 }); + Assert.AreEqual(double.NegativeInfinity, maximum.LogPDF(0)); + // The minimum of two GPA(0,1,2) variables has constant density two up to .5. + var minimum = new CompetingRisks(new UnivariateDistributionBase[] { new GeneralizedPareto(0, 1, 2), new GeneralizedPareto(0, 1, 2) }); + Assert.AreEqual(2, minimum.PDF(.5), 2E-14); + foreach (var component in new UnivariateDistributionBase[] + { new GeneralizedLogistic(0, 1, 2), new GeneralizedExtremeValue(0, 1, 2), new KappaFour(0, 1, 2, -1) }) + { + minimum = new CompetingRisks(new[] { component, component.Clone() }); + Assert.AreEqual(2, minimum.PDF(.5), 2E-14, component.DisplayName); + } + var kappa = new KappaFour(0, 1, 1, 2); + maximum = new CompetingRisks(new UnivariateDistributionBase[] { kappa, kappa.Clone() }) { MinimumOfRandomVariables = false }; + Assert.AreEqual(2, maximum.PDF(.5), 2E-14); + foreach (var component in new UnivariateDistributionBase[] + { new PearsonTypeIII(.25, .5, 4), new LogPearsonTypeIII(.25, .5, 4) { Base = Math.E } }) + { + maximum = new CompetingRisks(new[] { component, component.Clone(), component.Clone(), component.Clone() }) { MinimumOfRandomVariables = false }; + // R 4.4.3: exp(-4*lgamma(1.25)). Both transformed endpoints have unit local scale. + Assert.AreEqual(1.4815477904878236, maximum.PDF(component.Minimum), 3E-14, component.DisplayName); + } + var reflected = new LogPearsonTypeIII(0, 2, -2) { Base = Math.E }; + maximum = new CompetingRisks(new UnivariateDistributionBase[] { reflected, reflected.Clone() }) { MinimumOfRandomVariables = false }; + Assert.AreEqual(Math.Exp(-2), maximum.PDF(0), 2E-14); + } + + /// Checked integration covers complete support and refreshes after a min/max change. + [TestMethod] + public void CompetingMomentsUseFullSupportAndRefreshConfiguration() + { + var distribution = new CompetingRisks(new UnivariateDistributionBase[] { new Exponential(0, 1), new Exponential(0, 1) }); + Assert.AreEqual(.5, distribution.Mean, 2E-9); + Assert.AreEqual(.5, distribution.StandardDeviation, 2E-9); + Assert.AreEqual(2, distribution.Skewness, 2E-8); + Assert.AreEqual(9, distribution.Kurtosis, 2E-7); + distribution.MinimumOfRandomVariables = false; + Assert.AreEqual(1.5, distribution.Mean, 2E-9); + Assert.AreEqual(Math.Sqrt(1.25), distribution.StandardDeviation, 2E-9); + } + + /// Positive conditional quantiles remain accurate for remote tails and tiny physical scales. + [TestMethod] + public void HurdleQuantilesAndCentralMomentsAreScaleStable() + { + var half = new Mixture(new[] { 1d }, new UnivariateDistributionBase[] { new Normal() }) { IsZeroInflated = true, ZeroWeight = .2 }; + double mean = .8 * Math.Sqrt(2 / Math.PI); + double variance = .8 - mean * mean; + double central3 = 1.6 * Math.Sqrt(2 / Math.PI) - 3 * mean * .8 + 2 * mean * mean * mean; + double central4 = 2.4 - 4 * mean * 1.6 * Math.Sqrt(2 / Math.PI) + 6 * mean * mean * .8 - 3 * Math.Pow(mean, 4); + Assert.AreEqual(mean, half.Mean, 1E-9); + Assert.AreEqual(Math.Sqrt(variance), half.StandardDeviation, 1E-9); + Assert.AreEqual(central3 / Math.Pow(variance, 1.5), half.Skewness, 2E-8); + Assert.AreEqual(central4 / (variance * variance), half.Kurtosis, 2E-8); + var remote = new Mixture(new[] { 1d }, new UnivariateDistributionBase[] { new Normal(-40, 1) }) { IsZeroInflated = true }; + double q = remote.InverseCDF(.5); + Assert.AreEqual(Math.Log(.5), remote.LogCCDF(q), 2E-6); + // GNO has no legacy Normal.Sigma minimum-scale clamp. + var tiny = new Mixture(new[] { 1d }, new UnivariateDistributionBase[] { new GeneralizedNormal(0, 1E-100, 0) }) { IsZeroInflated = true }; + Assert.AreEqual(.6744897501960817, tiny.InverseCDF(.5) / 1E-100, 2E-6); + } + + /// Ordinary mixtures solve quantiles in scaled coordinates rather than treating nearby physical endpoints as equal. + [TestMethod] + public void MixtureQuantileRespectsTinyScaleAndNonparametricChildren() + { + var tiny = new Mixture(new[] { .5, .5 }, new UnivariateDistributionBase[] + { new GeneralizedNormal(0, 1E-100, 0), new GeneralizedNormal(1E-100, 1E-100, 0) }); + Assert.AreEqual(.5, tiny.InverseCDF(.5) / 1E-100, 2E-6); + var empirical = new EmpiricalDistribution(new[] { 1d, 2d, 3d }, new[] { .1, .5, .9 }); + var single = new Mixture(new[] { 1d }, new UnivariateDistributionBase[] { empirical }); + Assert.AreEqual(empirical.InverseCDF(.5), single.InverseCDF(.5)); + } + + /// The generic fixed-bin moment approximation must not subtract two large raw moments. + [TestMethod] + public void NumericalBinMomentsPreserveTranslation() + { + var unit = new Normal(0, 1).CentralMoments(300); + var translated = new Normal(1E10, 1).CentralMoments(300); + Assert.AreEqual(unit[1], translated[1], 3E-6); + Assert.AreEqual(unit[2], translated[2], 3E-6); + Assert.AreEqual(unit[3], translated[3], 3E-5); + } + + /// Independent minimum quantiles use the same dimensionless root accuracy at tiny scales. + [TestMethod] + public void CompetingQuantilePreservesPhysicalScale() + { + var distribution = new CompetingRisks(new UnivariateDistributionBase[] + { new GeneralizedNormal(0, 1E-100, 0), new GeneralizedNormal(1E-100, 1E-100, 0) }); + Assert.AreEqual(-.1725495296281549, distribution.InverseCDF(.5) / 1E-100, 2E-6); + } + + /// Identical component laws retain their relative weights even at enormous negative log densities. + [TestMethod] + public void LogResponsibilityNormalizationPreservesWeights() + { + var responsibilities = new double[3]; + double logRow = MixtureLogWeights.Normalize(new[] { -5E199, -5E199, double.PositiveInfinity }, new[] { .4, .6, 0d }, responsibilities); + Assert.AreEqual(-5E199, logRow); + Assert.AreEqual(.4, responsibilities[0], 2E-15); + Assert.AreEqual(.6, responsibilities[1], 2E-15); + Assert.AreEqual(0, responsibilities[2]); + } + } +} diff --git a/Test_Numerics/Distributions/Univariate/Test_DistributionLikelihoodRegression.cs b/Test_Numerics/Distributions/Univariate/Test_DistributionLikelihoodRegression.cs new file mode 100644 index 00000000..b70773a5 --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Test_DistributionLikelihoodRegression.cs @@ -0,0 +1,49 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Distributions; + +namespace Distributions.Univariate +{ + /// Regression contracts for shared censored log likelihoods. + [TestClass] + public class Test_DistributionLikelihoodRegression + { + /// R 4.4.3 pnorm(log.p=TRUE) oracle for a representable upper-tail interval. + [TestMethod] + public void NormalTailIntervalRemainsFinite() + { + var distribution = new Normal(0, 1); + Assert.AreEqual(-43.628216632280818, distribution.LogLikelihood_Intervals(9, 10), 2E-13); + Assert.AreEqual(-43.628216632280818, distribution.LogLikelihood_Intervals(-10, -9), 2E-13); + Assert.AreEqual(-46.970640393085588, distribution.LogLikelihood_Intervals(0, 1E-20), 2E-13); + } + + /// An empty censoring category contributes zero even at an impossible threshold. + [TestMethod] + public void ZeroCensoringCountsContributeZero() + { + var distribution = new Exponential(0, 1); + Assert.AreEqual(0, distribution.LogLikelihood_LeftCensored(-1, 0)); + Assert.AreEqual(0, distribution.LogLikelihood_RightCensored(double.PositiveInfinity, 0)); + } + + /// Intervals are empty when their limits coincide, including infinities. + [TestMethod] + public void EqualIntervalBoundsHaveZeroProbability() + { + var distribution = new Normal(0, 1); + Assert.AreEqual(double.NegativeInfinity, distribution.LogLikelihood_Intervals(0, 0)); + Assert.AreEqual(double.NegativeInfinity, distribution.LogLikelihood_Intervals(double.PositiveInfinity, double.PositiveInfinity)); + } + + /// Invalid censoring counts and reversed intervals are rejected explicitly. + [TestMethod] + public void InvalidCensoringInputsAreRejected() + { + var distribution = new Normal(0, 1); + Assert.ThrowsExactly(() => distribution.LogLikelihood_LeftCensored(0, -1)); + Assert.ThrowsExactly(() => distribution.LogLikelihood_RightCensored(0, -1)); + Assert.ThrowsExactly(() => distribution.LogLikelihood_Intervals(1, 0)); + } + } +} diff --git a/Test_Numerics/Distributions/Univariate/Test_DistributionNumerics.cs b/Test_Numerics/Distributions/Univariate/Test_DistributionNumerics.cs new file mode 100644 index 00000000..656b466d --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Test_DistributionNumerics.cs @@ -0,0 +1,118 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Distributions; + +namespace Distributions.Univariate +{ + /// Independent numerical contracts for shared distribution primitives. + [TestClass] + public class Test_DistributionNumerics + { + /// Subnormal derivative scaling must precede the final exponential rounding. + [TestMethod] + public void GammaSubnormalDerivativeAndTinyShapeTailsRetainScale() + { + Assert.AreEqual(-5.597735947761608E-21, DistributionNumerics.GammaLogCDF(1E-20, .5), 1E-34); + Assert.AreEqual(-5.597735947761608E-101, DistributionNumerics.GammaLogCDF(1E-100, .5), 1E-113); + Assert.AreEqual(4.090863547565521E-321, DistributionNumerics.GammaQuantileShapeDerivative(.9, double.Epsilon), 2 * double.Epsilon); + } + + /// R qgamma Richardson derivatives cross-checked independently with pgamma at fixed x. + [TestMethod] + public void GammaShapeDerivativesMatchIndependentQuantileOracles() + { + int count = 0; + foreach (var row in DistributionOracle.Read("extreme-positive.csv")) + { + if (row["family"] != "Gamma" || row["quantity"] != "GradientShape" || row["status"] != "finite") continue; + double a = DistributionOracle.Number(row["shape"]), p = DistributionOracle.Number(row["p"]); + double q = DistributionNumerics.GammaInverseCDF(a, p); + double actual = DistributionNumerics.GammaQuantileShapeDerivative(a, q); + double expected = DistributionOracle.Number(row["value"]); + Assert.AreEqual(expected, actual, Math.Abs(expected) * 5E-8, $"shape={a},p={p}"); + count++; + } + Assert.AreEqual(49, count); + } + + /// R 4.4.3 pgamma(log.p=TRUE), including its direct complemented tail. + [TestMethod] + public void GammaLogTailsMatchROracles() + { + double[,] cases = { + { .001, 1E-10, -.022449457331756989, -3.8076925670559727 }, + { .001, 1, -.0002196324750319099, -8.4236647908310722 }, + { .001, 1000, 0, -1013.8090239153952 }, + { .5, 1E-10, -11.392143227368317, -1.1283855333035131E-5 }, + { .5, 1, -.17114331524104021, -1.8496055099332522 }, + { .5, 1000, 0, -1004.0267419589519 }, + { 1, 1E-10, -23.025850929990458, -1E-10 }, + { 1, 1, -.45867514538708193, -1 }, + { 100, 80, -4.0681907911970132, -.017256351106458189 }, + { 100, 100, -.66689715058528931, -.72010489302547409 }, + { 100, 120, -.028259299048920376, -3.5804290809275314 }, + { 10000, 9800, -3.8073232363866576, -.022457843956297584 }, + { 10000, 10000, -.69049109440197243, -.69581034030382005 }, + { 10000, 10200, -.023562756305381644, -3.7598461809322816 }, + { 1E8, 99980000, -3.7834216952685238, -.023007384281740352 }, + { 1E8, 1E8, -.69312058476158833, -.69317377706565741 }, + { 1E8, 100020000, -.023018433854231277, -3.7829470521492174 } + }; + for (int i = 0; i < cases.GetLength(0); i++) + { + Assert.AreEqual(cases[i, 2], DistributionNumerics.GammaLogCDF(cases[i, 0], cases[i, 1]), 3E-12, $"lower case {i}"); + Assert.AreEqual(cases[i, 3], DistributionNumerics.GammaLogSurvival(cases[i, 0], cases[i, 1]), 3E-12, $"upper case {i}"); + } + } + + /// Independent closed forms and R references, preserving tiny input probabilities. + [TestMethod] + public void GammaInverseUsesBothTailsDirectly() + { + Assert.AreEqual(46.051701859880914, DistributionNumerics.GammaInverseCDF(1, 1E-20, true), 1E-13); + Assert.AreEqual(1E-20, DistributionNumerics.GammaInverseCDF(1, 1E-20), 1E-35); + double q = DistributionNumerics.GammaInverseCDF(.001, .5); + Assert.AreEqual(5.244206408274966E-302, q, 2E-313); + foreach (double a in new[] { .1, .5, 2, 100, 10000, 1E8 }) + foreach (double p in new[] { 1E-10, .1, .5 }) + foreach (bool upper in new[] { false, true }) + { + double value = DistributionNumerics.GammaInverseCDF(a, p, upper); + double actual = upper ? DistributionNumerics.GammaLogSurvival(a, value) : DistributionNumerics.GammaLogCDF(a, value); + Assert.AreEqual(Math.Log(p), actual, a >= 1E8 ? 2E-11 : 2E-12, $"a={a},p={p},upper={upper}"); + } + } + + /// R pnorm log-tail references and exact exponential divided-difference limits. + [TestMethod] + public void LogProbabilityPrimitivesPreserveLimits() + { + Assert.AreEqual(-804.6084420137538, DistributionNumerics.NormalLogCDF(-40), 2E-13); + Assert.AreEqual(-43.62814911333212, DistributionNumerics.NormalLogSurvival(9), 2E-13); + Assert.AreEqual(double.NegativeInfinity, DistributionNumerics.Log1mExp(0)); + Assert.AreEqual(0, DistributionNumerics.Log1mExp(double.NegativeInfinity)); + Assert.AreEqual(double.NegativeInfinity, DistributionNumerics.LogDifference(-1, -1)); + Assert.AreEqual(1, DistributionNumerics.Exprel(0)); + Assert.AreEqual(.5, DistributionNumerics.ExprelDerivative(0)); + Assert.AreEqual(2, DistributionNumerics.Standardize(1E308, -1E308, 1E308)); + } + + /// Scaled pivoting preserves sign, exact duplicate-row singularity, and finite logs. + [TestMethod] + public void LogDeterminantAvoidsProductOverflowAndArtificialPivots() + { + double log = DistributionNumerics.LogAbsDeterminant(new double[,] { { 1E200, 1E200 }, { 1E200, -1E200 } }, out int sign); + Assert.AreEqual(400 * Math.Log(10) + Math.Log(2), log, 2E-13); + Assert.AreEqual(-1, sign); + Assert.AreEqual(double.NegativeInfinity, DistributionNumerics.LogAbsDeterminant(new double[,] { { 1, 2 }, { 1, 2 } }, out sign)); + Assert.AreEqual(0, sign); + Assert.AreEqual(0, DistributionNumerics.LogAbsDeterminant(new double[,] { { 1E308, 1E-308 }, { 1E308, 2E-308 } }, out sign), 1E-12); + Assert.AreEqual(1, sign); + Assert.AreEqual(double.NegativeInfinity, DistributionNumerics.LogAbsDeterminant(new double[,] { { 1, 2, 3 }, { 4, 5, 6 }, { 5, 7, 9 } }, out sign)); + Assert.AreEqual(0, sign); + Assert.AreEqual(double.NegativeInfinity, new KappaFour().LogAbsQuantileJacobian(new[] { .5, .5, .5, .5 })); + Assert.AreEqual(Math.Log(2 * Normal.StandardZ(.9)), new Normal().LogAbsQuantileJacobian(new[] { .1, .9 }), 1E-14); + Assert.ThrowsExactly(() => new Normal().LogAbsQuantileJacobian(new[] { 0d, .9 })); + } + } +} diff --git a/Test_Numerics/Distributions/Univariate/Test_ExtremePositiveRobustness.cs b/Test_Numerics/Distributions/Univariate/Test_ExtremePositiveRobustness.cs new file mode 100644 index 00000000..ddc9c1b0 --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Test_ExtremePositiveRobustness.cs @@ -0,0 +1,461 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Distributions; + +namespace Distributions.Univariate +{ + /// Independent probability, moment, and uncertainty regressions for six extreme and positive families. + [TestClass] + public class Test_ExtremePositiveRobustness + { + /// Frozen R and defining-formula oracles catch cancelled tails, artificial shape plateaus and endpoint errors. + [TestMethod] + [DataRow("Exponential")] + [DataRow("Gamma")] + [DataRow("GEV")] + [DataRow("GPA")] + [DataRow("Gumbel")] + [DataRow("Weibull")] + public void ProbabilityFunctionsMatchIndependentOracle(string family) + { + foreach (var row in Rows(family).Where(r => r["quantity"].StartsWith("Log", StringComparison.Ordinal) || r["quantity"] == "InverseCDF")) + { + var distribution = Create(row); + double x = Number(row, "x"), p = Number(row, "p"); + string quantity = row["quantity"]; + double actual = quantity == "LogPDF" ? distribution.LogPDF(x) + : quantity == "LogCDF" ? distribution.LogCDF(x) + : quantity == "LogCCDF" ? distribution.LogCCDF(x) : distribution.InverseCDF(p); + OracleAssert(row, actual); + if (quantity == "LogPDF") ScalarAssert(Math.Exp(Number(row, "value")), distribution.PDF(x), row, 2E-10); + if (quantity == "LogCDF") ScalarAssert(Math.Exp(Number(row, "value")), distribution.CDF(x), row, 2E-10); + if (quantity == "LogCCDF") ScalarAssert(Math.Exp(Number(row, "value")), distribution.CCDF(x), row, 2E-10); + } + } + + /// Independent complete-tail moments distinguish bounded positive shapes from nonexistent heavy-tail moments. + [TestMethod] + [DataRow("Exponential")] + [DataRow("Gamma")] + [DataRow("GEV")] + [DataRow("GPA")] + [DataRow("Gumbel")] + [DataRow("Weibull")] + public void MomentsAndSupportMatchIndependentOracle(string family) + { + foreach (var row in Rows(family).Where(r => new[] { "Mean", "StandardDeviation", "Skewness", "Kurtosis", "Minimum", "Maximum" }.Contains(r["quantity"]))) + { + var distribution = Create(row); + double actual = row["quantity"] == "Mean" ? distribution.Mean + : row["quantity"] == "StandardDeviation" ? distribution.StandardDeviation + : row["quantity"] == "Skewness" ? distribution.Skewness + : row["quantity"] == "Kurtosis" ? distribution.Kurtosis + : row["quantity"] == "Minimum" ? distribution.Minimum : distribution.Maximum; + // Gumbel's existing rounded published skewness remains its documented precision. + OracleAssert(row, actual, family == "Gumbel" && row["quantity"] == "Skewness" ? 5E-5 : 0); + } + } + + /// Actual-quantile derivatives use independent R qgamma derivatives and analytic other-family gradients. + [TestMethod] + [DataRow("Exponential")] + [DataRow("Gamma")] + [DataRow("GEV")] + [DataRow("GPA")] + [DataRow("Gumbel")] + [DataRow("Weibull")] + public void QuantileGradientsMatchIndependentOracle(string family) + { + foreach (var row in Rows(family).Where(r => r["quantity"].StartsWith("Gradient", StringComparison.Ordinal))) + { + if (row["status"] == "unresolved-quantile-underflow") continue; + int index = row["quantity"] == "GradientScale" ? 0 + : row["quantity"] == "GradientShape" ? 1 : int.Parse(row["quantity"].Substring(8)) - 1; + OracleAssert(row, ((IStandardError)Create(row)).QuantileGradient(Number(row, "p"))[index]); + } + } + + /// Covariance matches independent order-statistic, Fisher and moment-Jacobian references in public coordinates. + [TestMethod] + [DataRow("Exponential")] + [DataRow("Gamma")] + [DataRow("GEV")] + [DataRow("GPA")] + [DataRow("Gumbel")] + [DataRow("Weibull")] + public void CovarianceMatchesIndependentOracle(string family) + { + var matrices = new Dictionary(); + foreach (var row in Rows(family).Where(r => r["quantity"].StartsWith("Covariance", StringComparison.Ordinal))) + { + var method = row["case"].StartsWith("MoM", StringComparison.Ordinal) + ? ParameterEstimationMethod.MethodOfMoments : ParameterEstimationMethod.MaximumLikelihood; + var distribution = (IStandardError)Create(row); + int n = (int)Number(row, "n"); + if (row["quantity"] == "CovarianceDefined") + { + Assert.Throws(() => distribution.ParameterCovariance(n, method), Description(row)); + continue; + } + string key = row["case"] + "/" + row["scale"] + "/" + row["shape"] + "/" + row["n"]; + if (!matrices.TryGetValue(key, out var covariance)) + matrices.Add(key, covariance = distribution.ParameterCovariance(n, method)); + int i = row["quantity"][10] - '1', j = row["quantity"][11] - '1'; + // Preserve the existing published rounded Gumbel and Weibull Fisher constants. + double publishedPrecision = family == "Gumbel" ? 9E-5 : family == "Weibull" ? 2E-6 : 0; + OracleAssert(row, covariance[i, j], publishedPrecision); + Assert.AreEqual(covariance[i, j], covariance[j, i], Description(row)); + } + } + + /// Quantile uncertainty rejects endpoints, nonfinite probabilities and invalid sample sizes for every family. + [TestMethod] + [DataRow("Exponential")] + [DataRow("Gamma")] + [DataRow("GEV")] + [DataRow("GPA")] + [DataRow("Gumbel")] + [DataRow("Weibull")] + public void UncertaintyRejectsInvalidInputs(string family) + { + var distribution = (IStandardError)Create(family, 0, 2, family == "Gamma" || family == "Weibull" ? 2 : 0); + foreach (double p in new[] { -1d, 0d, 1d, 2d, double.NaN, double.PositiveInfinity, double.NegativeInfinity }) + { + Assert.Throws(() => distribution.QuantileGradient(p), family + "/gradient"); + Assert.Throws(() => distribution.QuantileVariance(p, 100, ParameterEstimationMethod.MaximumLikelihood), family + "/variance"); + } + foreach (int n in new[] { 0, -1 }) + Assert.Throws(() => distribution.ParameterCovariance(n, ParameterEstimationMethod.MaximumLikelihood), family); + Assert.Throws(() => distribution.ParameterCovariance(100, ParameterEstimationMethod.MethodOfLinearMoments), family); + var probabilities = Enumerable.Repeat(.5, ((UnivariateDistributionBase)distribution).NumberOfParameters).ToArray(); + distribution.QuantileJacobian(probabilities, out double determinant); + Assert.AreEqual(0d, determinant, family + "/duplicate quantile determinant"); + } + + /// Validation inspects the candidate vector, including wrong lengths and invalid shape entries. + [TestMethod] + [DataRow("Exponential")] + [DataRow("Gamma")] + [DataRow("GEV")] + [DataRow("GPA")] + [DataRow("Gumbel")] + [DataRow("Weibull")] + public void CandidateValidationRejectsMalformedVectors(string family) + { + var distribution = Create(family, 0, 2, family == "Gamma" || family == "Weibull" ? 2 : 0); + foreach (var parameters in new[] { Array.Empty(), new[] { 1d }, new[] { 1d, 2d, 3d, 4d } }) + { + Assert.IsNotNull(distribution.ValidateParameters(parameters, false), family); + Assert.Throws(() => distribution.ValidateParameters(parameters, true), family); + } + var candidate = distribution.GetParameters; + candidate[candidate.Length - 1] = double.NaN; + Assert.IsNotNull(distribution.ValidateParameters(candidate, false), family); + Assert.Throws(() => distribution.InverseCDF(double.NaN), family); + } + + /// Initialization keeps tiny scales feasible, handles signed centers, and rejects unusable samples. + [TestMethod] + [DataRow("Exponential")] + [DataRow("Gamma")] + [DataRow("GEV")] + [DataRow("GPA")] + [DataRow("Gumbel")] + [DataRow("Weibull")] + public void InitializationHasFiniteOrderedFeasibleBounds(string family) + { + var distribution = (IMaximumLikelihoodEstimation)Create(family, 0, 2, family == "Gamma" || family == "Weibull" ? 2 : 0); + foreach (var sample in new[] { new[] { 1d, 1d, 1d, 1d }, new[] { 1d, 2d, 3d, double.NaN }, new[] { 1d, 2d, 3d, double.PositiveInfinity }, new[] { 1d } }) + Assert.Throws(() => distribution.GetParameterConstraints(sample), family); + if (family == "Gamma" || family == "Weibull") + Assert.Throws(() => distribution.GetParameterConstraints(new[] { 0d, 1d, 2d, 3d }), family); + foreach (double scale in new[] { 1E-200, 1d, 1E200 }) + { + var sample = new[] { 1d, 2d, 4d, 7d, 8d, 10d }.Select(x => x * scale).ToArray(); + CheckConstraints(distribution.GetParameterConstraints(sample), family); + if (family != "Gamma" && family != "Weibull") + CheckConstraints(distribution.GetParameterConstraints(sample.Select(x => x - 5 * scale).ToArray()), family); + } + } + + /// The Weibull median gradient and its variance correspond to the actual inverse CDF. + [TestMethod] + public void WeibullMedianGradientAndVarianceAreCorrect() + { + var distribution = new Weibull(2, 2); + var gradient = distribution.QuantileGradient(.5); + Assert.AreEqual(.832554611157698, gradient[0], 1E-14); + Assert.AreEqual(.152571011039570, gradient[1], 1E-14); + Assert.AreEqual(.00955664647618049, distribution.QuantileVariance(.5, 100, ParameterEstimationMethod.MaximumLikelihood), 1E-15); + } + + /// Shape one is a reverse exponential for GEV and a uniform distribution for GPA. + [TestMethod] + public void ShapeOneHasCorrectMedianModeAndEndpointDensity() + { + var gev = new GeneralizedExtremeValue(0, 1, 1); + Assert.AreEqual(.3068528194400547, gev.Median, 1E-15); + Assert.AreEqual(1d, gev.Mode); + Assert.AreEqual(1d, gev.PDF(1)); + var gpa = new GeneralizedPareto(0, 1, 1); + Assert.AreEqual(.5, gpa.Median); + Assert.IsTrue(double.IsNaN(gpa.Mode), "The uniform mode is nonunique."); + Assert.AreEqual(1d, gpa.PDF(1)); + Assert.AreEqual(1d, new GeneralizedPareto(0, 2, 2).Mode); + } + + /// Finite affine standardized coordinates remain usable when the unscaled subtraction overflows. + [TestMethod] + public void AffineStandardizationAvoidsSpuriousOverflow() + { + foreach (string family in new[] { "Exponential", "Gumbel", "GEV", "GPA" }) + { + var unit = Create(family, 0, 1, 0); + var shifted = Create(family, -1E308, 1E308, 0); + Assert.AreEqual(unit.LogCDF(2), shifted.LogCDF(1E308), 1E-13, family); + Assert.AreEqual(unit.LogCCDF(2), shifted.LogCCDF(1E308), 1E-13, family); + Assert.AreEqual(unit.LogPDF(2) - Math.Log(1E308), shifted.LogPDF(1E308), 1E-12, family); + } + } + + /// A capped named Gamma approximation is constant beyond the cap and reflects negative skew consistently. + [TestMethod] + public void GammaNamedApproximationClippingAndReflectionAreConsistent() + { + foreach (double p in new[] { .01, .25, .5, .75, .99 }) + { + Assert.AreEqual(GammaDistribution.FrequencyFactorKp(9.75, p), GammaDistribution.FrequencyFactorKp(100, p), 1E-14); + Assert.AreEqual(-GammaDistribution.FrequencyFactorKp(9.75, 1 - p), GammaDistribution.FrequencyFactorKp(-100, p), 1E-13); + Assert.AreEqual(-GammaDistribution.FrequencyFactorKp(3, 1 - p), GammaDistribution.FrequencyFactorKp(-3, p), 1E-13); + } + } + + /// Independent unit-scale quadratics retain finite scaling and the correct overflow classification. + [TestMethod] + [DataRow("Exponential", .0048564848377901943)] + [DataRow("Gamma", .015723154225565583)] + [DataRow("GEV", .015433980376270423)] + [DataRow("GPA", .0068559015049324415)] + [DataRow("Gumbel", .013787478943464875)] + [DataRow("Weibull", .0023891616190451241)] + public void QuantileVariancePreservesScaleAndOverflowClassification(string family, double unitVariance) + { + double shape = family == "Gamma" || family == "Weibull" ? 2 : 0; + foreach (double scale in new[] { 1E-150, 1d, 1E150 }) + { + var distribution = (IStandardError)Create(family, 0, scale, shape); + double expected = unitVariance * scale * scale; + Assert.AreEqual(expected, distribution.QuantileVariance(.5, 100, ParameterEstimationMethod.MaximumLikelihood), expected * 5E-8, family); + } + var large = (IStandardError)Create(family, 0, 1E200, shape); + foreach (double probability in new[] { .1, .5, .9 }) + Assert.IsTrue(double.IsPositiveInfinity(large.QuantileVariance(probability, 100, ParameterEstimationMethod.MaximumLikelihood)), family + "/overflowed positive variance"); + } + + /// A finite affine inverse survives an overflowing displacement followed by an opposite location. + [TestMethod] + [DataRow("Exponential", .8646647167633873)] + [DataRow("Gumbel", .8734230184931167)] + [DataRow("GEV", .8734230184931167)] + [DataRow("GPA", .8646647167633873)] + public void AffineInverseAvoidsSpuriousOverflow(string family, double probability) + { + var distribution = Create(family, -1E308, 1E308, 0); + Assert.AreEqual(1E308, distribution.InverseCDF(probability), 2E293, family); + } + + /// Tiny representable probabilities and adjacent support doubles do not acquire artificial zeros. + [TestMethod] + public void TinyProbabilitiesAndAdjacentSupportPointsRemainDistinct() + { + foreach (var distribution in new UnivariateDistributionBase[] { new Exponential(0, 1), new GammaDistribution(1, 1), new Weibull(1, 1), new GeneralizedPareto(0, 1, 0) }) + { + Assert.AreEqual(double.Epsilon, distribution.InverseCDF(double.Epsilon), distribution.DisplayName); + Assert.AreEqual(Math.Log(double.Epsilon), distribution.LogCDF(double.Epsilon), 1E-12, distribution.DisplayName); + } + double below = BitConverter.Int64BitsToDouble(BitConverter.DoubleToInt64Bits(2d) - 1); + double above = BitConverter.Int64BitsToDouble(BitConverter.DoubleToInt64Bits(2d) + 1); + foreach (var distribution in new UnivariateDistributionBase[] { new GeneralizedExtremeValue(0, 1, .5), new GeneralizedPareto(0, 1, .5) }) + { + Assert.IsGreaterThan(0d, distribution.CCDF(below), distribution.DisplayName); + Assert.IsFalse(double.IsInfinity(distribution.LogCCDF(below)), distribution.DisplayName); + Assert.AreEqual(double.NegativeInfinity, distribution.LogCCDF(2)); + Assert.AreEqual(0d, distribution.PDF(above)); + } + Assert.AreEqual(-1000d, new Gumbel(0, 1).LogCCDF(1000)); + Assert.AreEqual(-1000d, new GeneralizedExtremeValue(0, 1, 0).LogCCDF(1000)); + } + + /// The zero-shape derivative combines its dimensionless factor before the large scale. + [TestMethod] + public void RepresentableExtremeShapeGradientAvoidsIntermediateOverflow() + { + double gevProbability = Math.Exp(-Math.Exp(-1.4)); + double gpaProbability = 1 - Math.Exp(-1.4); + Assert.AreEqual(-9.8E307, new GeneralizedExtremeValue(0, 1E308, 0).QuantileGradient(gevProbability)[2], 1E294); + Assert.AreEqual(-9.8E307, new GeneralizedPareto(0, 1E308, 0).QuantileGradient(gpaProbability)[2], 1E294); + } + + /// Physical GPA tail values remain finite when their unit-scale counterparts exceed the floating-point range. + [TestMethod] + public void GpaScaleRescuesOverflowedUnitQuantileAndVariance() + { + var distribution = new GeneralizedPareto(0, 1E-200, -20); + double probability = 1 - 1E-16; + Assert.AreEqual(6.1768265779813681E117, distribution.InverseCDF(probability), 2E105); + Assert.AreEqual(-2.2660800481989075E119, distribution.QuantileGradient(probability)[2], 1E107); + Assert.AreEqual(2.2588688104431192E239, + distribution.QuantileVariance(probability, 100, ParameterEstimationMethod.MaximumLikelihood), 2E227); + } + + /// Gradient normalization retains a Gamma variance rescued from a tiny unit quantile by its physical scale. + [TestMethod] + public void GammaScaleRescuesUnderflowedUnitQuantileVariance() + { + var distribution = new GammaDistribution(1E200, .001); + // R contraction of the frozen actual-qgamma gradient and trigamma covariance, after physical scaling. + Assert.AreEqual(1.3215876806248227E-199, + distribution.QuantileVariance(.5, 100, ParameterEstimationMethod.MaximumLikelihood), 2E-207); + } + + /// Negative-skew approximate frequency factors preserve a tiny lower-tail probability during reflection. + [TestMethod] + public void GammaNamedApproximationReflectsTinyProbabilityWithoutRoundingItAway() + { + // Standalone R qnorm and the published magnitude-three Kirby polynomial. + Assert.AreEqual(-95.085021656541102, GammaDistribution.FrequencyFactorKp(-3, 1E-20), 2E-10); + } + + /// Physical tail logarithms remain finite when subtraction, standardization, or the shape product overflows. + [TestMethod] + [DataRow("GEV")] + [DataRow("GPA")] + public void TailTransformPreservesLogarithmsBeyondStandardizedRange(string family) + { + var rows = Rows(family).Where(row => row["case"] == "overflowing-tail-transform").ToList(); + Assert.IsNotEmpty(rows); + foreach (var row in rows) + { + var distribution = Create(row); + double x = Number(row, "x"); + double actual = row["quantity"] == "LogPDF" ? distribution.LogPDF(x) + : row["quantity"] == "LogCDF" ? distribution.LogCDF(x) : distribution.LogCCDF(x); + OracleAssert(row, actual); + } + } + + /// Finite covariance-gradient contributions survive mismatched coordinate magnitudes. + [TestMethod] + public void WeibullVariancePreservesMismatchedCoordinateRanges() + { + var row = Rows("Weibull").Single(item => item["case"] == "MLE-mismatched-range-variance"); + var distribution = (Weibull)Create(row); + OracleAssert(row, distribution.QuantileVariance(Number(row, "p"), (int)Number(row, "n"), ParameterEstimationMethod.MaximumLikelihood)); + } + + /// The Fisher residual stays positive when its leading trigamma term rounds to the reciprocal shape. + [TestMethod] + public void GammaFisherCovarianceRetainsLargeShapeResidual() + { + var rows = Rows("Gamma").Where(row => row["case"] == "MLE-large-shape-Fisher-residual").ToList(); + Assert.IsNotEmpty(rows); + foreach (var row in rows) + { + var covariance = ((GammaDistribution)Create(row)).ParameterCovariance((int)Number(row, "n"), ParameterEstimationMethod.MaximumLikelihood); + OracleAssert(row, covariance[row["quantity"][10] - '1', row["quantity"][11] - '1']); + } + } + + /// The positive mean-direction variance survives covariance cancellation at concentrated Gamma shapes. + [TestMethod] + [DataRow("MLE")] + [DataRow("MoM")] + public void GammaMedianVarianceRetainsLargeShapeMeanDirection(string method) + { + var row = Rows("Gamma").Single(item => item["case"] == method + "-large-shape-median-variance"); + var distribution = (GammaDistribution)Create(row); + var estimationMethod = method == "MLE" ? ParameterEstimationMethod.MaximumLikelihood : ParameterEstimationMethod.MethodOfMoments; + OracleAssert(row, distribution.QuantileVariance(Number(row, "p"), (int)Number(row, "n"), estimationMethod)); + } + + /// The median property restores physical scale before an unrepresentable unit quantile is rounded to zero. + [TestMethod] + public void WeibullMedianRetainsPhysicallyScaledTinyQuantile() + { + var row = Rows("Weibull").Single(item => item["case"] == "scale-rescued-median"); + var distribution = (Weibull)Create(row); + OracleAssert(row, distribution.Median); + OracleAssert(row, distribution.InverseCDF(.5)); + } + + /// GPA skewness and kurtosis remain finite when raw shape powers would overflow. + [TestMethod] + public void GpaHigherMomentsAvoidLargeShapePowerOverflow() + { + var rows = Rows("GPA").Where(row => row["case"] == "large-positive-shape-scaled-higher-moments").ToList(); + Assert.IsNotEmpty(rows); + foreach (var row in rows) + { + var distribution = (GeneralizedPareto)Create(row); + OracleAssert(row, row["quantity"] == "Skewness" ? distribution.Skewness : distribution.Kurtosis); + } + } + + /// Reads a family subset without a runtime R or network dependency. + private static IEnumerable> Rows(string family) => DistributionOracle.Read("extreme-positive.csv").Where(r => r["family"] == family); + + /// Creates the public-coordinate distribution used by an independent reference row. + private static UnivariateDistributionBase Create(Dictionary row) => Create(row["family"], Number(row, "xi"), Number(row, "scale"), Number(row, "shape")); + + /// Maps fixture families to their actual public constructors. + private static UnivariateDistributionBase Create(string family, double xi, double scale, double shape) => family switch + { + "Exponential" => new Exponential(xi, scale), "Gamma" => new GammaDistribution(scale, shape), + "GEV" => new GeneralizedExtremeValue(xi, scale, shape), "GPA" => new GeneralizedPareto(xi, scale, shape), + "Gumbel" => new Gumbel(xi, scale), "Weibull" => new Weibull(scale, shape), + _ => throw new ArgumentOutOfRangeException(nameof(family)) + }; + + /// Parses a scalar reference field using the shared invariant reader. + private static double Number(Dictionary row, string field) => DistributionOracle.Number(row[field]); + + /// Identifies the independent failing coordinate in a test result. + private static string Description(Dictionary row) => string.Join("/", new[] { row["family"], row["case"], row["quantity"], row["scale"], row["shape"], row["x"], row["p"], row["n"] }); + + /// Checks classification separately and uses the frozen absolute plus relative tolerance for finite values. + private static void OracleAssert(Dictionary row, double actual, double minimumRelativeTolerance = 0) + { + double expected = Number(row, "value"); + double tolerance = Number(row, "absolute_tolerance") + Math.Max(minimumRelativeTolerance, Number(row, "relative_tolerance")) * Math.Abs(expected); + if (double.IsNaN(expected)) Assert.IsTrue(double.IsNaN(actual), Description(row)); + else if (double.IsInfinity(expected)) Assert.AreEqual(expected, actual, Description(row)); + else + { + Assert.IsTrue(!double.IsNaN(actual) && !double.IsInfinity(actual), Description(row) + " must be finite."); + Assert.AreEqual(expected, actual, tolerance, Description(row)); + } + } + + /// Protects nonzero representable linear probabilities and density classifications. + private static void ScalarAssert(double expected, double actual, Dictionary row, double relativeTolerance) + { + if (double.IsNaN(expected)) Assert.IsTrue(double.IsNaN(actual), Description(row)); + else if (double.IsInfinity(expected) || expected == 0) Assert.AreEqual(expected, actual, Description(row)); + else Assert.AreEqual(expected, actual, Math.Max(double.Epsilon, relativeTolerance * Math.Abs(expected)), Description(row)); + } + + /// Checks bounds against their returned start without manufacturing expected numerical estimates. + private static void CheckConstraints(Tuple constraints, string family) + { + for (int i = 0; i < constraints.Item1.Length; i++) + { + double initial = constraints.Item1[i], lower = constraints.Item2[i], upper = constraints.Item3[i]; + Assert.IsTrue(!double.IsNaN(initial) && !double.IsInfinity(initial), family + "/finite start"); + Assert.IsTrue(!double.IsNaN(lower) && !double.IsInfinity(lower) && !double.IsNaN(upper) && !double.IsInfinity(upper), family + "/finite bounds"); + Assert.IsTrue(lower < upper && lower <= initial && initial <= upper, family + "/ordered feasible bounds"); + } + } + } +} diff --git a/Test_Numerics/Distributions/Univariate/Test_GeneralizedExtremeValue.cs b/Test_Numerics/Distributions/Univariate/Test_GeneralizedExtremeValue.cs index c2f161da..90559950 100644 --- a/Test_Numerics/Distributions/Univariate/Test_GeneralizedExtremeValue.cs +++ b/Test_Numerics/Distributions/Univariate/Test_GeneralizedExtremeValue.cs @@ -264,7 +264,7 @@ public void Test_Mean() Assert.AreEqual(100.42482, GEV2.Mean, 1e-04); var GEV3 = new GeneralizedExtremeValue(100, 10, 10); - Assert.AreEqual(double.NaN, GEV3.Mean); + Assert.AreEqual(-3628699d, GEV3.Mean, 1E-7); // 100 + (1 - Gamma(11)); this bounded shape has a mean. } /// @@ -277,7 +277,7 @@ public void Test_Median() Assert.AreEqual(103.66512, GEV.Median, 1e-04); var GEV2 = new GeneralizedExtremeValue(100, 10, 0.9); - Assert.AreEqual(104.3419519, GEV2.Median, 1e-04); + Assert.AreEqual(103.12196423496414, GEV2.Median, 1E-11); // R: 100 - 10*expm1(.9*log(log(2)))/.9. } /// @@ -290,7 +290,7 @@ public void Test_Mode() Assert.AreEqual(100, GEV.Mode); var GEV2 = new GeneralizedExtremeValue(100, 10, 1); - Assert.AreEqual(95, GEV2.Mode); + Assert.AreEqual(110, GEV2.Mode); // Reverse-exponential endpoint mode. } /// @@ -306,7 +306,7 @@ public void Test_StandardDeviation() Assert.AreEqual(9.280898, GEV2.StandardDeviation, 1e-04); var GEV3 = new GeneralizedExtremeValue(100, 10, 1); - Assert.AreEqual(double.NaN, GEV3.StandardDeviation); + Assert.AreEqual(10, GEV3.StandardDeviation); } /// @@ -316,13 +316,13 @@ public void Test_StandardDeviation() public void Test_Skewness() { var GEV = new GeneralizedExtremeValue(); - Assert.AreEqual(1.1396, GEV.Skewness); + Assert.AreEqual(1.1395470994046487, GEV.Skewness); var GEV2 = new GeneralizedExtremeValue(100, 10, 0.3); Assert.AreEqual(-0.0690175, GEV2.Skewness, 1e-03); var GEV3 = new GeneralizedExtremeValue(100, 10, 1); - Assert.AreEqual(double.NaN, GEV3.Skewness); + Assert.AreEqual(-2, GEV3.Skewness); } /// @@ -338,7 +338,7 @@ public void Test_Kurtosis() Assert.AreEqual(2.7659607, GEV2.Kurtosis, 1e-04); var GEV3 = new GeneralizedExtremeValue(100, 10, 1); - Assert.AreEqual(double.NaN, GEV3.Kurtosis); + Assert.AreEqual(9, GEV3.Kurtosis); } /// diff --git a/Test_Numerics/Distributions/Univariate/Test_GeneralizedLogistic.cs b/Test_Numerics/Distributions/Univariate/Test_GeneralizedLogistic.cs index ea7f213d..37a5d905 100644 --- a/Test_Numerics/Distributions/Univariate/Test_GeneralizedLogistic.cs +++ b/Test_Numerics/Distributions/Univariate/Test_GeneralizedLogistic.cs @@ -261,7 +261,8 @@ public void Test_Mode() Assert.AreEqual(100, l.Mode); var l2 = new GeneralizedLogistic(100, 10, 1); - Assert.AreEqual(95, l2.Mode); + // At kappa=1 the density increases to its one-sided maximum at xi+alpha. + Assert.AreEqual(110, l2.Mode); } /// @@ -345,11 +346,11 @@ public void Test_Maximum() public void Test_PDF() { var l = new GeneralizedLogistic(); - Assert.AreEqual(0.025,l.PDF(100)); + Assert.AreEqual(0.025,l.PDF(100), 1E-16); Assert.AreEqual(4.5395e-06, l.PDF(0), 1e-10); var l2 = new GeneralizedLogistic(100, 10, 1); - Assert.AreEqual(0.025, l2.PDF(100)); + Assert.AreEqual(0.025, l2.PDF(100), 1E-16); Assert.AreEqual(6.9444e-04, l2.PDF(0),1e-08); } diff --git a/Test_Numerics/Distributions/Univariate/Test_GeneralizedPareto.cs b/Test_Numerics/Distributions/Univariate/Test_GeneralizedPareto.cs index c1621096..5b8bb62b 100644 --- a/Test_Numerics/Distributions/Univariate/Test_GeneralizedPareto.cs +++ b/Test_Numerics/Distributions/Univariate/Test_GeneralizedPareto.cs @@ -282,7 +282,7 @@ public void Test_Mean() Assert.AreEqual(105.26315, GPA2.Mean, 1e-04); var GPA3 = new GeneralizedPareto(100, 10, 1); - Assert.AreEqual(double.NaN,GPA3.Mean); + Assert.AreEqual(105, GPA3.Mean); // Uniform(100,110). } /// @@ -295,7 +295,7 @@ public void Test_Median() Assert.AreEqual(106.93147, GPA.Median, 1e-04); var GPA2 = new GeneralizedPareto(100, 10, 1); - Assert.AreEqual(95, GPA2.Median); + Assert.AreEqual(105, GPA2.Median); } /// @@ -308,7 +308,7 @@ public void Test_Mode() Assert.AreEqual(100, GPA.Mode); var GPA2 = new GeneralizedPareto(100, 10, 1); - Assert.AreEqual(95, GPA2.Mode); + Assert.IsTrue(double.IsNaN(GPA2.Mode)); // A uniform density has no unique mode. } /// @@ -324,7 +324,7 @@ public void Test_StandardDeviation() Assert.AreEqual(6.531972, GPA2.StandardDeviation, 1e-04); var GPA3 = new GeneralizedPareto(100, 10, 1); - Assert.AreEqual(double.NaN, GPA3.StandardDeviation); + Assert.AreEqual(2.8867513459481291, GPA3.StandardDeviation, 1E-14); } /// @@ -340,7 +340,7 @@ public void Test_Skewness() Assert.AreEqual(0.932039, GPA2.Skewness, 1e-04); var GPA3 = new GeneralizedPareto(100, 10, 1); - Assert.AreEqual(double.NaN, GPA3.Skewness); + Assert.AreEqual(0, GPA3.Skewness); } /// @@ -356,7 +356,7 @@ public void Test_Kurtosis() Assert.AreEqual(3.786748, GPA2.Kurtosis, 1e-04); var GPA3 = new GeneralizedPareto(100, 10, 1); - Assert.AreEqual(double.NaN, GPA3.Kurtosis); + Assert.AreEqual(1.8, GPA3.Kurtosis); } /// @@ -389,7 +389,7 @@ public void Test_Maximum() public void Test_PDF() { var GPA = new GeneralizedPareto(); - Assert.AreEqual(0.1,GPA.PDF(100)); + Assert.AreEqual(0.1, GPA.PDF(100), 3E-17); // exp(-log(10)) differs by two ulps from the decimal literal. Assert.AreEqual(4.53999e-06, GPA.PDF(200), 1e-10); var GPA2 = new GeneralizedPareto(100, 10, 1); diff --git a/Test_Numerics/Distributions/Univariate/Test_GeneralizedRobustness.cs b/Test_Numerics/Distributions/Univariate/Test_GeneralizedRobustness.cs new file mode 100644 index 00000000..36213af3 --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Test_GeneralizedRobustness.cs @@ -0,0 +1,460 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Distributions; + +namespace Distributions.Univariate +{ + /// Independent probability, moment and local-MLE uncertainty contracts for Hosking's generalized families. + [TestClass] + public class Test_GeneralizedRobustness + { + /// The frozen R values distinguish exact nonzero shapes from the limiting distribution. + [TestMethod] + public void GeneralizedMoments_MatchIndependentRValues() + { + int count = 0; + foreach (var row in DistributionOracle.Read("generalized-fisher.csv").Where(r => r["quantity"] == "moment")) + { + var distribution = Create(row); + double[] moments = { distribution.Mean, distribution.StandardDeviation, distribution.Skewness, distribution.Kurtosis }; + int index = int.Parse(row["row"]) - 1; + double expected = Number(row, "value"); + Assert.AreEqual(expected, moments[index], Number(row, "absolute_error_estimate"), Context(row)); + count++; + } + Assert.AreEqual(88, count); + } + + /// Large-shape lognormal moments preserve finite products with very small scales. + [TestMethod] + public void GeneralizedNormal_MomentsAndMode_PreserveRepresentableProducts() + { + var distribution = new GeneralizedNormal(0, 1E-200, 30); + Assert.AreEqual(-Math.Exp(Math.Log(1E-200) + 450 - Math.Log(30)), distribution.Mean, 2E-19); + double expectedSd = Math.Exp(Math.Log(1E-200) + 900 - Math.Log(30)); + Assert.AreEqual(expectedSd, distribution.StandardDeviation, 2E-13 * expectedSd); + Assert.AreEqual(1E-200 / 30, distribution.Mode, 1E-215); + Assert.IsTrue(double.IsNegativeInfinity(distribution.Skewness)); + Assert.IsTrue(double.IsPositiveInfinity(distribution.Kurtosis)); + } + + /// Direct logarithms and survival probabilities remain informative after rounding and underflow. + [TestMethod] + public void GeneralizedProbabilities_ZeroShape_LogTailsAndInfiniteEndpoints() + { + var normal = new GeneralizedNormal(0, 1, 0); + Assert.AreEqual(-804.6084420137538, normal.LogCDF(-40), 2E-12); + Assert.AreEqual(-804.6084420137538, normal.LogCCDF(40), 2E-12); + Assert.AreEqual(-800.9189385332047, normal.LogPDF(40), 2E-12); + var logistic = new GeneralizedLogistic(0, 1, 0); + Assert.AreEqual(-1000d, logistic.LogCDF(-1000), 1E-12); + Assert.AreEqual(-1000d, logistic.LogCCDF(1000), 1E-12); + Assert.AreEqual(-1000d, logistic.LogPDF(-1000), 1E-12); + Assert.AreEqual(Math.Exp(-40), logistic.CCDF(40), 1E-32); + foreach (var distribution in new UnivariateDistributionBase[] { normal, logistic }) + { + Assert.AreEqual(0d, distribution.PDF(double.NegativeInfinity)); + Assert.AreEqual(0d, distribution.PDF(double.PositiveInfinity)); + Assert.AreEqual(0d, distribution.CDF(double.NegativeInfinity)); + Assert.AreEqual(1d, distribution.CDF(double.PositiveInfinity)); + Assert.ThrowsExactly(() => distribution.InverseCDF(double.NaN)); + } + } + + /// The latent shape transform remains finite when physical standardization overflows. + [TestMethod] + [DataRow("GNO", -1, -1072.0411870643629, -1531.7204579917529, 0d)] + [DataRow("GNO", 1, -1072.0411870643629, -1531.7204579917529, 0d)] + [DataRow("GLO", -1, -46.201488473558619, -509.71423934592178, 4.3044582966584941E-222)] + [DataRow("GLO", 1, -46.201488473558619, -509.71423934592178, 4.3044582966584941E-222)] + public void GeneralizedProbabilities_AffineOverflow_PreservesFiniteTransformedTail( + string family, int direction, double logTail, double logDensity, double density) + { + // Base R 4.4.3 pnorm/dnorm and defining logistic formulas; see generalized-affine-overflow.R/.md. + // The exact transform has |z|=46.201488473558619 although |x/alpha| exceeds binary64 range. + UnivariateDistributionBase distribution = family == "GNO" + ? new GeneralizedNormal(0, 1E-200, -20 * direction) + : new GeneralizedLogistic(0, 1E-200, -20 * direction); + double x = direction * 1E200; + double actualLogTail = direction < 0 ? distribution.LogCDF(x) : distribution.LogCCDF(x); + double actualTail = direction < 0 ? distribution.CDF(x) : distribution.CCDF(x); + Assert.AreEqual(logTail, actualLogTail, 5E-12); + Assert.AreEqual(logDensity, distribution.LogPDF(x), 5E-12); + Assert.AreEqual(Math.Exp(logTail), actualTail, Math.Exp(logTail) * 3E-12); + Assert.AreEqual(density, distribution.PDF(x), density * 3E-12); + } + + /// Nonzero tiny shapes retain their finite support and first-order quantile correction. + [TestMethod] + public void GeneralizedProbabilities_NearZeroShape_IsNotFlattened() + { + foreach (double k in new[] { -1E-6, 1E-6 }) + { + var normal = new GeneralizedNormal(0, 1, k); + var logistic = new GeneralizedLogistic(0, 1, k); + foreach (var distribution in new UnivariateDistributionBase[] { normal, logistic }) + { + Assert.AreEqual(1 / k, k < 0 ? distribution.Minimum : distribution.Maximum); + double z = distribution is GeneralizedNormal ? 1 : Math.Log(9d); + double p = distribution is GeneralizedNormal ? 0.8413447460685429 : 0.9; + double expected = z - k * z * z / 2 + k * k * z * z * z / 6; + Assert.AreEqual(expected, distribution.InverseCDF(p), 3E-14); + Assert.AreEqual(p, distribution.CDF(expected), 3E-14); + } + } + } + + /// The GLO stationary density point includes its exact boundary transition. + [TestMethod] + public void GeneralizedLogistic_ModeAndEndpointDensity_MatchAnalyticalLimits() + { + foreach (double k in new[] { -2d, -1d, -0.2, 0d, 0.2, 1d, 2d }) + { + var distribution = new GeneralizedLogistic(2, 3, k); + double expectedMode = Math.Abs(k) >= 1 ? 2 + 3 / k : k == 0 ? 2 + : 2 - 3 * (Math.Exp(-k * Math.Log((1 + k) / (1 - k))) - 1) / k; + Assert.AreEqual(expectedMode, distribution.Mode, 3E-14); + if (k == 0) continue; + double endpoint = k < 0 ? distribution.Minimum : distribution.Maximum; + double expectedDensity = Math.Abs(k) < 1 ? 0 : Math.Abs(k) == 1 ? 1d / 3 : double.PositiveInfinity; + Assert.AreEqual(expectedDensity, distribution.PDF(endpoint)); + } + } + + /// Moment divergence is determined by moment order, independently of MLE-information regularity. + [TestMethod] + public void GeneralizedLogistic_Moments_RespectEachExistenceBoundary() + { + foreach (double sign in new[] { -1d, 1d }) + { + Assert.IsTrue(double.IsNaN(new GeneralizedLogistic(0, 1, sign).Mean)); + Assert.IsTrue(double.IsNaN(new GeneralizedLogistic(0, 1, sign * 0.5).StandardDeviation)); + Assert.IsTrue(double.IsNaN(new GeneralizedLogistic(0, 1, sign / 3).Skewness)); + Assert.IsTrue(double.IsNaN(new GeneralizedLogistic(0, 1, sign * 0.25).Kurtosis)); + Assert.IsFalse(double.IsNaN(new GeneralizedLogistic(0, 1, sign * 0.49).Mean)); + } + } + + /// Symmetric L-moments produce a valid normal fit in the public xi/alpha/kappa coordinates. + [TestMethod] + public void GeneralizedNormal_LinearMoments_PreserveNormalLimit() + { + var distribution = new GeneralizedNormal(); + double[] parameters = distribution.ParametersFromLinearMoments(new[] { 2d, 3d, 0d, 0.122601719540891 }); + Assert.AreEqual(2d, parameters[0]); + Assert.AreEqual(3 * Math.Sqrt(Math.PI), parameters[1], 1E-14); + Assert.AreEqual(0d, parameters[2]); + double[] moments = distribution.LinearMomentsFromParameters(new[] { 2d, 3d, 0d }); + Assert.AreEqual(2d, moments[0]); + Assert.AreEqual(3 / Math.Sqrt(Math.PI), moments[1], 1E-14); + Assert.AreEqual(0d, moments[2]); + Assert.AreEqual(0.12260171954089095, moments[3], 2E-15); // 30*asin(1/3)/pi-9. + foreach (double k in new[] { -1E-6, 1E-6 }) + { + moments = distribution.LinearMomentsFromParameters(new[] { 2d, 3d, k }); + parameters = distribution.ParametersFromLinearMoments(moments); + Assert.AreEqual(2d, parameters[0], 1E-12); + Assert.AreEqual(3d, parameters[1], 3E-12); + Assert.AreEqual(k, parameters[2], 2E-13); + } + } + + /// Initialization rejects invalid samples before producing bounds or mutating fitted parameters. + [TestMethod] + public void GeneralizedInitialization_RejectsInvalidSamplesAndKeepsCenteredBounds() + { + foreach (var distribution in new IMaximumLikelihoodEstimation[] { new GeneralizedNormal(), new GeneralizedLogistic() }) + { + foreach (var sample in new[] { new[] { 1d, 2d, 3d }, new[] { 1d, 1d, 1d, 1d }, new[] { 0d, 1d, 2d, double.NaN }, new[] { 0d, 1d, 2d, double.PositiveInfinity } }) + Assert.Throws(() => distribution.GetParameterConstraints(sample)); + var constraints = distribution.GetParameterConstraints(new[] { -2d, -1d, 1d, 2d }); + for (int i = 0; i < 3; i++) + { + Assert.IsTrue(!double.IsInfinity(constraints.Item2[i]) && !double.IsInfinity(constraints.Item3[i])); + Assert.IsLessThan(constraints.Item3[i], constraints.Item2[i]); + Assert.IsTrue(constraints.Item1[i] >= constraints.Item2[i] && constraints.Item1[i] <= constraints.Item3[i]); + } + var candidate = (UnivariateDistributionBase)distribution; + candidate.SetParameters(constraints.Item1); + Assert.IsFalse(double.IsInfinity(candidate.LogLikelihood(new[] { -2d, -1d, 1d, 2d }))); + } + } + + /// Closed-form normal information agrees with independent complete-tail integration. + [TestMethod] + public void GeneralizedNormal_MleCovariance_MatchesIndependentRInformation() => CheckOracleCovariance("GNO"); + + /// Fixing hondo at minus one requires inversion of the GLO information block. + [TestMethod] + public void GeneralizedLogistic_MleCovariance_MatchesIndependentRInformation() => CheckOracleCovariance("GLO"); + + /// Kappa covariance uses all four score coordinates and includes heavy-tailed regular cases. + [TestMethod] + public void KappaFour_MleCovariance_MatchesIndependentRInformation() => CheckOracleCovariance("K4"); + + /// The zero-shape GNO covariance still estimates shape; it is not the two-parameter normal matrix. + [TestMethod] + public void GeneralizedNormal_MleCovariance_ExactZeroShapeAndLargeShape() + { + double[,] covariance = new GeneralizedNormal(0, 1, 0).ParameterCovariance(1, ParameterEstimationMethod.MaximumLikelihood); + double[,] expected = { { 7d / 6, 0, 1d / 3 }, { 0, 0.5, 0 }, { 1d / 3, 0, 2d / 3 } }; + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) Assert.AreEqual(expected[i, j], covariance[i, j], 2E-15); + covariance = new GeneralizedNormal(0, 1, 30).ParameterCovariance(1, ParameterEstimationMethod.MaximumLikelihood); + Assert.AreEqual(1d, covariance[0, 0]); + Assert.AreEqual(-30d, covariance[0, 1]); + Assert.AreEqual(900.5, covariance[1, 1]); + Assert.AreEqual(450d, covariance[2, 2]); + } + + /// Scale factors are combined before intermediate exponential or squared-shape overflow. + [TestMethod] + public void GeneralizedExtremeScales_PreserveRepresentableQuantilesAndCovarianceEntries() + { + double normalLatent = -37.047096299361199; // R qnorm(1e-300), independently rounded binary64 reference. + double expectedNormal = -Math.Exp(Math.Log(1E-200) - 30 * normalLatent - Math.Log(30)); + Assert.AreEqual(expectedNormal, new GeneralizedNormal(0, 1E-200, 30).InverseCDF(1E-300), Math.Abs(expectedNormal) * 3E-12); + double expectedLogistic = -Math.Exp(Math.Log(1E-308) - 2 * Math.Log(1E-300) - Math.Log(2)); + Assert.AreEqual(expectedLogistic, new GeneralizedLogistic(0, 1E-308, 2).InverseCDF(1E-300), Math.Abs(expectedLogistic) * 3E-12); + double[,] covariance = new GeneralizedNormal(0, 1E200, 30).ParameterCovariance(1, ParameterEstimationMethod.MaximumLikelihood); + double expectedCross = Math.Exp(Math.Log(1E200) - 900 + Math.Log(900d / 901)); + Assert.AreEqual(expectedCross, covariance[0, 2], expectedCross * 3E-12); + covariance = new GeneralizedNormal(0, 1E-200, 1E200).ParameterCovariance(1, ParameterEstimationMethod.MaximumLikelihood); + Assert.AreEqual(1d, covariance[1, 1], 3E-13); + covariance = new GeneralizedNormal(0, 1E-154, 1.4E154).ParameterCovariance(1, ParameterEstimationMethod.MaximumLikelihood); + Assert.AreEqual(9.8E307, covariance[2, 2], 3E294); + foreach (var distribution in new UnivariateDistributionBase[] { new GeneralizedNormal(-1E308, 1E308, .5), new GeneralizedLogistic(-1E308, 1E308, .5) }) + { + Assert.AreEqual(1E308, distribution.Maximum, 1E293); + Assert.AreEqual(1d, distribution.CDF(1.5E308)); + Assert.AreEqual(double.NegativeInfinity, distribution.LogPDF(1.5E308)); + } + } + + /// Covariances transform in xi/alpha coordinates and quantile variance is their delta contraction. + [TestMethod] + public void GeneralizedUncertainty_AffineScaleSampleSizeAndDeltaMethod() + { + foreach (var pair in new[] + { + (new GeneralizedNormal(0, 1, -0.2) as IStandardError, new GeneralizedNormal(7, 3, -0.2) as IStandardError), + (new GeneralizedLogistic(0, 1, 0.2) as IStandardError, new GeneralizedLogistic(7, 3, 0.2) as IStandardError), + (new KappaFour(0, 1, -1, -0.2) as IStandardError, new KappaFour(7, 3, -1, -0.2) as IStandardError) + }) + { + double[,] unit = pair.Item1.ParameterCovariance(1, ParameterEstimationMethod.MaximumLikelihood); + double[,] scaled = pair.Item2.ParameterCovariance(100, ParameterEstimationMethod.MaximumLikelihood); + double[] gradient = pair.Item2.QuantileGradient(0.9); + double expected = 0; + for (int i = 0; i < gradient.Length; i++) + for (int j = 0; j < gradient.Length; j++) + { + Assert.AreEqual(unit[i, j] * (i < 2 ? 3 : 1) * (j < 2 ? 3 : 1) / 100, scaled[i, j], 2E-11); + expected += gradient[i] * scaled[i, j] * gradient[j]; + } + Assert.IsGreaterThan(0d, expected); + Assert.AreEqual(expected, pair.Item2.QuantileVariance(0.9, 100, ParameterEstimationMethod.MaximumLikelihood), 2E-11 * expected); + } + } + + /// Scalar MLE variance preserves affine scale and reports true overflow independently of matrix range. + [TestMethod] + [DataRow("GNO", 0.011666666666666667)] + [DataRow("GLO", 0.03113972242232403)] + [DataRow("K4", 0.01550447368065933)] + public void GeneralizedQuantileVariance_ScaleAndOverflowClassification(string family, double unitVariance) + { + // GNO is exact; GLO/K4 contract the frozen R zero-shape matrices with analytical median gradients. + IStandardError Distribution(double alpha) => family == "GNO" ? new GeneralizedNormal(0, alpha, 0) + : family == "GLO" ? new GeneralizedLogistic(0, alpha, 0) : new KappaFour(0, alpha, 0, 0); + foreach (double alpha in new[] { 1E-150, 1d, 1E150 }) + { + double expected = unitVariance * alpha * alpha; + Assert.AreEqual(expected, Distribution(alpha).QuantileVariance(.5, 100, ParameterEstimationMethod.MaximumLikelihood), expected * 5E-8, family); + } + foreach (double probability in new[] { .1, .5, .9 }) + Assert.IsTrue(double.IsPositiveInfinity(Distribution(1E200).QuantileVariance(probability, 100, ParameterEstimationMethod.MaximumLikelihood)), family); + } + + /// A finite scalar can survive underflow or overflow in individual physical covariance entries. + [TestMethod] + [DataRow("GNO", 1.002832088139248E308)] + [DataRow("GLO", 3.032823215830043E-216)] + [DataRow("K4", 2.3597144586448996E-307)] + public void GeneralizedQuantileVariance_RecoversFiniteScalarAfterCovarianceRangeLoss(string family, double expected) + { + // Frozen R covariance entries contracted with analytical gradients using 400-digit Decimal arithmetic. + // The GNO case instead uses its exact closed-form covariance at k=2. + IStandardError distribution = family == "GNO" ? new GeneralizedNormal(0, 1E155, 2) + : family == "GLO" ? new GeneralizedLogistic(0, 1E-170, .2) : new KappaFour(0, 1E-170, -1, -.2); + double probability = family == "GNO" ? .5 : family == "GLO" ? 1E-300 : 1 - 1E-16; + Assert.AreEqual(expected, distribution.QuantileVariance(probability, 100, ParameterEstimationMethod.MaximumLikelihood), expected * 5E-8, family); + } + + /// Exact positive quadratic terms retain variance lost by overflowing gradients or endpoint cancellation. + [TestMethod] + [DataRow(20d, 1E-200, 1E-300, 2.5768579804728683E244)] + [DataRow(30d, 1E200, 0.9999999999999999, 4.158459661237856E185)] + public void GeneralizedNormal_QuantileVariance_PreservesExponentialScaleAndCancellation(double kappa, double alpha, double probability, double expected) + { + // 450-digit Decimal evaluation of the exact positive quadratic, with R qnorm references + // z=-37.047096299361199 and z=8.209536151601387 for these binary64 probabilities. + var distribution = new GeneralizedNormal(0, alpha, kappa); + Assert.AreEqual(expected, distribution.QuantileVariance(probability, 100, ParameterEstimationMethod.MaximumLikelihood), expected * 2E-10); + } + + /// Uncertainty validates its own regularity domain without narrowing distribution validity. + [TestMethod] + public void GeneralizedUncertainty_RejectsInvalidArgumentsAndNonregularShapes() + { + foreach (var distribution in new IStandardError[] { new GeneralizedNormal(), new GeneralizedLogistic(), new KappaFour() }) + { + foreach (double p in new[] { 0d, 1d, -0.1, 1.1, double.NaN, double.PositiveInfinity }) + { + Assert.ThrowsExactly(() => distribution.QuantileGradient(p)); + Assert.ThrowsExactly(() => distribution.QuantileVariance(p, 10, ParameterEstimationMethod.MaximumLikelihood)); + } + foreach (int n in new[] { 0, -1 }) + Assert.ThrowsExactly(() => distribution.ParameterCovariance(n, ParameterEstimationMethod.MaximumLikelihood)); + } + foreach (var distribution in new UnivariateDistributionBase[] { new GeneralizedLogistic(0, 1, -0.5), new GeneralizedLogistic(0, 1, 0.5), new KappaFour(0, 1, 0.5, 0), new KappaFour(0, 1, 0, 0.5), new KappaFour(0, 1, -1, -0.5) }) + { + Assert.IsTrue(distribution.ParametersValid); + Assert.ThrowsExactly(() => ((IStandardError)distribution).ParameterCovariance(100, ParameterEstimationMethod.MaximumLikelihood)); + } + } + + /// MLE covariance is never substituted for a different estimator's uncertainty. + [TestMethod] + public void GeneralizedUncertainty_OtherEstimatorsRemainExplicitlyUnsupported() + { + foreach (var distribution in new IStandardError[] { new GeneralizedNormal(), new GeneralizedLogistic(), new KappaFour() }) + foreach (var method in new[] { ParameterEstimationMethod.MethodOfMoments, ParameterEstimationMethod.MethodOfLinearMoments }) + { + Assert.ThrowsExactly(() => distribution.ParameterCovariance(100, method)); + Assert.ThrowsExactly(() => distribution.QuantileVariance(0.9, 100, method)); + } + } + + /// All score components integrate to zero and the principal information block matches the frozen R matrix. + [TestMethod] + public void KappaExpectedInformation_MeansErrorsAndMatrix_MatchIndependentR() + { + foreach (var group in DistributionOracle.Read("generalized-fisher.csv") + .Where(r => r["family"] != "GNO" && r["quantity"] == "information") + .GroupBy(r => r["family"] + ":" + r["kappa"] + ":" + r["hondo"])) + { + var first = group.First(); + int count = first["family"] == "K4" ? 4 : 3; + double[,] information = KappaExpectedInformation.ExpectedInformation(Number(first, "kappa"), Number(first, "hondo"), count, + out double[] means, out double[] meanErrors, out double[,] informationErrors); + for (int i = 0; i < count; i++) + Assert.IsLessThanOrEqualTo(4 * meanErrors[i] + 8E-15, Math.Abs(means[i]), Context(first)); + foreach (var row in group) + { + int i = int.Parse(row["row"]) - 1, j = int.Parse(row["column"]) - 1; + double expected = Number(row, "value"); + Assert.AreEqual(expected, information[i, j], Math.Max(5E-9 * Math.Max(1, Math.Abs(expected)), + 8 * (Number(row, "absolute_error_estimate") + informationErrors[i, j])), Context(row)); + Assert.IsLessThanOrEqualTo(1E-12 + 1E-10 * Math.Abs(information[i, j]), informationErrors[i, j]); + } + } + } + + /// A mathematically regular but unresolved boundary approach must not produce a finite-looking covariance. + [TestMethod] + public void KappaExpectedInformation_UnresolvedBoundaryApproach_ThrowsNumericalFailure() + { + var distribution = new KappaFour(0, 1, 0.499999999999, 0); + Assert.IsTrue(distribution.ParametersValid); + Assert.ThrowsExactly(() => distribution.ParameterCovariance(100, ParameterEstimationMethod.MaximumLikelihood)); + // Full Kappa information has a very narrow upper-tail feature at these shapes. + // Its unresolvable finite-shape calculation must fail explicitly, rather than + // mistaking log(t)=log(survival) for a uniform approximation in hondo. + var extremeHondo = new KappaFour(0, 1, 0, -1E200); + Assert.IsTrue(extremeHondo.ParametersValid); + Assert.ThrowsExactly(() => extremeHondo.ParameterCovariance(100, ParameterEstimationMethod.MaximumLikelihood)); + } + + /// The first four interior floating-point arguments are checked separately at every finite Kappa endpoint. + [TestMethod] + public void KappaFour_FirstFourAdjacentEndpointValues_MatchIndependentDecimal() + { + int count = 0; + foreach (var row in DistributionOracle.Read("generalized-adjacent-boundaries.csv")) + { + var distribution = new KappaFour(0, 1, Number(row, "kappa"), Number(row, "hondo")); + double x = Number(row, "x"); + string context = $"k={row["kappa"]}, h={row["hondo"]}, {row["endpoint"]} step={row["step"]}, x={x:R}"; + Assert.AreEqual(Number(row, "logcdf"), distribution.LogCDF(x), 5E-10, context); + Assert.AreEqual(Number(row, "logpdf"), distribution.LogPDF(x), 5E-10, context); + count++; + } + Assert.AreEqual(180, count); + var changing = new KappaFour(0, 1, -2, 10); + Assert.AreEqual(49.5, changing.Minimum); + changing.Xi = 2; + Assert.AreEqual(51.5, changing.Minimum); + changing.Alpha = 3; + Assert.AreEqual(150.5, changing.Minimum); + changing.Kappa = -1; + Assert.AreEqual(29d, changing.Minimum); + changing.Hondo = 2; + Assert.AreEqual(5d, changing.Minimum); + } + + /// Analytical shape derivatives retain their nonzero normal/logistic limit. + [TestMethod] + public void GeneralizedQuantileGradients_ZeroShapeAndSingularJacobians() + { + foreach (var distribution in new IStandardError[] { new GeneralizedNormal(2, 3, 0), new GeneralizedLogistic(2, 3, 0) }) + { + double z = distribution is GeneralizedNormal ? 1 : Math.Log(9d); + double p = distribution is GeneralizedNormal ? 0.8413447460685429 : 0.9; + double[] gradient = distribution.QuantileGradient(p); + Assert.AreEqual(1d, gradient[0]); + Assert.AreEqual(z, gradient[1], 3E-14); + Assert.AreEqual(-3 * z * z / 2, gradient[2], 3E-14); + distribution.QuantileJacobian(new[] { 0.2, 0.2, 0.9 }, out double determinant); + Assert.AreEqual(0d, determinant); + } + new KappaFour(0, 1, 0, 0).QuantileJacobian(new[] { 0.2, 0.2, 0.6, 0.9 }, out double kappaDeterminant); + Assert.AreEqual(0d, kappaDeterminant); + } + + private static void CheckOracleCovariance(string family) + { + int count = 0; + foreach (var group in DistributionOracle.Read("generalized-fisher.csv").Where(r => r["family"] == family && r["quantity"] == "covariance").GroupBy(r => r["kappa"] + ":" + r["hondo"])) + { + var distribution = (IStandardError)Create(group.First()); + double[,] covariance = distribution.ParameterCovariance(1, ParameterEstimationMethod.MaximumLikelihood); + foreach (var row in group) + { + int i = int.Parse(row["row"]) - 1; + int j = int.Parse(row["column"]) - 1; + double expected = Number(row, "value"); + double tolerance = Math.Max(5E-9 * Math.Max(1, Math.Abs(expected)), 8 * Number(row, "absolute_error_estimate")); + Assert.AreEqual(expected, covariance[i, j], tolerance, Context(row)); + Assert.AreEqual(covariance[i, j], covariance[j, i], 1E-12); + if (i == j) Assert.IsGreaterThan(0d, covariance[i, i]); + } + count++; + } + Assert.AreEqual(family == "GNO" ? 9 : family == "GLO" ? 11 : 18, count); + } + + private static UnivariateDistributionBase Create(Dictionary row) + { + double k = Number(row, "kappa"); + if (row["family"] == "GNO") return new GeneralizedNormal(0, 1, k); + if (row["family"] == "GLO") return new GeneralizedLogistic(0, 1, k); + return new KappaFour(0, 1, k, Number(row, "hondo")); + } + + private static double Number(Dictionary row, string column) => DistributionOracle.Number(row[column]); + private static string Context(Dictionary row) => $"{row["family"]} k={row["kappa"]} h={row["hondo"]} {row["quantity"]} [{row["row"]},{row["column"]}]"; + } +} diff --git a/Test_Numerics/Distributions/Univariate/Test_KappaFourRegression.cs b/Test_Numerics/Distributions/Univariate/Test_KappaFourRegression.cs index 0465dc57..cf67cbec 100644 --- a/Test_Numerics/Distributions/Univariate/Test_KappaFourRegression.cs +++ b/Test_Numerics/Distributions/Univariate/Test_KappaFourRegression.cs @@ -87,8 +87,10 @@ public void MleDoesNotInstallAnUnsuccessfulEstimate() { var d = new KappaFour(); double[] before = d.GetParameters; - var exception = Assert.ThrowsExactly(() => d.Estimate(FittingSample, ParameterEstimationMethod.MaximumLikelihood)); - StringAssert.Contains(exception.Message, "MaximumIterationsReached"); + // Nonfinite data produce a deterministic fitting failure independent of the optimizer trajectory. + double[] invalidSample = (double[])FittingSample.Clone(); + invalidSample[17] = double.NaN; + Assert.ThrowsExactly(() => d.Estimate(invalidSample, ParameterEstimationMethod.MaximumLikelihood)); AssertVector(before, d.GetParameters, 0); } @@ -206,7 +208,7 @@ public void EndpointDensitiesHaveDefinedLimits() } var singular = new KappaFour(0, 1, 0, 1.5); Assert.AreEqual(double.PositiveInfinity, singular.PDF(singular.Minimum)); - Assert.AreEqual(double.NegativeInfinity, singular.LogPDF(singular.Minimum)); + Assert.AreEqual(double.PositiveInfinity, singular.LogPDF(singular.Minimum)); Assert.AreEqual(1d, new KappaFour(0, 1, 1, 0).PDF(1)); } diff --git a/Test_Numerics/Distributions/Univariate/Test_LnNormalParameterContract.cs b/Test_Numerics/Distributions/Univariate/Test_LnNormalParameterContract.cs new file mode 100644 index 00000000..dbecfe22 --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Test_LnNormalParameterContract.cs @@ -0,0 +1,35 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Distributions; + +namespace Distributions.Univariate +{ + /// Physical-parameter preservation across conversion, cloning and serialization. + [TestClass] + public class Test_LnNormalParameterContract + { + /// Persisted physical moments are not repeatedly rounded through logarithmic coordinates. + [TestMethod] + public void PhysicalParameterVectorSurvivesRoundTripExactly() + { + double[] parameters = { 9.9999999999999982, 9.9999999999999964 }; + var distribution = new LnNormal(parameters[0], parameters[1]); + CollectionAssert.AreEqual(parameters, distribution.GetParameters); + CollectionAssert.AreEqual(parameters, distribution.Clone().GetParameters); + var clone = new LnNormal(); + clone.SetParameters(distribution.GetParameters); + CollectionAssert.AreEqual(parameters, clone.GetParameters); + } + + /// Direct mutations of log coordinates invalidate the preserved physical representation. + [TestMethod] + public void LogParameterMutationRefreshesPhysicalMoments() + { + var distribution = new LnNormal(10, 2); + distribution.Mu = 0; + distribution.Sigma = 1; + Assert.AreEqual(Math.Exp(.5), distribution.Mean, 2E-15); + Assert.AreEqual(Math.Sqrt(Math.E * (Math.E - 1)), distribution.StandardDeviation, 2E-15); + } + } +} diff --git a/Test_Numerics/Distributions/Univariate/Test_LogPearsonTypeIII.cs b/Test_Numerics/Distributions/Univariate/Test_LogPearsonTypeIII.cs index 204dba09..9d3eeb39 100644 --- a/Test_Numerics/Distributions/Univariate/Test_LogPearsonTypeIII.cs +++ b/Test_Numerics/Distributions/Univariate/Test_LogPearsonTypeIII.cs @@ -275,10 +275,11 @@ public void Test_Median() public void Test_Mode() { var LP3 = new LogPearsonTypeIII(); - Assert.AreEqual(1000, LP3.Mode, 1e-04); + // Frozen defining-formula evidence: docs/distributions/oracles/normal-pearson.R. + Assert.AreEqual(265.67685816501944, LP3.Mode, 1e-10); var LP3ii = new LogPearsonTypeIII(1, 1, 1); - Assert.AreEqual(3.16227, LP3ii.Mode, 1e-04); + Assert.AreEqual(0.4980296964878923, LP3ii.Mode, 1e-13); } /// @@ -293,7 +294,8 @@ public void Test_Minimum() var LP3ii = new LogPearsonTypeIII(1,1,1); Assert.AreEqual(0.1, LP3ii.Minimum, 1e-05); - var LP3iii = new LogPearsonTypeIII(1, -1, 1); + // A bounded upper tail requires negative skew and a positive standard deviation. + var LP3iii = new LogPearsonTypeIII(1, 1, -1); Assert.AreEqual(0,LP3iii.Minimum); } @@ -309,7 +311,7 @@ public void Test_Maximum() var LP3ii = new LogPearsonTypeIII(1,1,1); Assert.AreEqual(double.PositiveInfinity, LP3ii.Maximum); - var LP3iii = new LogPearsonTypeIII(1, -1, 1); + var LP3iii = new LogPearsonTypeIII(1, 1, -1); Assert.AreEqual(1000, LP3iii.Maximum, 1e-04); } diff --git a/Test_Numerics/Distributions/Univariate/Test_Mixture.cs b/Test_Numerics/Distributions/Univariate/Test_Mixture.cs index f41b1918..d0335385 100644 --- a/Test_Numerics/Distributions/Univariate/Test_Mixture.cs +++ b/Test_Numerics/Distributions/Univariate/Test_Mixture.cs @@ -476,7 +476,8 @@ public void Test_Mixture_ZeroInflatedCDF_UsesSurvivalRatio() double expected = 1.0 - distribution.CCDF(1.0) / distribution.CCDF(0.0); Assert.AreEqual(0.5, expected, 0.0); - Assert.AreEqual(expected, mixture.CDF(1.0), 0.0); + // Logarithmic normalization introduces only final binary64 rounding. + Assert.AreEqual(expected, mixture.CDF(1.0), 2E-15); Assert.IsTrue(mixture.CDF(1.0) >= 0.0 && mixture.CDF(1.0) <= 1.0); } diff --git a/Test_Numerics/Distributions/Univariate/Test_NormalPearsonRobustness.cs b/Test_Numerics/Distributions/Univariate/Test_NormalPearsonRobustness.cs new file mode 100644 index 00000000..3cb54872 --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Test_NormalPearsonRobustness.cs @@ -0,0 +1,459 @@ +using System; +using System.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Distributions; + +namespace Distributions.Univariate +{ + /// + /// Independent tail, moment-coordinate and uncertainty regressions for the normal and Pearson families. + /// + [TestClass] + public class Test_NormalPearsonRobustness + { + /// Scalar uncertainty remains meaningful when physical covariance entries or intermediate squares overflow. + [TestMethod] + public void UncertaintyAtExtremeScaleCombinesFactorsBeforeSquaring() + { + const ParameterEstimationMethod method = ParameterEstimationMethod.MaximumLikelihood; + Assert.AreEqual(1E308, new Normal(0, 1E155).ParameterCovariance(100, method)[0, 0], 3E292); + Assert.AreEqual(3E307, new Logistic(0, 1E155).ParameterCovariance(1000, method)[0, 0], 8E291); + foreach (IStandardError distribution in new IStandardError[] + { new Normal(0, 1E200), new Logistic(0, 1E200), new PearsonTypeIII(0, 1E200, .5) }) + Assert.AreEqual(double.PositiveInfinity, distribution.QuantileVariance(.5, 100, method)); + var natural = new LnNormal { Mu = 200, Sigma = 20 }; + Assert.AreEqual(4 * Math.Exp(400), natural.QuantileVariance(.5, 100, method), 8E161); + var logged = new LogPearsonTypeIII(370, 1E-10, 0) { Base = Math.E }; + // Full-family zero skew includes variance of estimated skew: median unit variance is 7/(6*n). + Assert.AreEqual((7d / 6) * 2.38735282838454E299, logged.QuantileVariance(.5, 100, method), 2E287); + } + + /// Normal log tails remain finite beyond ordinary probability underflow. + [TestMethod] + public void Normal_LogTailsAndLargeScale_MatchDefiningDensity() + { + var normal = new Normal(); + Assert.AreEqual(-804.6084420137538, normal.LogCDF(-40), 2E-12); + Assert.AreEqual(-804.6084420137538, normal.LogCCDF(40), 2E-12); + Assert.AreEqual(-710.11514717537079, new Normal(0, 1E308).LogPDF(0), 2E-12); + Assert.AreEqual(new Normal().LogPDF(2) - Math.Log(1E308), new Normal(-1E308, 1E308).LogPDF(1E308), 2E-12); + Assert.AreEqual(0d, normal.CCDF(double.PositiveInfinity)); + Assert.AreEqual(1d, normal.CCDF(double.NegativeInfinity)); + } + + /// Logistic evaluation uses direct log tails and exact infinite endpoints. + [TestMethod] + public void Logistic_LogTailsAndEndpoints_MatchDefiningLogit() + { + var logistic = new Logistic(0, 1); + Assert.AreEqual(-1000d, logistic.LogPDF(-1000), 1E-12); + Assert.AreEqual(-1000d, logistic.LogPDF(1000), 1E-12); + Assert.AreEqual(-1000d, logistic.LogCDF(-1000), 1E-12); + Assert.AreEqual(-1000d, logistic.LogCCDF(1000), 1E-12); + Assert.AreEqual(0d, logistic.PDF(double.NegativeInfinity)); + Assert.AreEqual(0d, logistic.PDF(double.PositiveInfinity)); + Assert.AreEqual(Math.Exp(-40), logistic.CCDF(40), 1E-32); + } + + /// Signed and exactly centered samples have finite feasible optimizer initialization. + [TestMethod] + public void LocationScaleConstraints_SignedZeroAndLargeSamples_AreFiniteAndFeasible() + { + foreach (double[] sample in new[] { new[] { -5d, -4d, -2d, -1d }, new[] { -2d, -1d, 1d, 2d } }) + { + CheckConstraints(new Normal().GetParameterConstraints(sample)); + CheckConstraints(new Logistic().GetParameterConstraints(sample)); + CheckConstraints(new PearsonTypeIII().GetParameterConstraints(sample)); + } + CheckConstraints(new Normal().GetParameterConstraints(new[] { -1E200, -5E199, 5E199, 1E200 })); + CheckConstraints(new LogNormal().GetParameterConstraints(new[] { 0.01d, 0.1d, 10d, 100d })); + CheckConstraints(new LogPearsonTypeIII().GetParameterConstraints(new[] { 0.01d, 0.1d, 10d, 100d })); + } + + /// Malformed samples are rejected before fitting or replacing nonpositive observations. + [TestMethod] + public void SampleValidation_RejectsNonfiniteDegenerateAndNonpositiveSamples() + { + IMaximumLikelihoodEstimation[] distributions = { new Normal(), new Logistic(), new LnNormal(), new LogNormal(), new PearsonTypeIII(), new LogPearsonTypeIII() }; + foreach (var distribution in distributions) + { + Assert.Throws(() => distribution.GetParameterConstraints(new[] { 1d, double.NaN, 2d, 3d })); + Assert.Throws(() => distribution.GetParameterConstraints(new[] { 1d, double.PositiveInfinity, 2d, 3d })); + Assert.Throws(() => distribution.GetParameterConstraints(new[] { 2d, 2d, 2d, 2d })); + Assert.Throws(() => distribution.GetParameterConstraints(new[] { 2d })); + CheckConstraints(distribution.GetParameterConstraints(new[] { 1d, 2d, 3d, 4d })); + } + Assert.Throws(() => LnNormal.IndirectMethodOfMoments(new[] { -1d, 1d, 2d, 3d })); + Assert.Throws(() => new LogNormal().IndirectMethodOfMoments(new[] { 0d, 1d, 2d, 3d })); + Assert.Throws(() => new LogPearsonTypeIII().IndirectMethodOfMoments(new[] { 0d, 1d, 2d, 3d })); + } + + /// LnNormal public conversion coordinates are physical mean and standard deviation. + [TestMethod] + public void LnNormal_PublicMomentCoordinates_AndTinyVarianceRoundTrip() + { + var distribution = new LnNormal(10, 2); + double[] parameters = distribution.ParametersFromMoments(new[] { 10d, 2d }); + Assert.AreEqual(10d, parameters[0], 1E-13); + Assert.AreEqual(2d, parameters[1], 1E-13); + Assert.IsFalse(new LnNormal(-10, 2).ParametersValid); + var tiny = new LnNormal(1, 1E-10); + Assert.AreEqual(1E-10, tiny.StandardDeviation, 1E-23); + Assert.AreEqual(1d, tiny.Mean, 1E-14); + // d[m^2/sqrt(m^2+s^2)]/dm at (m,s)=(10,2) is exactly 135/(26*sqrt(26)). + Assert.AreEqual(135d / (26d * Math.Sqrt(26d)), distribution.QuantileGradient(0.5)[0], 2E-15); + CheckGradient(distribution, 0.83, 3E-7); + } + + /// The indirect moment estimator transforms log-sample uncertainty into physical coordinates. + [TestMethod] + public void LnNormal_IndirectMomentCovarianceAndMedianVariance_MatchLogEstimator() + { + var distribution = new LnNormal(1, 1); + double[,] covariance = distribution.ParameterCovariance(100, ParameterEstimationMethod.MethodOfMoments); + double logVariance = Math.Log(2d); + Assert.AreEqual((logVariance + logVariance * logVariance / 2d) / 100d, covariance[0, 0], 2E-15); + Assert.AreEqual((logVariance + 1.5d * logVariance * logVariance) / 100d, covariance[0, 1], 2E-15); + Assert.AreEqual((logVariance + 4.5d * logVariance * logVariance) / 100d, covariance[1, 1], 2E-14); + Assert.AreEqual(0.0034657359027997265d, distribution.QuantileVariance(0.5, 100, ParameterEstimationMethod.MethodOfMoments), 3E-15); + var fitted = new LnNormal(); + fitted.Estimate(new[] { Math.Exp(-Math.Sqrt(1.5d)), 1d, 1d, Math.Exp(Math.Sqrt(1.5d)) }, ParameterEstimationMethod.MethodOfMoments); + Assert.AreEqual(0d, fitted.Mu, 2E-15); + Assert.AreEqual(1d, fitted.Sigma, 2E-15); + Assert.AreEqual(Math.Exp(0.5), fitted.GetParameters[0], 2E-14); + CheckCovarianceAndVariance(distribution, ParameterEstimationMethod.MethodOfMoments, 0.5); + } + + /// The MLE delta transformation includes both public coordinates. + [TestMethod] + public void LnNormal_MleCovariance_MatchesTransformedNormalInformation() + { + var distribution = new LnNormal(10, 2); + double variance = distribution.QuantileVariance(0.8, 100, ParameterEstimationMethod.MaximumLikelihood); + double z = Normal.StandardZ(0.8); + double expected = Math.Pow(distribution.InverseCDF(0.8), 2) * Math.Log(1.04) / 100 * (1 + z * z / 2); + Assert.AreEqual(expected, variance, 2E-13); + CheckCovarianceAndVariance(distribution, ParameterEstimationMethod.MaximumLikelihood, 0.8); + } + + /// LogNormal conversion and mode account for the logarithm base. + [TestMethod] + public void LogNormal_MomentConversionModeAndBaseRoundTrip_MatchLognormal() + { + foreach (double logarithmBase in new[] { Math.E, 2d, 10d }) + { + var distribution = new LogNormal { Base = logarithmBase }; + double[] parameters = distribution.ParametersFromMoments(new[] { 10d, 2d }); + distribution.SetParameters(parameters); + Assert.AreEqual(10d, distribution.Mean, 1E-12); + Assert.AreEqual(2d, distribution.StandardDeviation, 1E-12); + Assert.AreEqual(10d / Math.Pow(1.04, 1.5), distribution.Mode, 1E-12); + double[] moments = distribution.MomentsFromParameters(parameters); + Assert.AreEqual(10d, moments[0], 1E-12); + Assert.AreEqual(2d, moments[1], 1E-12); + CheckGradient(distribution, 0.83, 5E-7); + CheckCovarianceAndVariance(distribution, ParameterEstimationMethod.MaximumLikelihood, 0.83); + } + Assert.AreEqual(0.0860086348330568, new LogNormal().ParametersFromMoments(new[] { 10d, 2d })[1], 2E-16); + } + + /// Standardized lognormal moments depend on shape, even when raw moments overflow. + [TestMethod] + public void LogNormal_ShapeMoments_AreScaleIndependent() + { + var ordinary = new LogNormal(0, 0.2); + var huge = new LogNormal(200, 0.2); + Assert.AreEqual(ordinary.Skewness, huge.Skewness, 2E-14); + Assert.AreEqual(ordinary.Kurtosis, huge.Kurtosis, 2E-14); + Assert.IsTrue(IsFinite(huge.StandardDeviation)); + var tiny = new LogNormal(0, 1E-10) { Base = Math.E }; + Assert.AreEqual(1E-10, tiny.StandardDeviation, 1E-23); + } + + /// Base one, nonfinite bases and bases below one are invalid. + [TestMethod] + public void LogarithmBases_RequireFiniteValuesGreaterThanOne() + { + foreach (double value in new[] { 0d, 1d, double.NaN, double.PositiveInfinity }) + { + Assert.Throws(() => new LogNormal { Base = value }); + Assert.Throws(() => new LogPearsonTypeIII { Base = value }); + } + } + + /// Negative skew inverts the gamma upper tail directly. + [TestMethod] + public void Pearson_NegativeSkewDeepTail_MatchesReflectedExponential() + { + var distribution = new PearsonTypeIII(0, 1, -2); + double quantile = distribution.InverseCDF(1E-20); + Assert.AreEqual(-45.051701859880914, quantile, 2E-12); + Assert.AreEqual(Math.Log(1E-20), distribution.LogCDF(quantile), 2E-12); + Assert.AreEqual(1E-20, distribution.CDF(quantile), 2E-32); + var reflected = new PearsonTypeIII(0, 1, 2); + Assert.AreEqual(distribution.LogCDF(quantile), reflected.LogCCDF(-quantile), 2E-12); + } + + /// Gamma density endpoint limits cover shape one and shape below one. + [TestMethod] + public void Pearson_DensityEndpointAndModes_MatchGammaLimits() + { + Assert.AreEqual(0d, new PearsonTypeIII(1, 1, 2).LogPDF(0), 0d); + Assert.AreEqual(1d, new PearsonTypeIII(1, 1, 2).PDF(0), 0d); + foreach (double skew in new[] { -3d, 3d }) + { + var distribution = new PearsonTypeIII(1, 2, skew); + Assert.AreEqual(distribution.Xi, distribution.Mode, 0d); + Assert.AreEqual(double.PositiveInfinity, distribution.LogPDF(distribution.Xi)); + } + } + + /// Small nonzero skew retains its signed first-order quantile correction. + [TestMethod] + public void Pearson_SmallSkew_PreservesShapeAndNormalLimit() + { + foreach (double skew in new[] { -1E-5, 1E-5 }) + { + var distribution = new PearsonTypeIII(0, 1, skew); + Assert.AreEqual(-skew / 6d, distribution.InverseCDF(0.5), 3E-11); + Assert.AreEqual(1d / 6d, (0.5 - distribution.CDF(0)) / (-skew * Math.Exp(-0.5 * Math.Log(2 * Math.PI))), 3E-5); + Assert.AreEqual(skew > 0 ? -2d / skew : double.NegativeInfinity, distribution.Minimum); + } + } + + /// Pearson uncertainty is expressed in the same public coordinates as quantiles. + [TestMethod] + public void Pearson_PublicGradientCovarianceAndNormalLimit_AreConsistent() + { + foreach (double skew in new[] { -1.2d, -0.2d, 0d, 0.2d, 1.2d }) + { + var distribution = new PearsonTypeIII(2, 3, skew); + CheckGradient(distribution, 0.83, 2E-5); + CheckCovarianceAndVariance(distribution, ParameterEstimationMethod.MaximumLikelihood, 0.83); + CheckCovarianceAndVariance(distribution, ParameterEstimationMethod.MethodOfMoments, 0.83); + double[] publicGradient = distribution.QuantileGradient(0.83); + double[] momentGradient = distribution.QuantileGradientForMoments(0.83); + for (int i = 0; i < 3; i++) Assert.AreEqual(publicGradient[i], momentGradient[i], 0d); + } + double[,] covariance = new PearsonTypeIII(2, 3, 0).ParameterCovariance(100, ParameterEstimationMethod.MaximumLikelihood); + Assert.AreEqual(0.09, covariance[0, 0], 1E-15); + Assert.AreEqual(0.045, covariance[1, 1], 1E-15); + Assert.AreEqual(0.06, covariance[2, 2], 1E-15); + foreach (double skew in new[] { -2d, -Math.Sqrt(2), Math.Sqrt(2), 2d }) + Assert.Throws(() => new PearsonTypeIII(0, 1, skew).ParameterCovariance(100, ParameterEstimationMethod.MaximumLikelihood)); + } + + /// The negative-skew exponential transformed with base e is uniform on (0,e). + [TestMethod] + public void LogPearson_UniformCase_AndTransformedEndpoints() + { + var distribution = new LogPearsonTypeIII(0, 1, -2) { Base = Math.E }; + Assert.AreEqual(-1d, distribution.LogPDF(0), 1E-14); + Assert.AreEqual(-1d, distribution.LogPDF(1), 1E-14); + Assert.AreEqual(-1d, distribution.LogPDF(Math.E), 1E-14); + Assert.AreEqual(Math.E / 2, distribution.Mean, 2E-14); + Assert.AreEqual(Math.E / Math.Sqrt(12), distribution.StandardDeviation, 2E-14); + Assert.AreEqual(0d, distribution.Skewness, 2E-13); + Assert.AreEqual(1.8d, distribution.Kurtosis, 2E-12); + Assert.AreEqual(Math.E * 1E-20, distribution.InverseCDF(1E-20), 2E-31); + } + + /// Each LP3 moment has its own existence threshold. + [TestMethod] + public void LogPearson_MomentExistence_IsCheckedForEachOrder() + { + var distribution = new LogPearsonTypeIII(0, 0.8, 1) { Base = Math.E }; + Assert.AreEqual(1.55784d, distribution.Mean, 1E-5); + Assert.AreEqual(4.80099d, distribution.StandardDeviation, 1E-5); + Assert.AreEqual(double.PositiveInfinity, distribution.Skewness); + Assert.AreEqual(double.PositiveInfinity, distribution.Kurtosis); + var normalLimit = new LogPearsonTypeIII(0.3, 0.2, 0) { Base = Math.E }; + Assert.AreEqual(Math.Exp(0.26), normalLimit.Mode, 2E-14); + var skewed = new LogPearsonTypeIII(0.3, 0.2, 0.8) { Base = Math.E }; + Assert.AreEqual(Math.Exp(skewed.Xi + (skewed.Alpha - 1) * skewed.Beta / (1 + skewed.Beta)), skewed.Mode, 2E-14); + } + + /// LP3 quantile derivatives transform actual quantiles with one Jacobian factor. + [TestMethod] + public void LogPearson_PublicCoordinatesBaseRoundTripAndVariance_AreConsistent() + { + foreach (double skew in new[] { -0.8d, 0d, 0.8d }) + { + var distribution = new LogPearsonTypeIII(0.3, 0.2, skew) { Base = Math.E }; + CheckGradient(distribution, 0.83, 3E-5); + CheckCovarianceAndVariance(distribution, ParameterEstimationMethod.MaximumLikelihood, 0.83); + CheckCovarianceAndVariance(distribution, ParameterEstimationMethod.MethodOfMoments, 0.83); + double[] moments = distribution.MomentsFromParameters(distribution.GetParameters); + Assert.AreEqual(distribution.Mean, moments[0], 1E-13); + Assert.AreEqual(distribution.StandardDeviation, moments[1], 1E-13); + } + } + + /// Uncertainty methods reject endpoint probabilities and invalid sample sizes. + [TestMethod] + public void Uncertainty_RejectsNoninteriorProbabilitiesAndInvalidSampleSizes() + { + IStandardError[] distributions = { new Normal(), new Logistic(), new LnNormal(), new LogNormal(), new PearsonTypeIII(), new LogPearsonTypeIII() }; + foreach (IStandardError distribution in distributions) + { + foreach (double probability in new[] { 0d, 1d, double.NaN, double.PositiveInfinity, -0.1d }) + { + Assert.Throws(() => distribution.QuantileGradient(probability)); + Assert.Throws(() => distribution.QuantileVariance(probability, 100, ParameterEstimationMethod.MaximumLikelihood)); + } + foreach (int sampleSize in new[] { -1, 0 }) + Assert.Throws(() => distribution.ParameterCovariance(sampleSize, ParameterEstimationMethod.MaximumLikelihood)); + } + } + + /// Frozen R stats quantiles, numerical derivatives and independently inverted Fisher matrices agree. + [TestMethod] + public void FrozenRStatsOracle_MatchesPublicQuantilesGradientsAndCovariances() + { + int count = 0; + foreach (Dictionary row in DistributionOracle.Read("normal-pearson.csv")) + { + double Number(string key) => DistributionOracle.Number(row[key]); + UnivariateDistributionBase distribution = row["family"] switch + { + "PearsonTypeIII" => new PearsonTypeIII(Number("mu"), Number("sigma"), Number("gamma")), + "LogPearsonTypeIII" => new LogPearsonTypeIII(Number("mu"), Number("sigma"), Number("gamma")) { Base = Number("base") }, + "LogNormal" => new LogNormal(Number("mu"), Number("sigma")) { Base = Number("base") }, + "LnNormal" => new LnNormal(Number("mu"), Number("sigma")), + _ => throw new InvalidOperationException("Unknown oracle distribution.") + }; + var uncertainty = (IStandardError)distribution; + double probability = Number("probability"), expectedQuantile = Number("quantile"); + Assert.AreEqual(expectedQuantile, distribution.InverseCDF(probability), 3E-11 * Math.Max(1, Math.Abs(expectedQuantile))); + double[] gradient = uncertainty.QuantileGradient(probability); + string[] names = { "gradient_mu", "gradient_sigma", "gradient_gamma" }; + for (int i = 0; i < gradient.Length; i++) + Assert.AreEqual(Number(names[i]), gradient[i], 3E-6 * Math.Max(1, Math.Abs(gradient[i])), $"{row["family"]} g={row["gamma"]} p={probability}, gradient {i}"); + double[,] covariance = uncertainty.ParameterCovariance(100, ParameterEstimationMethod.MaximumLikelihood); + for (int i = 0; i < gradient.Length; i++) + for (int j = i; j < gradient.Length; j++) + Assert.AreEqual(Number($"cov{i + 1}{j + 1}"), covariance[i, j], 2E-10 * Math.Max(1, Math.Abs(covariance[i, j]))); + double expectedVariance = Number("variance_mle"); + Assert.AreEqual(expectedVariance, uncertainty.QuantileVariance(probability, 100, ParameterEstimationMethod.MaximumLikelihood), 3E-6 * Math.Max(1, expectedVariance)); + count++; + } + Assert.AreEqual(50, count); + } + + /// Confidence interval input validation runs before simulation or quantile calculation. + [TestMethod] + public void ConfidenceIntervals_RejectInvalidProbabilitiesAndSampleSizes() + { + var normal = new Normal(); + foreach (double probability in new[] { 0d, 1d, double.NaN }) + { + Assert.Throws(() => normal.NormalConfidenceIntervals(10, new[] { probability }, new[] { 0.5d })); + Assert.Throws(() => normal.NoncentralTConfidenceIntervals(10, new[] { 0.5d }, new[] { probability })); + Assert.Throws(() => normal.MonteCarloConfidenceIntervals(10, 1, new[] { probability }, new[] { 0.5d })); + Assert.Throws(() => new LogNormal().MonteCarloConfidenceIntervals(10, 1, new[] { 0.5d }, new[] { probability })); + Assert.Throws(() => normal.ExpectedProbability(10, probability)); + } + Assert.Throws(() => normal.NormalConfidenceIntervals(0, new[] { 0.5d }, new[] { 0.5d })); + Assert.Throws(() => normal.NoncentralTConfidenceIntervals(1, new[] { 0.5d }, new[] { 0.5d })); + Assert.Throws(() => normal.MonteCarloConfidenceIntervals(10, 0, new[] { 0.5d }, new[] { 0.5d })); + Assert.Throws(() => new LogNormal().MonteCarloConfidenceIntervals(1, 1, new[] { 0.5d }, new[] { 0.5d })); + } + + /// Finite covariance scales are retained when squaring the original scale would overflow. + [TestMethod] + public void LargeScaleCovariance_DividesBeforeSquaring() + { + Assert.AreEqual(1E308, new Normal(0, 1E155).ParameterCovariance(100, ParameterEstimationMethod.MaximumLikelihood)[0, 0], 3E292); + Assert.AreEqual(3E306, new Logistic(0, 1E155).ParameterCovariance(10000, ParameterEstimationMethod.MaximumLikelihood)[0, 0], 1E291); + } + + /// Large LP3 standardized moments retain finite ratios and classify genuine overflow. + [TestMethod] + public void LogPearson_LargeShapeMoments_NormalizeBeforeExponentiating() + { + var wide = new LogPearsonTypeIII(0, 16, 0) { Base = Math.E }; + Assert.AreEqual(5.8759900382892355E166, wide.Skewness, 2E153); + Assert.AreEqual(double.PositiveInfinity, wide.Kurtosis); + var moderate = new LogPearsonTypeIII(0, 12, 0) { Base = Math.E }; + Assert.AreEqual(1.4243659274306933E250, moderate.Kurtosis, 5E236); + // Frozen gamma-MGF calculations in normal-pearson.R cover both nonzero skew signs. + Assert.AreEqual(2.6371094491313571E162, new LogPearsonTypeIII(0, 16, -0.001) { Base = Math.E }.Skewness, 6E151); + Assert.AreEqual(2.0889174692524114E171, new LogPearsonTypeIII(0, 16, 0.001) { Base = Math.E }.Skewness, 5E160); + Assert.AreEqual(1.8770290525950149E244, new LogPearsonTypeIII(0, 12, -0.001) { Base = Math.E }.Kurtosis, 4E233); + Assert.AreEqual(1.9321207191996086E256, new LogPearsonTypeIII(0, 12, 0.001) { Base = Math.E }.Kurtosis, 4E245); + } + + /// Lognormal covariance and quantile variance do not square an unscaled large factor. + [TestMethod] + public void LogNormal_Uncertainty_PreservesFiniteScaledSquares() + { + foreach (ParameterEstimationMethod method in new[] { ParameterEstimationMethod.MethodOfMoments, ParameterEstimationMethod.MaximumLikelihood }) + { + Assert.AreEqual(1E308, new LogNormal(0, 1E155).ParameterCovariance(100, method)[0, 0], 3E292); + var distribution = new LogNormal(370, 1E-10) { Base = Math.E }; + Assert.AreEqual(2.38735282838454E299, distribution.QuantileVariance(0.5, 100, method), 2E286); + } + } + + private static void CheckConstraints(Tuple constraints) + { + for (int i = 0; i < constraints.Item1.Length; i++) + { + Assert.IsTrue(IsFinite(constraints.Item1[i]) && IsFinite(constraints.Item2[i]) && IsFinite(constraints.Item3[i])); + Assert.IsGreaterThan(constraints.Item2[i], constraints.Item1[i]); + Assert.IsLessThan(constraints.Item3[i], constraints.Item1[i]); + } + } + + private static bool IsFinite(double value) => !double.IsNaN(value) && !double.IsInfinity(value); + + private static void CheckGradient(UnivariateDistributionBase distribution, double probability, double tolerance) + { + var uncertainty = (IStandardError)distribution; + double[] gradient = uncertainty.QuantileGradient(probability); + double[] parameters = distribution.GetParameters; + for (int i = 0; i < parameters.Length; i++) + { + double step = Math.Max(1, Math.Abs(parameters[i])) * 1E-5; + var plus = distribution.Clone(); + var minus = distribution.Clone(); + double[] upper = (double[])parameters.Clone(); + double[] lower = (double[])parameters.Clone(); + upper[i] += step; + lower[i] -= step; + plus.SetParameters(upper); + minus.SetParameters(lower); + double difference = (plus.InverseCDF(probability) - minus.InverseCDF(probability)) / (2 * step); + Assert.AreEqual(difference, gradient[i], tolerance * Math.Max(1, Math.Abs(difference)), $"{distribution.DisplayName}, parameter {i}"); + } + } + + private static void CheckCovarianceAndVariance(UnivariateDistributionBase distribution, ParameterEstimationMethod method, double probability) + { + var uncertainty = (IStandardError)distribution; + double[] gradient = uncertainty.QuantileGradient(probability); + double[,] covariance = uncertainty.ParameterCovariance(100, method); + double expected = 0; + for (int i = 0; i < gradient.Length; i++) + { + Assert.IsTrue(IsFinite(covariance[i, i]) && covariance[i, i] > 0); + for (int j = 0; j < gradient.Length; j++) + { + Assert.AreEqual(covariance[i, j], covariance[j, i], 1E-14); + expected += gradient[i] * covariance[i, j] * gradient[j]; + } + } + Assert.IsGreaterThan(0d, expected); + Assert.AreEqual(expected, uncertainty.QuantileVariance(probability, 100, method), 2E-12 * Math.Max(1, expected)); + double[] probabilities = gradient.Length == 2 ? new[] { 0.2, 0.8 } : new[] { 0.2, 0.5, 0.8 }; + double[,] jacobian = uncertainty.QuantileJacobian(probabilities, out double determinant); + Assert.IsTrue(IsFinite(determinant)); + for (int row = 0; row < probabilities.Length; row++) + { + double[] rowGradient = uncertainty.QuantileGradient(probabilities[row]); + for (int column = 0; column < gradient.Length; column++) + Assert.AreEqual(rowGradient[column], jacobian[row, column], 2E-12 * Math.Max(1, Math.Abs(rowGradient[column]))); + } + } + } +} diff --git a/Test_Numerics/Distributions/Univariate/Test_Weibull.cs b/Test_Numerics/Distributions/Univariate/Test_Weibull.cs index 9289818c..3f477177 100644 --- a/Test_Numerics/Distributions/Univariate/Test_Weibull.cs +++ b/Test_Numerics/Distributions/Univariate/Test_Weibull.cs @@ -84,10 +84,9 @@ public void Test_Weibull_StandardError() { // Maximum Likelihood - var GUM = new Gumbel(8049.6d, 4478.6d); - double qVar99 = Math.Sqrt(GUM.QuantileVariance(0.99d, 53, ParameterEstimationMethod.MaximumLikelihood)); - double true_qVar99 = 2486.5d; - Assert.IsLessThan(0.01d, (qVar99 - true_qVar99) / true_qVar99); + var distribution = new Weibull(2, 2); + double standardError = Math.Sqrt(distribution.QuantileVariance(.5, 100, ParameterEstimationMethod.MaximumLikelihood)); + Assert.AreEqual(.09775810184419749, standardError, 1E-14); } /// diff --git a/Test_Numerics/Test_Numerics.csproj b/Test_Numerics/Test_Numerics.csproj index 723e0131..e320c51e 100644 --- a/Test_Numerics/Test_Numerics.csproj +++ b/Test_Numerics/Test_Numerics.csproj @@ -35,6 +35,10 @@ + + + + diff --git a/docs/distributions/bestfit-robustness-evidence.md b/docs/distributions/bestfit-robustness-evidence.md new file mode 100644 index 00000000..d90e0f31 --- /dev/null +++ b/docs/distributions/bestfit-robustness-evidence.md @@ -0,0 +1,124 @@ +# Distribution robustness and uncertainty evidence + +This record accompanies the [approved repair plan](bestfit-robustness-plan.md). The implementation starts from Numerics `c0d67b9` and BestFit `b732703`, in isolated `codex/distribution-robustness` worktrees. The original checkouts, including unrelated BestFit changes, are preserved. No package release, push, or BestFit Verification execution is part of this work. + +## Probability and likelihood contracts + +The 15 BestFit families now provide direct logarithmic lower and upper tails. Normal tails use a continued-fraction Mills ratio in the remote tail. Gamma tails use direct lower series, upper continued fractions, a small-shape complementary series and a large-shape transition expansion; actual gamma quantiles use those tails and implicit shape differentiation. Named approximate frequency-factor methods remain separate APIs. GEV, GPA, GLO and GNO evaluate the actual nonzero shape with continuous divided differences, retaining its finite support endpoint. + +`LogPDF` preserves mathematical endpoint limits, including positive infinity. The established aggregate sample log-likelihood convention still maps nonfinite totals to negative infinity. Zero censoring counts contribute zero. Interval likelihood means `(lower, upper]`, uses the better-conditioned difference of logarithmic tails, and handles mixture atoms explicitly. A collapsed continuous interval has a density-quadrature fallback, limited to explicitly recognized continuous families. + +Independent CompetingRisks combines logarithmic survival/CDF/density terms and resolves endpoint product limits. Its minimum support is the minimum of component lower endpoints through the minimum of upper endpoints; maximum support uses the corresponding maxima. Dependent probability-combination rules are retained, with bounded numerical density differentiation and explicit failure for unresolved derivatives. Ordinary mixture moments combine component central moments about a scaled mixture center. Hurdle conditioning uses logarithmic positive mass, with the zero atom included explicitly in moments and intervals. Mutable component, weight, base, transform and dependence settings invalidate derived caches. + +Representative regressions include: + +| Case | Independent expected result | +|---|---:| +| Standard Normal `LogCDF(-40)` | -804.6084420137538 | +| Standard Normal log probability of `(9,10]` | -43.628216632280818 | +| Standard Normal log probability of `(0,1e-20]` | -46.970640393085588 | +| Weibull(scale 2, shape 2) median gradient | [0.832554611157698, 0.152571011039570] | +| GNO(0,1,2) mean | -3.1945280494653252 | +| Mixture of identical Normal(1e10,1) components, SD | 1 | +| Weights .9/.1 at Normal means -1e308/+1e308, SD | 6e307 | +| Minimum of two unit exponentials, `LogPDF(1000)` | -1999.3068528194401 | +| Maximum of two Gamma(scale 1, shape .5), density at zero | 4/pi | + +BestFit's aggregate and pointwise univariate/point-process quantile priors consume the additive `IStandardError.LogAbsQuantileJacobian` extension. The existing interface and `QuantileJacobian` signatures are unchanged. Scaled elimination sums log pivots; an exact binary-rational fallback distinguishes an unresolved small pivot from true singularity without installing an artificial pivot. Repeated probabilities produce negative-infinite log absolute determinant. Finite log determinants remain available when the raw determinant overflows or underflows. + +BestFit mixture EM keeps exact, censored, interval, positive-conditional and measurement-error observation calculations logarithmic through responsibility normalization. Component log observations are centered before adding log weights, so identical components retain weights .4/.6 even at a common log density near -5e199. Existing GL20 measurement-error nodes, domain and retained-mass normalization are preserved. Failed automatic initialization is reported through model validation while retaining editable parameter state; it does not fabricate observations or prior bounds. + +## Public parameter coordinates and uncertainty + +Every covariance row/column and quantile-gradient entry follows the public parameter order below. `n` is the number of independent observations; it is not a posterior draw count. Probabilities supplied to uncertainty methods must be finite and strictly between zero and one, and `n` must be positive. + +| Family | Public coordinates | Implemented uncertainty | +|---|---|---| +| Exponential | Xi, Alpha (location, scale) | Existing MoM; exact covariance of implemented minimum/mean-minus-minimum MLE | +| GammaDistribution | Theta, Kappa (scale, shape) | Existing MoM and MLE, with actual-quantile gradients | +| GEV | Xi, Alpha, Kappa | Full three-parameter local MLE, Kappa < 1/2 | +| GLO | Xi, Alpha, Kappa | New local MLE, abs(Kappa) < 1/2 | +| GNO | Xi, Alpha, Kappa | New closed-form local MLE for finite shape and positive finite scale | +| GPA | Xi, Alpha, Kappa | Existing MLE for Kappa < 1/2; MoM for Kappa > -1/4; location covariance also requires n + 2*Kappa > 0 | +| Gumbel | Xi, Alpha | Existing MLE covariance and delta method | +| KappaFour | Xi, Alpha, Kappa, Hondo | New local MLE for Kappa < 1/2, Hondo < 1/2 and Kappa*Hondo < 1/2 | +| LnNormal | Mean, StandardDeviation in observation space | Existing indirect log-moment/MLE estimators, transformed into physical coordinates | +| Logistic | Xi, Alpha | Existing MoM/MLE covariance and delta method | +| LogNormal | Mu, Sigma of log-base observations | Existing indirect log-moment/MLE estimators; Base is fixed, not an estimated coordinate | +| LogPearsonTypeIII | Mu, Sigma, Gamma of log-base observations | Existing MoM and regular full-family MLE; fixed Base | +| Normal | Mu, Sigma | Existing MoM/MLE leading asymptotic covariance | +| PearsonTypeIII | Mu, Sigma, Gamma of observations | Existing MoM and regular full-family MLE | +| Weibull | Lambda, Kappa (scale, shape) | Existing MLE covariance with repaired actual-quantile gradient | + +Pearson and Log-Pearson regular MLE covariance requires `abs(Gamma) < sqrt(2)`, equivalent to shifted-gamma shape greater than two. The smooth zero-skew covariance is `diag(Sigma^2, Sigma^2/2, 6)/n`, retaining skew-estimation uncertainty. GPA's scalar quantile variance continues to omit its order-`n^-2` location term, as documented by the existing method. These uncertainty domains do not restrict distribution validity or fitting bounds. + +For Exponential MLE the covariance is `Alpha^2 * diag(1/n^2, (n-1)/n^2)`. For LnNormal, source inspection resolved an ambiguity in the initial review: `Estimate(MethodOfMoments)` fits moments of the log observations. A direct physical-moment estimator would have a different covariance and is not substituted under that enum. At physical mean/SD `(1,1)`, median variance for the existing indirect estimator and `n=100` is .0034657359027997265; the initially proposed .00875 belongs to a different estimator. Supplied physical LnNormal parameters are preserved exactly through serialization and cloning until log-coordinate setters change them. + +GNO uses the approved inverse expected information with `v=k^2`, `c=-expm1(-v/2)/v`, `R=((1+v)*exp(v)-1-2*v)/v^2` and `S=diag(Alpha,Alpha,1)`: + +``` +Cov = S * C * S / n +C = [[1+c*c/R, -k, c/R], + [-k, v+1/2, k/2], + [c/R, k/2, v/2+1/R]] +``` + +At zero shape, `c=1/2`, `R=3/2`, and `C=[[7/6,0,1/3],[0,1/2,0],[1/3,0,2/3]]`. Series and logarithmic scale restoration avoid cancellation and intermediate overflow. Its scalar delta variance is evaluated through an exact sum of nonnegative squares, preserving finite uncertainty after endpoint-gradient cancellation. Other scalar delta methods similarly contract in normalized coordinates before restoring physical scale. This does not regularize a covariance or replace the estimator. + +GLO, Kappa and GEV integrate analytical fixed-observation score products over both complete probability tails. GLO fixes Hondo=-1 and inverts the three-parameter principal **information** block; GEV fixes Hondo=0. The implementation checks relative error `1e-10`, absolute error `1e-12`, score means, positive-definite information, finite solves and the inverse residual. It adds no jitter, clipping or pseudoinverse. Integration/solve failures throw `InvalidOperationException`; mathematical regularity violations throw `ArgumentOutOfRangeException`. These matrices describe local asymptotic uncertainty, not global MLE existence, optimization convergence or finite-sample coverage. + +## Moments, support and initialization + +GNO uses analytical transformed-normal moments. GLO stabilizes its existing reciprocal-sinc formulas. GEV and GPA require `k > -1/r` for moment order `r`; GLO requires `r*abs(k) < 1`. Log-Pearson guards each required gamma moment-generating-function value before evaluating its central moments. Lognormal standardized moments depend only on shape and fixed base. Weibull uses log-Gamma moments and the exact centered-power relation to GEV to preserve very small and very large shape limits. Ordinary mixture moments avoid subtraction of large raw moments, including when the component locations themselves span nearly the full floating-point range. + +The inherited `CentralMoments(int)` remains an approximate bin calculation, now accumulating central powers in scaled coordinates. `CentralMoments(double)` retains its documented numerical approximation. These overloads are distinct from analytical scalar properties. Competing and positive-truncated mixture moments use checked full-support central integration where general component formulas are unavailable; they do not silently renormalize estimated mass or clip divergent moments. + +Initialization validates the supplied sample and candidate vector, including finite values, required positivity, minimum length and nonconstant data. Signed location bounds handle negative and zero-centered samples; positive scale bounds are finite and ordered with feasible starts. Existing fitting algorithms, estimator enums, random seeds, convergence settings, public parameter coordinates, log bases, serialization conventions and existing minimum-scale policies are retained. + +Corrected legacy expectations include GEV/GPA moment existence, median/mode values, the GLO shape-one endpoint mode, and the Weibull standard-error test that instantiated Gumbel. Correcting a Kappa endpoint allowed an old optimizer fixture to converge under unchanged settings; its requirement that that particular sample exhaust iterations was replaced by a deterministic invalid-data rejection/unchanged-state regression. Endpoint evaluation, not optimizer tuning, changed that trajectory. + +## Independent oracles and reproducibility + +The new oracle regression tests embed CSV values and do not load R, Python, a package service or a network source. Companion generators and provenance are in [oracles](oracles). R 4.4.3 `stats` supplies ordinary-family tails/quantiles; standalone defining formulas cover transformations and R underflow limitations. Python 3.12 Decimal/Fraction scripts use exact input doubles and up to 400-digit arithmetic for adjacent Kappa endpoints, scalar variance identities and gamma expansion coefficients. + +| Evidence | Coverage | +|---|---| +| [extreme-positive.csv](oracles/extreme-positive.csv), [.R](oracles/extreme-positive.R), [notes](oracles/extreme-positive.md) | 2,497 values for six families; 49 checked actual gamma derivatives, log tails, covariance, moments, scale extremes and existence boundaries | +| [generalized-fisher.csv](oracles/generalized-fisher.csv), [.R](oracles/generalized-fisher.R), [notes](oracles/generalized-fisher.md) | 1,333 rows, including 46 information/covariance cases and 88 moment values; score means, alternative tail transformations and independent fixed-observation score checks | +| [generalized-adjacent-boundaries.csv](oracles/generalized-adjacent-boundaries.csv), [.py](oracles/generalized-adjacent-boundaries.py) | First through fourth interior doubles at 45 finite Kappa endpoints, 180 rows | +| [generalized-scalar-variance.py](oracles/generalized-scalar-variance.py) | High-precision scalar contractions and the exact GNO positive quadratic at extreme scales/shapes | +| [generalized-affine-overflow.R](oracles/generalized-affine-overflow.R), [notes](oracles/generalized-affine-overflow.md) | Both GNO/GLO shape signs with finite log tails after the standardized observation overflows | +| [normal-pearson.csv](oracles/normal-pearson.csv), [.R](oracles/normal-pearson.R), [notes](oracles/normal-pearson-evidence.txt) | Reflected/transformed tails, covariance coordinates and standardized-moment/uncertainty scale regressions | +| [generate-gamma-temme-coefficients.py](oracles/generate-gamma-temme-coefficients.py) | Exact rational derivation of the gamma transition-expansion coefficients | + +Each oracle records its mapping, precision/tolerance and limitations. The [manifest](oracles/manifest.json) records row counts and checksums with portable LF normalization. GNO is Hosking's transformed normal, not SciPy `gennorm`; GLO is Hosking's generalized logistic, not SciPy `genlogistic`. The R Weibull routines can internally underflow a positive power before computing a logarithm; selected such rows explicitly use the independent defining log formula. Five gamma derivative cases whose reference unit quantile underflows are identified as unresolved, not converted into zero expected derivatives. Published rounded Gumbel/Weibull covariance coefficients retain their documented precision. + +Primary references: [R normal log-tail interface](https://stat.ethz.ch/R-manual/R-devel/library/stats/html/Normal.html), [R gamma interface](https://stat.ethz.ch/R-manual/R-devel/library/stats/html/GammaDist.html), [Hosking-compatible lmom definitions](https://raw.githubusercontent.com/cran/lmom/master/R/lmom.r), [DLMF gamma asymptotic expansions](https://dlmf.nist.gov/8.12), and [Hosking and Wallis GPA estimation](https://stat.cmu.edu/technometrics/80-89/VOL-29-03/v2903339.pdf). + +## Retained limitations + +- New GNO/GLO/Kappa covariance is MLE only. L-moment and product-moment covariance still require estimator-specific derivations. Other previously unsupported combinations remain unsupported; composites do not gain generic component-estimator covariance APIs. +- A mathematically regular covariance can remain numerically unresolved near a regularity boundary or outside double range. Checked Fisher integration reports failure instead of modifying the information. Scalar variance can sometimes remain representable even when a full physical covariance is not. +- Dependent CompetingRisks retains its existing ordinary-probability dependence backend; logarithmic combinations are direct for the independent case. This change does not introduce a different dependence model or an arbitrary-precision multivariate tail calculation. +- Subtracting two component log values cannot recover corrections already lost by rounding those values. For example, a positive-conditioned Normal with mean -1e100 and scale 1 requires a specialized conditional-ratio calculation to resolve its density around x=1e-100; generic double-valued log-tail subtraction is insufficient there. This extreme conditional-ratio domain remains a documented limitation. +- The tiny continuous-interval density fallback is a fixed quadrature rule, not a rigorous arbitrary-precision error enclosure. Generic moment quadrature can explicitly fail for unresolved or divergent integrals. Existing full uncertain-observation likelihood integration outside the repaired EM path retains its established arithmetic and quadrature policy. +- The separate, pre-existing `GammaDistribution.MLE_NR` method uses the instance `Kappa` rather than the newly solved shape when computing its scale. BestFit uses the main MLE; that alternate-estimator defect was recorded during review and left outside this repair inventory. + +## Validation and review + +Targeted red runs preceded repairs, including the final scale/cancellation cases. Independent code review covered family formulas, shared probability/derivative helpers, composite semantics and BestFit integration. The [machine-readable results](bestfit-robustness-validation.json) record final gate counts, report checksums and the matching Numerics dependency hash in all three BestFit test outputs. + +| Gate | Result | +|---|---| +| Numerics Release build, XML enforcement, net481/net8.0/net9.0/net10.0 | 0 warnings, 0 errors | +| Numerics complete Release tests, all four frameworks | 2,675/2,675 passed on each; 10,700 passes total, no failures or skips | +| BestFit core fast project, explicit Numerics worktree | 3,406/3,406 passed; strict documentation; one test worker | +| BestFit UI fast project, explicit Numerics worktree | 593/593 passed; strict documentation | +| BestFit App fast project, explicit Numerics worktree | 443/443 passed; strict documentation | +| BestFit Verification | Not executed; outside authorization | + +Numerics commands are `dotnet build -c Release -p:EnforceXmlDocumentation=true` and `dotnet test -c Release --no-build`. BestFit project commands set `UseLocalRmcNumerics=true` and `RmcNumericsProjectPath` to the intended isolated Numerics project's absolute path. The BestFit MSTest.Sdk 3.6.4 host uses Microsoft.Testing.Platform; targeted filters are application arguments after `--`, and every reported result count was checked in the test output/TRX. + +The initial complete gates exposed an unintended change from Normal's established decade bounds and shape-one subnormal quantile round-trips on .NET Framework. Both were corrected; the seeded RWMH test now reproduces every existing literal unchanged, and all four final framework runs pass. One initial live BOM download returned HTTP 500; all four final runs pass that unchanged network test. + +BestFit's unrelated `RunAsync_MultipleAnalyses_Parallel` stopwatch test passed in isolation but failed under default inter-test contention (369 ms and 673 ms against its unchanged 250 ms limit). The final core gate uses the optional `docs/validation/distribution-robustness.runsettings` in the paired BestFit worktree to schedule test cases with one worker. All 3,406 cases execute; the test still runs its three mock analyses concurrently and uses its original assertion. UI/App use their normal scheduling. No runner default, test threshold, application concurrency or production numerical setting was changed. The configuration mechanism is documented by [MSTest execution control](https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-mstest-writing-tests-controlling-execution). diff --git a/docs/distributions/bestfit-robustness-plan.md b/docs/distributions/bestfit-robustness-plan.md new file mode 100644 index 00000000..098b5f7b --- /dev/null +++ b/docs/distributions/bestfit-robustness-plan.md @@ -0,0 +1,75 @@ +# BestFit distribution robustness and uncertainty repair + +Approved implementation plan, September 8, 2026. + +## Baselines and authorization + +- Numerics: `c0d67b9c52d62dc49bdbfb744ec47b9a205cc86b`. +- BestFit: `b7327036ecd25770201597dc163945353370347e`. +- Haden explicitly approved implementation of the review and repair plan, including the stated formula, reference, support, derivative, moment, covariance and focused BestFit changes. Missing MLE uncertainty for GNO/GLO/KappaFour is included. Other estimator combinations remain unsupported. +- Work in isolated worktrees; preserve unrelated original-checkout changes. Do not push/publish. Preserve optimizer selection, convergence settings, seeds, dependence rules, public signatures and serialization coordinates. Additive log-Jacobian extension is approved. +- No BestFit Verification runs without separately named authorization. All numerical oracle regression tests belong in Numerics; BestFit fast tests cover integration contracts. + +## Review evidence and required behavior + +All 15 BestFit distributions plus CompetingRisks/Mixture were reviewed. Only KappaFour had direct lower/upper log tails. Baseline net8 build had zero warnings/errors and all 344 selected distribution tests passed despite reproduced defects. + +| Family | Approved repairs | +|---|---| +| Exponential | Stable tiny probabilities and log tails; covariance integer overflow and implemented-MLE estimator alignment; validation/constraints. | +| GammaDistribution | Shape-one endpoint; direct log gamma tails; exact-quantile derivatives; scale-stable moments; frequency-factor reflection/cap corrections. | +| GEV | Exact-zero shape/support, gradients/Fisher limits, correct moment existence, median/mode/endpoints, feasible constraints. | +| GLO | Exact-zero shape/support, gradients, stable existing analytical moments, mode/endpoints; MLE covariance. | +| GNO | Exact-zero shape/support, analytical moments and gradients, symmetric L-moment initializer, mode/endpoints; closed-form MLE covariance. | +| GPA | Exact-zero shape/support, gradients, moment existence, median/mode/endpoints, covariance domains and validation. | +| Gumbel | Direct log tails; affine and scale overflow avoidance; input/constraint validation. | +| KappaFour | Retain recent repairs; MLE uncertainty; singular Jacobians and first-through-fourth adjacent boundary regressions. | +| LnNormal | Public real-space mean/SD covariance and gradients; correct MoM variance; conversion and negative-mean validation. | +| Logistic | Stable log density/tails; signed/zero-center initialization bounds; uncertainty validation. | +| LogNormal | Base/moment conversion, mode, base validation, shape-only moments, actual-quantile gradient with complete covariance transformations. | +| LogPearsonTypeIII | Moment existence, reflected tails, endpoints, transformed mode, base preservation, correct uncertainty coordinates and exact quantile derivatives. | +| Normal | Stable normal log tails and log scale evaluation; uncertainty/constraints validation. | +| PearsonTypeIII | Reflected tails, endpoints, mode/constraints, full public-coordinate covariance and regularity, exact derivatives and full zero-skew limit. | +| Weibull | Direct log tails, actual-quantile derivatives, full-rank Jacobian, stable analytical moments and scale-homogeneous uncertainty; correct the test that constructed Gumbel. | +| CompetingRisks | No invented out-of-support density; independent log combinations, min/max support, supplied-vector validation, cache invalidation; audit dependent differentiation without changing copula rules. | +| Mixture | Inactive/singular components; conditional log tails/support/caches; central component-moment combination and checked positive-conditional integration. | + +Representative independent regressions: Normal log-CDF(-40)=-804.6084420137538; Normal interval (9,10] log probability=-43.628216632280818; Weibull(2,2) median gradient=[.832554611157698,.152571011039570]; GNO(0,1,2) mean=-3.1945280494653252; identical Normal(1e10,1) mixture SD=1; minimum of two unit exponentials log-PDF(1000)=-1999.3068528194401. Existing GEV/GPA moment/median/mode goldens and a Weibull test incorrectly targeting Gumbel require corresponding fixes. + +## Implementation and interfaces + +1. Freeze oracle fixtures before each repair and run their failing regressions. Record versions/mappings/precision/tolerances. Tests need no external numerical runtime/network. +2. Internal probability helpers: stable log complements/differences, normal log tails, gamma log P/Q and implicit gamma-quantile shape differentiation. Preserve represented support endpoints and true one-sided density limits. Exact nonzero shapes must not be flattened to zero. +3. Shared likelihood: stable differences of log tails for (lower,upper] intervals, including atoms; zero censoring count contributes zero. Keep aggregate infinite-log-density handling. +4. Existing uncertainty: all gradients/Jacobians/covariances use public parameter order/coordinates; exact quantile derivatives, strict finite interior probabilities, appropriate positive sample sizes, unchanged unsupported estimator behavior. GEV/GPA MLE k<.5; GPA MoM k>-.25 and its location-moment sample-size condition; Pearson regular MLE |gamma|-1/r. Guard LP3 moments and stabilize lognormal standardized moments. Ordinary mixtures combine central component moments; positive-conditional cases retain checked integration. Document generic approximate numerical CentralMoments separately. +7. Support/validation/initializers: validate candidate vectors, correct bounds and inactive components, refresh derived caches when dependencies change; reject invalid/degenerate data; preserve signed log coordinates, bases and serialization. +8. Add LogAbsQuantileJacobian extension without modifying IStandardError. Use scaled pivoting and log pivots; exact singularity=-Infinity without artificial pivots. Update BestFit aggregate/pointwise Univariate and PointProcess priors, and preserve logarithmic mixture EM observation/responsibility calculations. + +## Oracle definitions + +- R 4.4.3 stats: Normal(mu,sigma); logistic(xi,alpha); exponential(xi,scale alpha); gamma(shape k,scale theta); Weibull(shape k,scale lambda). +- LnNormal oracle uses meanlog/sdlog internal conversion from public physical moments. LogNormal uses meanlog=Mu*log(Base), sdlog=Sigma*log(Base). +- PIII a=4/gamma^2, signed beta=sigma*gamma/2, xi=mu-2*sigma/gamma; shifted/reflected gamma with direct complementary tails. LP3 is Base^PIII with density Jacobian 1/(x log(Base)). +- Hosking GEV uses repository shape k; scipy genpareto uses shape -k. GLO/GNO use Hosking definitions, not scipy genlogistic/gennorm. scipy kappa4 shape order is (h,k). +- Independent sources: R stats numerical functions; Hosking lmom source; 90+ digit defining formulas and independent fixed-observation log-density differences for Fisher matrices. Do not call production helpers to generate goldens. + +## Execution ledger + +- [x] Original baselines and dirty state recorded; isolated worktrees created. +- [x] Shared probability/uncertainty helpers and likelihood regressions. +- [x] Exponential/Gamma/GEV/GPA/Gumbel/Weibull repairs. +- [x] Normal/logistic/lognormal/Pearson repairs. +- [x] GLO/GNO/KappaFour repairs and new uncertainty. +- [x] CompetingRisks/Mixture repairs. +- [x] BestFit integration repairs. +- [x] Independent code review and corrective follow-up. +- [x] Full required Release/XML/four-framework Numerics checks and all BestFit fast projects. +- [x] Final evidence and reviewed changes prepared for scoped local commits; no push. + +## Completion gates + +Cover signs, zeros/near-zeros, adjacent boundaries, scale/translation extremes, invalid/sample/constraint cases, existence/regularity thresholds, independent actual-quantile derivatives, covariance and determinant properties, and new method numerical failure. Before commits run dotnet build -c Release and dotnet test -c Release --no-build on all supported targets. BestFit tests reference the intended Numerics worktree explicitly. New unapproved scientific changes require a decision; failures do not authorize adjusting optimizers, seeds, likelihood policy or goldens beyond this plan. + +Implementation details, parameter coordinates, evidence and retained limitations are recorded in [bestfit-robustness-evidence.md](bestfit-robustness-evidence.md). diff --git a/docs/distributions/bestfit-robustness-validation.json b/docs/distributions/bestfit-robustness-validation.json new file mode 100644 index 00000000..5d441da6 --- /dev/null +++ b/docs/distributions/bestfit-robustness-validation.json @@ -0,0 +1,94 @@ +{ + "date": "2026-09-08", + "numericsBaseline": "c0d67b9c52d62dc49bdbfb744ec47b9a205cc86b", + "bestFitBaseline": "b7327036ecd25770201597dc163945353370347e", + "buildCommand": "dotnet build -c Release -p:EnforceXmlDocumentation=true", + "buildWarnings": 0, + "buildErrors": 0, + "testCommand": "dotnet test -c Release --no-build", + "numerics": [ + { + "framework": "net10.0", + "total": 2675, + "executed": 2675, + "passed": 2675, + "failed": 0, + "report": "numerics-release-final_net10.0_20260908143914.trx", + "reportSha256": "6b974fde57453fdd12a009bc819d6a2fe8d25da3aef48bfe1f2c16241b580327" + }, + { + "framework": "net481", + "total": 2675, + "executed": 2675, + "passed": 2675, + "failed": 0, + "report": "numerics-release-final_net481_20260908143953.trx", + "reportSha256": "7e848030730ec27a1f7a96c544f03a6d30d1c2daa81d8ad624eb020612502156" + }, + { + "framework": "net8.0", + "total": 2675, + "executed": 2675, + "passed": 2675, + "failed": 0, + "report": "numerics-release-final_net8.0_20260908143926.trx", + "reportSha256": "5c57f5b134ab547f26f484f7d00b277cdb96469759054acdb8db5775d317c8e7" + }, + { + "framework": "net9.0", + "total": 2675, + "executed": 2675, + "passed": 2675, + "failed": 0, + "report": "numerics-release-final_net9.0_20260908143926.trx", + "reportSha256": "4e9e475590cb79157f52422a76de63a8c00b06a66236fdab9d8bebbed8347a53" + } + ], + "bestFitVerificationExecuted": false, + "bestFitFastGates": [ + { + "project": "RMC.BestFit.Tests", + "framework": "net10.0", + "total": 3406, + "executed": 3406, + "passed": 3406, + "failed": 0, + "warnings": 0, + "errors": 0, + "scheduling": "one test worker; internal test concurrency unchanged", + "report": "bestfit-core-release-closeout.trx", + "reportSha256": "ea086899b568765e8583de7ec25c7b67ec36f653774f6836001f22107d7fc251", + "numericsAssemblySha256": "f7cd36f91412d004b6865a27e1a51c28ed135a4082fef46244ea838a148e1ffc" + }, + { + "project": "RMC.BestFit.UI.Tests", + "framework": "net10.0-windows", + "total": 593, + "executed": 593, + "passed": 593, + "failed": 0, + "warnings": 0, + "errors": 0, + "scheduling": "project default", + "report": "bestfit-ui-release-closeout.trx", + "reportSha256": "aec8468f09152e6bc2b27f465aadaf8dd76e393666a5d360a704d517f67ffd3f", + "numericsAssemblySha256": "f7cd36f91412d004b6865a27e1a51c28ed135a4082fef46244ea838a148e1ffc" + }, + { + "project": "RMC.BestFit.App.Tests", + "framework": "net10.0-windows", + "total": 443, + "executed": 443, + "passed": 443, + "failed": 0, + "warnings": 0, + "errors": 0, + "scheduling": "project default", + "report": "bestfit-app-release-closeout.trx", + "reportSha256": "24f1a0b5830de31949d15982fcef03c07091837db07ca1480f4f068582965e58", + "numericsAssemblySha256": "f7cd36f91412d004b6865a27e1a51c28ed135a4082fef46244ea838a148e1ffc" + } + ], + "numericsProjectReference": "C:/GIT/numerics/artifacts/worktrees/distribution-robustness/Numerics/Numerics.csproj", + "xmlNamespaceScan": "Passed, 941 C# files; strict builds completed separately" +} diff --git a/docs/distributions/oracles/extreme-positive.R b/docs/distributions/oracles/extreme-positive.R new file mode 100644 index 00000000..d7bf7be6 --- /dev/null +++ b/docs/distributions/oracles/extreme-positive.R @@ -0,0 +1,296 @@ +# Offline, independent oracle generator. Requires only R 4.4.3 base/stats. +# Run from the repository root; never calls Numerics or changes production/tests. +options(digits=17, warn=1) +stopifnot(as.character(getRversion()) == "4.4.3") +args <- commandArgs(trailingOnly=TRUE) +out <- if (length(args)) args[1] else "docs/distributions/oracles/extreme-positive.csv" +rows <- list() +fmt <- function(x) if (is.na(x)) "NaN" else sprintf("%.17g", x) +add <- function(family, case, scale=1, shape=0, xi=0, x=NA_real_, p=NA_real_, + n=NA_real_, quantity, value, oracle, rel=2e-11, abs=0, + error=0, status="finite") { + if (is.na(value) && status == "finite") status <- "undefined" + if (is.infinite(value)) status <- "infinite" + rows[[length(rows)+1L]] <<- data.frame(family, case, xi=fmt(xi), scale=fmt(scale), + shape=fmt(shape), x=fmt(x), p=fmt(p), n=fmt(n), quantity, value=fmt(value), + absolute_tolerance=fmt(abs), relative_tolerance=fmt(rel), + estimated_absolute_error=fmt(error), oracle, status, stringsAsFactors=FALSE) +} +exprel <- function(t) { + ans <- expm1(t)/t + small <- abs(t)<1e-3 + z <- t[small] + ans[small] <- 1+z*(1/2+z*(1/6+z*(1/24+z*(1/120+z*(1/720+z/5040))))) + ans +} +exprel2 <- function(t) { + ans <- (expm1(t)-t)/(t*t) + small <- abs(t)<1e-3 + z <- t[small] + ans[small] <- 1/2+z*(1/6+z*(1/24+z*(1/120+z*(1/720+z*(1/5040+z/40320))))) + ans +} +log1mexp <- function(x) ifelse(x < -log(2), log1p(-exp(x)), log(-expm1(x))) +standard_q <- function(family, logp, k) { + z <- switch(family, GEV=-log(-logp), GPA=-log1mexp(logp), Gumbel=-log(-logp)) + if (family=="Gumbel") z else z*exprel(-k*z) +} +gradient <- function(family,p,a,k) { + if (family=="Exponential") return(c(1,-log1p(-p))) + if (family=="Gumbel") return(c(1,-log(-log(p)))) + if (family=="Weibull") { + lt <- log(-log1p(-p)); q <- a*exp(lt/k) + return(c(q/a,-q*lt/k^2)) + } + z <- if(family=="GEV") -log(-log(p)) else -log1p(-p) + # Independent derivative of z*(exp(-k*z)-1)/(-k*z). + c(1,z*exprel(-k*z),a*z*z*(exprel2(-k*z)-exprel(-k*z))) +} +# Sixth-order Richardson estimates using positive log-parameter perturbations. +# Error estimates are empirical convergence indicators, not rigorous bounds. +differentiate <- function(f) { + hs <- .04/2^(0:11) + d <- vapply(hs,function(h)(f(-2*h)-8*f(-h)+8*f(h)-f(2*h))/(12*h),0.0) + r <- (16*d[-1]-d[-length(d)])/15 + err <- abs(diff(r)) + good <- which(is.finite(err) & is.finite(r[-1])) + if (!length(good)) return(c(value=NA_real_,error=Inf)) + j <- good[which.min(err[good])]+1 + c(value=r[j],error=max(err[j-1],32*.Machine$double.eps*abs(r[j]))) +} +gamma_quantile <- function(p,k) if(p<=.5) qgamma(log(p),shape=k,log.p=TRUE) else qgamma(log1p(-p),shape=k,lower.tail=FALSE,log.p=TRUE) +gamma_shape_derivative <- function(p,k) { + q <- gamma_quantile(p,k) + if (!is.finite(q) || q==0) return(c(value=NA,error=NA,implicit=NA)) + d <- differentiate(function(u) log(gamma_quantile(p,k*exp(u)))) + value <- (q/k)*d["value"] + err <- abs(q/k)*d["error"] + lower <- p<=.5 + dp <- differentiate(function(u) pgamma(q,shape=k*exp(u),lower.tail=lower,log.p=TRUE)) + lp <- if(lower) log(p) else log1p(-p) + implicit <- (if(lower)-1 else 1)*exp(lp-dgamma(q,shape=k,log=TRUE))/k*dp["value"] + err <- max(err,abs(value-implicit)) + c(value=unname(value),error=unname(err),implicit=unname(implicit)) +} +# Native R stats probability and log-probability oracles in standardized units. +for (family in c("Exponential","Gamma","Weibull")) { + shapes <- switch(family,Exponential=0,Gamma=c(.001,.01,.1,.5,1,2,10,100,1e4),Weibull=c(.1,.5,1,2,10,100)) + for(k in shapes) for(a in c(1,1e-200,1e200)) { + zs <- if(family=="Gamma") c(0,1e-10,.1,1,max(1,k),1000) else c(0,1e-20,.1,1,10,1000) + for(z in unique(zs)) { + lp <- switch(family,Exponential=dexp(z,log=TRUE),Gamma=dgamma(z,shape=k,log=TRUE),Weibull=dweibull(z,shape=k,log=TRUE))-log(a) + lc <- switch(family,Exponential=pexp(z,log.p=TRUE),Gamma=pgamma(z,shape=k,log.p=TRUE),Weibull=pweibull(z,shape=k,log.p=TRUE)) + ls <- switch(family,Exponential=pexp(z,lower.tail=FALSE,log.p=TRUE),Gamma=pgamma(z,shape=k,lower.tail=FALSE,log.p=TRUE),Weibull=pweibull(z,shape=k,lower.tail=FALSE,log.p=TRUE)) + log_density_repaired <- FALSE; log_cdf_repaired <- FALSE + if(family=="Weibull" && z>0 && is.finite(z)) { + log_power <- k*log(z) + defining_lp <- log(k)+(k-1)*log(z)-exp(log_power)-log(a) + if(is.infinite(lp) && lp<0 && is.finite(defining_lp)) {lp<-defining_lp;log_density_repaired<-TRUE} + # When z^k underflows, log(1-exp(-z^k)) equals k*log(z) to binary64 precision. + if(is.infinite(lc) && lc<0 && is.finite(log_power) && log_power<0) {lc<-log_power;log_cdf_repaired<-TRUE} + } + for(nm in c("LogPDF","LogCDF","LogCCDF")) add(family,"R-standardized-evaluation",a,k,x=a*z,quantity=nm,value=switch(nm,LogPDF=lp,LogCDF=lc,LogCCDF=ls),oracle=if((nm=="LogPDF" && log_density_repaired)||(nm=="LogCDF" && log_cdf_repaired))"Weibull-defining-log-formula-R-underflow" else "R-stats",abs=if(nm=="LogPDF")2e-11 else 0) + } + } +} +for(family in c("Exponential","Gamma","Weibull")) { + shapes <- switch(family,Exponential=0,Gamma=c(.001,.01,.1,.5,1,2,10,100,1e4),Weibull=c(.1,.5,1,2,10,100)) + for(k in shapes) for(p in c(1e-12,1e-6,.01,.5,.99,1-1e-12)) { + q <- switch(family,Exponential=qexp(p),Gamma=gamma_quantile(p,k),Weibull=qweibull(p,shape=k)) + add(family,"R-quantile",shape=k,p=p,quantity="InverseCDF",value=q,oracle="R-stats",status=if(q==0)"underflowed" else "finite") + if(family=="Gamma") { + d <- gamma_shape_derivative(p,k) + add(family,"exact-quantile-gradient",shape=k,p=p,quantity="GradientScale",value=q,oracle="R-qgamma-scale-identity",status=if(q==0)"underflowed" else "finite") + # Do not freeze a zero shape derivative when R qgamma has underflowed. + add(family,"exact-quantile-gradient",shape=k,p=p,quantity="GradientShape",value=d["value"],oracle="R-qgamma-log-shape-Richardson-crosschecked-pgamma",rel=5e-8,error=d["error"],status=if(q==0)"unresolved-quantile-underflow" else "finite") + if(q>0 && !(is.finite(d["value"]) && d["value"]>0 && d["error"]<=5e-8*abs(d["value"]))) stop(sprintf("Gamma derivative unresolved k=%g p=%.17g value=%.17g error=%.17g implicit=%.17g",k,p,d["value"],d["error"],d["implicit"])) + } else { + g <- gradient(family,p,1,k) + for(j in seq_along(g)) add(family,"analytic-quantile-gradient",shape=k,p=p,quantity=paste0("Gradient",j),value=g[j],oracle="defining-quantile-derivative") + } + } +} +# GEV/GPA use Hosking's kappa convention; SciPy mappings: genextreme(c=k), genpareto(c=-k). +for(family in c("GEV","GPA","Gumbel")) { + ks <- if(family=="Gumbel")0 else c(-1,-.5,-.2,-1e-6,0,1e-6,.2,.5,1,2) + for(k in ks) { + for(p in c(1e-12,.01,.5,.99,1-1e-12)) { + q <- standard_q(family,log(p),k) + add(family,"defining-quantile",shape=k,p=p,quantity="InverseCDF",value=q,oracle="Hosking-quantile-expm1") + g <- gradient(family,p,1,k) + for(j in seq_along(g)) add(family,"analytic-quantile-gradient",shape=k,p=p,quantity=paste0("Gradient",j),value=g[j],oracle="defining-quantile-derivative") + } + lo <- if(family=="GPA")0 else if(k<0)1/k else -Inf + hi <- if(k>0)1/k else Inf + for(nm in c("Minimum","Maximum")) add(family,"support",shape=k,quantity=nm,value=if(nm=="Minimum")lo else hi,oracle="mathematical-support") + xs <- unique(c(lo,hi,if(family=="Gumbel")c(-10,0,10) else c(0,.1))) + for(x in xs) { + if(xhi) {lp <- -Inf; lc <- if(x36)-y else log1mexp(lc)} + for(nm in c("LogPDF","LogCDF","LogCCDF"))add(family,"overflowing-tail-transform",a,k,xi=xi,x=x,quantity=nm,value=switch(nm,LogPDF=lp,LogCDF=lc,LogCCDF=ls),oracle="R-defining-log-Hosking-physical-coordinates") + } +} +# Existing rounded Weibull MLE coefficients, contracted analytically before +# the coordinate magnitudes (lambda=kappa=1e100) can hide finite contributions. +log_t<-log(log(2)) +add("Weibull","MLE-mismatched-range-variance",scale=1e100,shape=1e100,p=.5,n=100, + quantity="QuantileVariance",value=(1.108665-2*.257022*log_t+.607927*log_t^2)/100, + oracle="R-analytical-Weibull-variance-retained-coefficients") +# At kappa >= 1e16, integrate the scaled Fisher-residual kernel in u=kappa*t. +# The first omitted integrated Bernoulli term has magnitude 1/(30*kappa^3). +for(test in list(c(a=1,k=1e16,n=100),c(a=1e150,k=1e16,n=100), + c(a=1e-150,k=1e16,n=100),c(a=1,k=1e155,n=2e9))) { + a<-test["a"];k<-test["k"];n<-test["n"] + residual<-integrate(function(u)exp(-u)*(u/2+u*u/(12*k)),0,Inf,rel.tol=1e-13,abs.tol=1e-14)$value + vals<-c(exp(2*log(a)-log(n)+log(1/residual+1/k)), + -exp(log(a)+log(k)-log(n)-log(residual)), + exp(2*log(k)-log(n)-log(residual))) + for(j in 1:3)add("Gamma","MLE-large-shape-Fisher-residual",a,k,n=n,quantity=c("Covariance11","Covariance12","Covariance22")[j],value=vals[j],oracle="R-integrate-scaled-Fisher-residual-kernel",rel=2e-10) +} +# At the median q=k-1/3+O(1/k), dq/dk=1+O(1/k^2). The defining +# positive Fisher/MoM factor therefore gives k/n with relative correction O(1/k). +for(method in c("MLE","MoM"))add("Gamma",paste0(method,"-large-shape-median-variance"),shape=1e16,p=.5,n=100, + quantity="QuantileVariance",value=1e16/100,oracle="R-defining-concentrated-Gamma-median-variance-limit",rel=2e-10) +add("Weibull","scale-rescued-median",scale=1e200,shape=.0004,p=.5, + quantity="Median",value=exp(log(1e200)+log(log(2))/.0004),oracle="R-defining-log-Weibull-median") +for(k in c(1e200,1e308)) { + r<-1/k + vals<-c((2*(r-1)/(r+3))*sqrt(k)*sqrt(r+2), + k*(3*(r+2)*(3*r*r-r+2)/((r+3)*(r+4)))) + for(j in 1:2)add("GPA","large-positive-shape-scaled-higher-moments",shape=k, + quantity=c("Skewness","Kurtosis")[j],value=vals[j],oracle="R-Hosking-rational-moments-in-reciprocal-shape") +} +for(family in c("GEV","GPA"))for(k in c(-1e-12,1e-12,-1e-8,1e-8,-1e-4,1e-4)) { + add(family,"nonzero-small-shape-support",shape=k,quantity="Minimum",value=if(family=="GPA")0 else if(k<0)1/k else -Inf,oracle="exact-nonzero-shape-support") + add(family,"nonzero-small-shape-support",shape=k,quantity="Maximum",value=if(k>0)1/k else Inf,oracle="exact-nonzero-shape-support") + for(p in c(.01,.5,.99)) { + add(family,"nonzero-small-shape-quantile",shape=k,p=p,quantity="InverseCDF",value=standard_q(family,log(p),k),oracle="Hosking-quantile-expm1") + g<-gradient(family,p,1,k) + for(j in 1:3)add(family,"nonzero-small-shape-gradient",shape=k,p=p,quantity=paste0("Gradient",j),value=g[j],oracle="defining-quantile-derivative") + } +} +# Full probability integration for existence-aware GEV/GPA centered moments. +integrate_pair <- function(fun) integrate(function(u){lt=8*log(u)-log(2);jac=4*u^7;(fun(lt)+fun(log1p(-exp(lt))))*jac},0,1,rel.tol=1e-10,abs.tol=1e-12,subdivisions=1000) +for(family in c("GEV","GPA")) for(k in c(-1,-.5,-.2,-1e-6,0,1e-6,.2,.5,1,2)) { + vals <- rep(NA_real_,4); errs <- rep(0,4) + if(k> -1) { + q <- function(lp)standard_q(family,lp,k) + r <- integrate_pair(q);vals[1]<-r$value;errs[1]<-r$abs.error + if(k> -.5) { + r<-integrate_pair(function(lp)(q(lp)-vals[1])^2);vals[2]<-sqrt(r$value);errs[2]<-r$abs.error/(2*vals[2]) + if(k> -1/3){r<-integrate_pair(function(lp)((q(lp)-vals[1])/vals[2])^3);vals[3]<-r$value;errs[3]<-r$abs.error} + if(k> -.25){r<-integrate_pair(function(lp)((q(lp)-vals[1])/vals[2])^4);vals[4]<-r$value;errs[4]<-r$abs.error} + } + } + for(j in 1:4)add(family,"full-probability-central-moments",shape=k,quantity=c("Mean","StandardDeviation","Skewness","Kurtosis")[j],value=vals[j],oracle="R-integrate-full-probability-Hosking-quantile",rel=2e-8,abs=2e-9,error=errs[j]) +} +# Large positive GEV shapes have finite standardized ratios even when raw gamma moments overflow. +for(k in c(50,100,200)) for(a in c(1,1e-200,1e200)) { + l<-lgamma(1+(1:4)*k) + lv<-l[2]+log1p(-exp(2*l[1]-l[2])) + vals<-c(-exp(log(a)+l[1]+log1p(-exp(-l[1]))-log(k)), + exp(log(a)+lv/2-log(k)), + -exp(l[3]+log1p(-3*exp(l[1]+l[2]-l[3])+2*exp(3*l[1]-l[3]))-1.5*lv), + exp(l[4]+log1p(-4*exp(l[1]+l[3]-l[4])+6*exp(2*l[1]+l[2]-l[4])-3*exp(4*l[1]-l[4]))-2*lv)) + for(j in 1:4)add("GEV","large-positive-shape-lgamma-moments",a,k,quantity=c("Mean","StandardDeviation","Skewness","Kurtosis")[j],value=vals[j],oracle="R-lgamma-normalized-analytical-moments",rel=2e-10) +} +# Weibull X=scale*T^(1/kappa), T unit exponential. Small kappa needs lgamma +# moments, while large kappa is integrated after centering and dividing by c=1/kappa. +for(k in c(.005,.01,.02)) for(a in c(1,1e-200,1e200)) { + c<-1/k;l<-lgamma(1+(1:4)*c);lv<-l[2]+log1p(-exp(2*l[1]-l[2])) + vals<-c(exp(log(a)+l[1]),exp(log(a)+lv/2), + exp(l[3]+log1p(-3*exp(l[1]+l[2]-l[3])+2*exp(3*l[1]-l[3]))-1.5*lv), + exp(l[4]+log1p(-4*exp(l[1]+l[3]-l[4])+6*exp(2*l[1]+l[2]-l[4])-3*exp(4*l[1]-l[4]))-2*lv)) + for(j in 1:4)add("Weibull","small-shape-lgamma-moments",a,k,quantity=c("Mean","StandardDeviation","Skewness","Kurtosis")[j],value=vals[j],oracle="R-lgamma-normalized-analytical-power-moments",rel=2e-10) +} +for(k in c(1e3,1e6,1e12,1e200)) { + c<-1/k;q<-function(lp)expm1(c*log(-lp))/c + m<-integrate_pair(q)$value;v<-integrate_pair(function(lp)(q(lp)-m)^2)$value + skew<-integrate_pair(function(lp)((q(lp)-m)/sqrt(v))^3)$value + kurt<-integrate_pair(function(lp)((q(lp)-m)/sqrt(v))^4)$value + for(a in c(1,1e-200,1e200)) { + vals<-c(exp(log(a)+lgamma(1+c)),exp(log(a)+log(c)+log(v)/2),skew,kurt) + for(j in 1:4)add("Weibull","large-shape-full-probability-moments",a,k,quantity=c("Mean","StandardDeviation","Skewness","Kurtosis")[j],value=vals[j],oracle="R-integrate-centered-scaled-exponential-power",rel=2e-8) + } +} +for(family in c("Exponential","Gamma","Gumbel","Weibull")) for(a in c(1,1e-200,1e200)) { + ks<-switch(family,Exponential=0,Gamma=c(.1,1,2,100),Gumbel=0,Weibull=c(.5,1,2,10)) + for(k in ks) { + vals <- switch(family,Exponential=c(a,a,2,9),Gamma=c(a*k,a*sqrt(k),2/sqrt(k),3+6/k),Gumbel=c(-digamma(1)*a,a*pi/sqrt(6),1.1395470994046487,5.4),Weibull={g<-gamma(1+(1:4)/k);v<-g[2]-g[1]^2;c(a*g[1],a*sqrt(v),(g[3]-3*g[1]*g[2]+2*g[1]^3)/v^1.5,(g[4]-4*g[1]*g[3]+6*g[1]^2*g[2]-3*g[1]^4)/v^2)}) + for(j in 1:4)add(family,"analytic-scale-separated-moments",a,k,quantity=c("Mean","StandardDeviation","Skewness","Kurtosis")[j],value=vals[j],oracle="R-gamma-and-analytical-moments",rel=if(family=="Weibull")2e-8 else 2e-11) + } +} +covrows <- function(family,k,C,method,a=1,n=100)for(i in 1:nrow(C))for(j in i:ncol(C))add(family,paste0(method,"-covariance"),a,k,n=n,quantity=paste0("Covariance",i,j),value=C[i,j],oracle=method,rel=2e-9,abs=1e-12) +for(n in c(2,10,100,50000))covrows("Exponential",0,diag(c(1/n^2,(n-1)/n^2)),"actual-MLE-order-statistics",n=n) +for(k in c(.001,.1,1,2,100,1e4)) { + t<-trigamma(k); d<-k*t-1 + covrows("Gamma",k,matrix(c(t,-1,-1,k),2)/(100*d),"MLE-R-trigamma") + J<-matrix(c(-1/k,2,1/k,-1),2);S<-matrix(c(k,2*k,2*k,2*k*(k+3)),2) + covrows("Gamma",k,J%*%S%*%t(J)/100,"MoM-independent-moment-Jacobian") +} +euler <- -digamma(1); B<-6/pi^2; A<-1+B*(1-euler)^2; C<-B*(1-euler) +covrows("Gumbel",0,matrix(c(A,C,C,B),2)/100,"MLE-exact-Euler-pi") +for(k in c(.5,1,2,10))covrows("Weibull",k,matrix(c(A/k^2,C,C,B*k^2),2)/100,"MLE-exact-Euler-pi") +for(k in c(-.2,0,.2,.49)) { + covrows("GPA",k,matrix(c(1/((100+2*k)*(100+k)^2)*100,0,0,0,2*(1-k)/100,(1-k)/100,0,(1-k)/100,(1-k)^2/100),3),"MLE-Hosking-Wallis") +} +for(k in c(-.2,0,.2,1,2)) { + den<-(1+2*k)*(1+3*k)*(1+4*k) + vscale<-2*(1+k)^2*(1+6*k+12*k^2)/(100*den) + vshape<-(1+k)^2*(1+2*k)^2*(1+k+6*k^2)/(100*den) + cross<-(1+k)^2*(1+2*k)*(1+4*k+12*k^2)/(100*den) + C<-matrix(c(100/((100+2*k)*(100+k)^2),0,0,0,vscale,cross,0,cross,vshape),3) + covrows("GPA",k,C,"MoM-Hosking-Wallis") +} +gev_scores <- function(lp,k) { + z<--log(-lp);r<-1+lp + A<-z*exprel(k*z);B<-z*z*exprel2(k*z) + cbind((r-k)*exp(k*z),-1+(r-k)*A,z+(k-r)*B) +} +for(k in c(-.2,-1e-6,0,1e-6,.2,.49)) { + I<-matrix(0,3,3) + for(i in 1:3)for(j in i:3){z<-integrate_pair(function(lp){s<-gev_scores(lp,k);s[,i]*s[,j]});I[i,j]<-I[j,i]<-z$value} + stopifnot(min(eigen(I,symmetric=TRUE,only.values=TRUE)$values)>0) + covrows("GEV",k,solve(I)/100,"MLE-independent-score-outer-product-integral") +} +for(family in c("GEV","GPA"))for(k in c(.5,1,2))add(family,"MLE-nonregular-domain",shape=k,n=100,quantity="CovarianceDefined",value=0,oracle="regular-Fisher-domain-kappa-less-than-half",status="outside-uncertainty-domain") +for(k in c(-1,-.3,-.25))add("GPA","MoM-missing-fourth-moment",shape=k,n=100,quantity="CovarianceDefined",value=0,oracle="Hosking-Wallis-fourth-moment-domain",status="outside-uncertainty-domain") +result <- do.call(rbind,rows) +write.table(result,out,sep=",",row.names=FALSE,col.names=TRUE,quote=TRUE,na="NaN",eol="\n") +cat(R.version.string,"\nRows:",nrow(result),"\n") +cat("Gamma finite derivative rows:",sum(result$family=="Gamma" & result$quantity=="GradientShape" & result$status=="finite"),"\n") +cat("Gamma unresolved underflow rows:",sum(result$status=="unresolved-quantile-underflow"),"\n") diff --git a/docs/distributions/oracles/extreme-positive.csv b/docs/distributions/oracles/extreme-positive.csv new file mode 100644 index 00000000..40238bcc --- /dev/null +++ b/docs/distributions/oracles/extreme-positive.csv @@ -0,0 +1,2498 @@ +"family","case","xi","scale","shape","x","p","n","quantity","value","absolute_tolerance","relative_tolerance","estimated_absolute_error","oracle","status" +"Exponential","R-standardized-evaluation","0","1","0","0","NaN","NaN","LogPDF","-0","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","1","0","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Exponential","R-standardized-evaluation","0","1","0","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","1","0","9.9999999999999995e-21","NaN","NaN","LogPDF","-9.9999999999999995e-21","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","1","0","9.9999999999999995e-21","NaN","NaN","LogCDF","-46.051701859880914","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","1","0","9.9999999999999995e-21","NaN","NaN","LogCCDF","-9.9999999999999995e-21","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","1","0","0.10000000000000001","NaN","NaN","LogPDF","-0.10000000000000001","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","1","0","0.10000000000000001","NaN","NaN","LogCDF","-2.3521684610440907","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","1","0","0.10000000000000001","NaN","NaN","LogCCDF","-0.10000000000000001","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","1","0","1","NaN","NaN","LogPDF","-1","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","1","0","1","NaN","NaN","LogCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","1","0","1","NaN","NaN","LogCCDF","-1","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","1","0","10","NaN","NaN","LogPDF","-10","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","1","0","10","NaN","NaN","LogCDF","-4.5400960370489214e-05","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","1","0","10","NaN","NaN","LogCCDF","-10","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","1","0","1000","NaN","NaN","LogPDF","-1000","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","1","0","1000","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","1","0","1000","NaN","NaN","LogCCDF","-1000","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999998e-201","0","0","NaN","NaN","LogPDF","460.51701859880916","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999998e-201","0","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Exponential","R-standardized-evaluation","0","9.9999999999999998e-201","0","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999998e-201","0","9.9999999999999999e-221","NaN","NaN","LogPDF","460.51701859880916","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999998e-201","0","9.9999999999999999e-221","NaN","NaN","LogCDF","-46.051701859880914","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999998e-201","0","9.9999999999999999e-221","NaN","NaN","LogCCDF","-9.9999999999999995e-21","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999998e-201","0","1.0000000000000001e-201","NaN","NaN","LogPDF","460.41701859880914","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999998e-201","0","1.0000000000000001e-201","NaN","NaN","LogCDF","-2.3521684610440907","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999998e-201","0","1.0000000000000001e-201","NaN","NaN","LogCCDF","-0.10000000000000001","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999998e-201","0","9.9999999999999998e-201","NaN","NaN","LogPDF","459.51701859880916","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999998e-201","0","9.9999999999999998e-201","NaN","NaN","LogCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999998e-201","0","9.9999999999999998e-201","NaN","NaN","LogCCDF","-1","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999998e-201","0","9.9999999999999998e-200","NaN","NaN","LogPDF","450.51701859880916","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999998e-201","0","9.9999999999999998e-200","NaN","NaN","LogCDF","-4.5400960370489214e-05","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999998e-201","0","9.9999999999999998e-200","NaN","NaN","LogCCDF","-10","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999998e-201","0","9.9999999999999999e-198","NaN","NaN","LogPDF","-539.48298140119084","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999998e-201","0","9.9999999999999999e-198","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999998e-201","0","9.9999999999999999e-198","NaN","NaN","LogCCDF","-1000","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999997e+199","0","0","NaN","NaN","LogPDF","-460.51701859880916","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999997e+199","0","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Exponential","R-standardized-evaluation","0","9.9999999999999997e+199","0","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999997e+199","0","9.9999999999999989e+179","NaN","NaN","LogPDF","-460.51701859880916","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999997e+199","0","9.9999999999999989e+179","NaN","NaN","LogCDF","-46.051701859880914","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999997e+199","0","9.9999999999999989e+179","NaN","NaN","LogCCDF","-9.9999999999999995e-21","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999997e+199","0","1.0000000000000001e+199","NaN","NaN","LogPDF","-460.61701859880918","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999997e+199","0","1.0000000000000001e+199","NaN","NaN","LogCDF","-2.3521684610440907","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999997e+199","0","1.0000000000000001e+199","NaN","NaN","LogCCDF","-0.10000000000000001","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999997e+199","0","9.9999999999999997e+199","NaN","NaN","LogPDF","-461.51701859880916","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999997e+199","0","9.9999999999999997e+199","NaN","NaN","LogCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999997e+199","0","9.9999999999999997e+199","NaN","NaN","LogCCDF","-1","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999997e+199","0","9.999999999999999e+200","NaN","NaN","LogPDF","-470.51701859880916","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999997e+199","0","9.999999999999999e+200","NaN","NaN","LogCDF","-4.5400960370489214e-05","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999997e+199","0","9.999999999999999e+200","NaN","NaN","LogCCDF","-10","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999997e+199","0","0.99999999999999999e+203","NaN","NaN","LogPDF","-1460.517018598809","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999997e+199","0","0.99999999999999999e+203","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-standardized-evaluation","0","9.9999999999999997e+199","0","0.99999999999999999e+203","NaN","NaN","LogCCDF","-1000","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.001","0","NaN","NaN","LogPDF","Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","1","0.001","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","1","0.001","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.001","1e-10","NaN","NaN","LogPDF","16.095646193526662","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.001","1e-10","NaN","NaN","LogCDF","-0.022449457331756989","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.001","1e-10","NaN","NaN","LogCCDF","-3.8076925670559727","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.001","0.10000000000000001","NaN","NaN","LogPDF","-4.706896377482801","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.001","0.10000000000000001","NaN","NaN","LogCDF","-0.001823652105188687","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.001","0.10000000000000001","NaN","NaN","LogCCDF","-6.3078258248075425","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.001","1","NaN","NaN","LogPDF","-7.9071788853838534","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.001","1","NaN","NaN","LogCDF","-0.0002196324750319099","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.001","1","NaN","NaN","LogCCDF","-8.4236647908310722","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.001","1000","NaN","NaN","LogPDF","-1013.808026409087","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.001","1000","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.001","1000","NaN","NaN","LogCCDF","-1013.8090239153952","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.001","0","NaN","NaN","LogPDF","Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.001","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.001","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.001","1e-210","NaN","NaN","LogPDF","476.61266479233581","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.001","1e-210","NaN","NaN","LogCDF","-0.022449457331756989","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.001","1e-210","NaN","NaN","LogCCDF","-3.8076925670559727","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.001","1.0000000000000001e-201","NaN","NaN","LogPDF","455.81012222132637","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.001","1.0000000000000001e-201","NaN","NaN","LogCDF","-0.001823652105188687","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.001","1.0000000000000001e-201","NaN","NaN","LogCCDF","-6.3078258248075425","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.001","9.9999999999999998e-201","NaN","NaN","LogPDF","452.60983971342529","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.001","9.9999999999999998e-201","NaN","NaN","LogCDF","-0.0002196324750319099","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.001","9.9999999999999998e-201","NaN","NaN","LogCCDF","-8.4236647908310722","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.001","9.9999999999999999e-198","NaN","NaN","LogPDF","-553.29100781027785","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.001","9.9999999999999999e-198","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.001","9.9999999999999999e-198","NaN","NaN","LogCCDF","-1013.8090239153952","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.001","0","NaN","NaN","LogPDF","Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.001","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.001","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.001","1.0000000000000001e+190","NaN","NaN","LogPDF","-444.42137240528251","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.001","1.0000000000000001e+190","NaN","NaN","LogCDF","-0.022449457331756989","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.001","1.0000000000000001e+190","NaN","NaN","LogCCDF","-3.8076925670559727","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.001","1.0000000000000001e+199","NaN","NaN","LogPDF","-465.22391497629195","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.001","1.0000000000000001e+199","NaN","NaN","LogCDF","-0.001823652105188687","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.001","1.0000000000000001e+199","NaN","NaN","LogCCDF","-6.3078258248075425","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.001","9.9999999999999997e+199","NaN","NaN","LogPDF","-468.42419748419303","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.001","9.9999999999999997e+199","NaN","NaN","LogCDF","-0.0002196324750319099","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.001","9.9999999999999997e+199","NaN","NaN","LogCCDF","-8.4236647908310722","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.001","0.99999999999999999e+203","NaN","NaN","LogPDF","-1474.3250450078963","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.001","0.99999999999999999e+203","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.001","0.99999999999999999e+203","NaN","NaN","LogCCDF","-1013.8090239153952","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.01","0","NaN","NaN","LogPDF","Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","1","0.01","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","1","0.01","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.01","1e-10","NaN","NaN","LogPDF","18.196112542499034","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.01","1e-10","NaN","NaN","LogCDF","-0.22456820135432504","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.01","1e-10","NaN","NaN","LogCCDF","-1.6037595222189915","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.01","0.10000000000000001","NaN","NaN","LogPDF","-2.4199206359779168","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.01","0.10000000000000001","NaN","NaN","LogCDF","-0.018301776508399554","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.01","0.10000000000000001","NaN","NaN","LogCCDF","-4.00989407871855","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.01","1","NaN","NaN","LogPDF","-5.599479878042021","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.01","1","NaN","NaN","LogCDF","-0.0022186941057126489","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.01","1","NaN","NaN","LogCCDF","-6.111945638793733","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.01","1000","NaN","NaN","LogPDF","-1011.4381576042343","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.01","1000","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.01","1000","NaN","NaN","LogCCDF","-1011.4391461284306","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.01","0","NaN","NaN","LogPDF","Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.01","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.01","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.01","1e-210","NaN","NaN","LogPDF","478.71313114130817","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.01","1e-210","NaN","NaN","LogCDF","-0.22456820135432504","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.01","1e-210","NaN","NaN","LogCCDF","-1.6037595222189915","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.01","1.0000000000000001e-201","NaN","NaN","LogPDF","458.09709796283124","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.01","1.0000000000000001e-201","NaN","NaN","LogCDF","-0.018301776508399554","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.01","1.0000000000000001e-201","NaN","NaN","LogCCDF","-4.00989407871855","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.01","9.9999999999999998e-201","NaN","NaN","LogPDF","454.91753872076714","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.01","9.9999999999999998e-201","NaN","NaN","LogCDF","-0.0022186941057126489","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.01","9.9999999999999998e-201","NaN","NaN","LogCCDF","-6.111945638793733","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.01","9.9999999999999999e-198","NaN","NaN","LogPDF","-550.92113900542518","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.01","9.9999999999999999e-198","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.01","9.9999999999999999e-198","NaN","NaN","LogCCDF","-1011.4391461284306","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.01","0","NaN","NaN","LogPDF","Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.01","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.01","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.01","1.0000000000000001e+190","NaN","NaN","LogPDF","-442.32090605631015","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.01","1.0000000000000001e+190","NaN","NaN","LogCDF","-0.22456820135432504","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.01","1.0000000000000001e+190","NaN","NaN","LogCCDF","-1.6037595222189915","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.01","1.0000000000000001e+199","NaN","NaN","LogPDF","-462.93693923478708","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.01","1.0000000000000001e+199","NaN","NaN","LogCDF","-0.018301776508399554","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.01","1.0000000000000001e+199","NaN","NaN","LogCCDF","-4.00989407871855","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.01","9.9999999999999997e+199","NaN","NaN","LogPDF","-466.11649847685118","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.01","9.9999999999999997e+199","NaN","NaN","LogCDF","-0.0022186941057126489","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.01","9.9999999999999997e+199","NaN","NaN","LogCCDF","-6.111945638793733","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.01","0.99999999999999999e+203","NaN","NaN","LogPDF","-1471.9551762030435","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.01","0.99999999999999999e+203","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.01","0.99999999999999999e+203","NaN","NaN","LogCCDF","-1011.4391461284306","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.10000000000000001","0","NaN","NaN","LogPDF","Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","1","0.10000000000000001","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","1","0.10000000000000001","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.10000000000000001","1e-10","NaN","NaN","LogPDF","18.470553185112205","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.10000000000000001","1e-10","NaN","NaN","LogCDF","-2.2527126517432969","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.10000000000000001","1e-10","NaN","NaN","LogCCDF","-0.11105860856547681","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.10000000000000001","0.10000000000000001","NaN","NaN","LogPDF","-0.28038606803956473","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.10000000000000001","0.10000000000000001","NaN","NaN","LogCDF","-0.18928362433858814","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.10000000000000001","0.10000000000000001","NaN","NaN","LogCCDF","-1.7576581431065503","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.10000000000000001","1","NaN","NaN","LogPDF","-3.252712651734206","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.10000000000000001","1","NaN","NaN","LogCDF","-0.024423176211098942","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.10000000000000001","1","NaN","NaN","LogCCDF","-3.7244094871979372","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.10000000000000001","1000","NaN","NaN","LogPDF","-1008.4696924028181","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.10000000000000001","1000","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.10000000000000001","1000","NaN","NaN","LogCCDF","-1008.4705911014665","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","0","NaN","NaN","LogPDF","Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","1e-210","NaN","NaN","LogPDF","478.98757178392134","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","1e-210","NaN","NaN","LogCDF","-2.2527126517432969","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","1e-210","NaN","NaN","LogCCDF","-0.11105860856547681","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","1.0000000000000001e-201","NaN","NaN","LogPDF","460.23663253076961","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","1.0000000000000001e-201","NaN","NaN","LogCDF","-0.18928362433858814","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","1.0000000000000001e-201","NaN","NaN","LogCCDF","-1.7576581431065503","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","9.9999999999999998e-201","NaN","NaN","LogPDF","457.26430594707494","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","9.9999999999999998e-201","NaN","NaN","LogCDF","-0.024423176211098942","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","9.9999999999999998e-201","NaN","NaN","LogCCDF","-3.7244094871979372","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","9.9999999999999999e-198","NaN","NaN","LogPDF","-547.9526738040089","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","9.9999999999999999e-198","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","9.9999999999999999e-198","NaN","NaN","LogCCDF","-1008.4705911014665","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","0","NaN","NaN","LogPDF","Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","1.0000000000000001e+190","NaN","NaN","LogPDF","-442.04646541369698","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","1.0000000000000001e+190","NaN","NaN","LogCDF","-2.2527126517432969","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","1.0000000000000001e+190","NaN","NaN","LogCCDF","-0.11105860856547681","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","1.0000000000000001e+199","NaN","NaN","LogPDF","-460.79740466684871","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","1.0000000000000001e+199","NaN","NaN","LogCDF","-0.18928362433858814","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","1.0000000000000001e+199","NaN","NaN","LogCCDF","-1.7576581431065503","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","9.9999999999999997e+199","NaN","NaN","LogPDF","-463.76973125054337","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","9.9999999999999997e+199","NaN","NaN","LogCDF","-0.024423176211098942","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","9.9999999999999997e+199","NaN","NaN","LogCCDF","-3.7244094871979372","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","0.99999999999999999e+203","NaN","NaN","LogPDF","-1468.9867110016271","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","0.99999999999999999e+203","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","0.99999999999999999e+203","NaN","NaN","LogCCDF","-1008.4705911014665","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.5","0","NaN","NaN","LogPDF","Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","1","0.5","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","1","0.5","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.5","1e-10","NaN","NaN","LogPDF","10.940560521945526","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.5","1e-10","NaN","NaN","LogCDF","-11.392143227368317","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.5","1e-10","NaN","NaN","LogCCDF","-1.1283855333035131e-05","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.5","0.10000000000000001","NaN","NaN","LogPDF","0.47892760357232267","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.5","0.10000000000000001","NaN","NaN","LogCDF","-1.0634020471545285","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.5","0.10000000000000001","NaN","NaN","LogCCDF","-0.42354632347596588","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.5","1","NaN","NaN","LogPDF","-1.5723649429247","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.5","1","NaN","NaN","LogCDF","-0.17114331524104021","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.5","1","NaN","NaN","LogCCDF","-1.8496055099332522","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.5","1000","NaN","NaN","LogPDF","-1004.0262425824158","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.5","1000","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","0.5","1000","NaN","NaN","LogCCDF","-1004.0267419589519","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","0","NaN","NaN","LogPDF","Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","1e-210","NaN","NaN","LogPDF","471.4575791207547","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","1e-210","NaN","NaN","LogCDF","-11.392143227368317","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","1e-210","NaN","NaN","LogCCDF","-1.1283855333035131e-05","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","1.0000000000000001e-201","NaN","NaN","LogPDF","460.9959462023815","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","1.0000000000000001e-201","NaN","NaN","LogCDF","-1.0634020471545285","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","1.0000000000000001e-201","NaN","NaN","LogCCDF","-0.42354632347596588","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","9.9999999999999998e-201","NaN","NaN","LogPDF","458.94465365588445","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","9.9999999999999998e-201","NaN","NaN","LogCDF","-0.17114331524104021","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","9.9999999999999998e-201","NaN","NaN","LogCCDF","-1.8496055099332522","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","9.9999999999999999e-198","NaN","NaN","LogPDF","-543.5092239836066","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","9.9999999999999999e-198","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","9.9999999999999999e-198","NaN","NaN","LogCCDF","-1004.0267419589519","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","0","NaN","NaN","LogPDF","Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","1.0000000000000001e+190","NaN","NaN","LogPDF","-449.57645807686362","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","1.0000000000000001e+190","NaN","NaN","LogCDF","-11.392143227368317","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","1.0000000000000001e+190","NaN","NaN","LogCCDF","-1.1283855333035131e-05","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","1.0000000000000001e+199","NaN","NaN","LogPDF","-460.03809099523681","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","1.0000000000000001e+199","NaN","NaN","LogCDF","-1.0634020471545285","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","1.0000000000000001e+199","NaN","NaN","LogCCDF","-0.42354632347596588","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","9.9999999999999997e+199","NaN","NaN","LogPDF","-462.08938354173387","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","9.9999999999999997e+199","NaN","NaN","LogCDF","-0.17114331524104021","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","9.9999999999999997e+199","NaN","NaN","LogCCDF","-1.8496055099332522","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","0.99999999999999999e+203","NaN","NaN","LogPDF","-1464.5432611812248","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","0.99999999999999999e+203","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","0.99999999999999999e+203","NaN","NaN","LogCCDF","-1004.0267419589519","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","1","0","NaN","NaN","LogPDF","-0","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","1","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","1","1","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","1","1e-10","NaN","NaN","LogPDF","-1e-10","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","1","1e-10","NaN","NaN","LogCDF","-23.025850929990458","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","1","1e-10","NaN","NaN","LogCCDF","-9.9999999999999874e-11","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","1","0.10000000000000001","NaN","NaN","LogPDF","-0.10000000000000001","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","1","0.10000000000000001","NaN","NaN","LogCDF","-2.3521684610440907","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","1","0.10000000000000001","NaN","NaN","LogCCDF","-0.10000000000000002","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","1","1","NaN","NaN","LogPDF","-1","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","1","1","NaN","NaN","LogCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","1","1","NaN","NaN","LogCCDF","-1","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","1","1000","NaN","NaN","LogPDF","-1000","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","1","1000","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","1","1000","NaN","NaN","LogCCDF","-1000","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","1","0","NaN","NaN","LogPDF","460.51701859880916","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","1","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","1","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","1","1e-210","NaN","NaN","LogPDF","460.51701859870917","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","1","1e-210","NaN","NaN","LogCDF","-23.025850929990458","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","1","1e-210","NaN","NaN","LogCCDF","-9.9999999999999874e-11","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","1","1.0000000000000001e-201","NaN","NaN","LogPDF","460.41701859880914","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","1","1.0000000000000001e-201","NaN","NaN","LogCDF","-2.3521684610440907","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","1","1.0000000000000001e-201","NaN","NaN","LogCCDF","-0.10000000000000002","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","1","9.9999999999999998e-201","NaN","NaN","LogPDF","459.51701859880916","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","1","9.9999999999999998e-201","NaN","NaN","LogCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","1","9.9999999999999998e-201","NaN","NaN","LogCCDF","-1","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","1","9.9999999999999999e-198","NaN","NaN","LogPDF","-539.48298140119084","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","1","9.9999999999999999e-198","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","1","9.9999999999999999e-198","NaN","NaN","LogCCDF","-1000","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","1","0","NaN","NaN","LogPDF","-460.51701859880916","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","1","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","1","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","1","1.0000000000000001e+190","NaN","NaN","LogPDF","-460.51701859890915","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","1","1.0000000000000001e+190","NaN","NaN","LogCDF","-23.025850929990458","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","1","1.0000000000000001e+190","NaN","NaN","LogCCDF","-9.9999999999999874e-11","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","1","1.0000000000000001e+199","NaN","NaN","LogPDF","-460.61701859880918","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","1","1.0000000000000001e+199","NaN","NaN","LogCDF","-2.3521684610440907","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","1","1.0000000000000001e+199","NaN","NaN","LogCCDF","-0.10000000000000002","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","1","9.9999999999999997e+199","NaN","NaN","LogPDF","-461.51701859880916","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","1","9.9999999999999997e+199","NaN","NaN","LogCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","1","9.9999999999999997e+199","NaN","NaN","LogCCDF","-1","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","1","0.99999999999999999e+203","NaN","NaN","LogPDF","-1460.517018598809","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","1","0.99999999999999999e+203","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","1","0.99999999999999999e+203","NaN","NaN","LogCCDF","-1000","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","2","0","NaN","NaN","LogPDF","-Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","1","2","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","1","2","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","2","1e-10","NaN","NaN","LogPDF","-23.025850930040459","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","2","1e-10","NaN","NaN","LogCDF","-46.744849040507525","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","2","1e-10","NaN","NaN","LogCCDF","-4.9999999996666709e-21","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","2","0.10000000000000001","NaN","NaN","LogPDF","-2.4025850929940455","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","2","0.10000000000000001","NaN","NaN","LogCDF","-5.3647050287321116","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","2","0.10000000000000001","NaN","NaN","LogCCDF","-0.0046898201956751427","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","2","1","NaN","NaN","LogPDF","-0.99999999999999989","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","2","1","NaN","NaN","LogCDF","-1.3308932682040548","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","2","1","NaN","NaN","LogCCDF","-0.3068528194400546","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","2","2","NaN","NaN","LogPDF","-1.3068528194400546","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","2","2","NaN","NaN","LogCDF","-0.52088580766434445","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","2","2","NaN","NaN","LogCCDF","-0.90138771133189022","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","2","1000","NaN","NaN","LogPDF","-993.09224472101789","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","2","1000","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","2","1000","NaN","NaN","LogCCDF","-993.09124522068475","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","2","0","NaN","NaN","LogPDF","-Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","2","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","2","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","2","1e-210","NaN","NaN","LogPDF","437.4911676687687","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","2","1e-210","NaN","NaN","LogCDF","-46.744849040507525","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","2","1e-210","NaN","NaN","LogCCDF","-4.9999999996666709e-21","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","2","1.0000000000000001e-201","NaN","NaN","LogPDF","458.1144335058151","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","2","1.0000000000000001e-201","NaN","NaN","LogCDF","-5.3647050287321116","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","2","1.0000000000000001e-201","NaN","NaN","LogCCDF","-0.0046898201956751427","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","2","9.9999999999999998e-201","NaN","NaN","LogPDF","459.51701859880916","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","2","9.9999999999999998e-201","NaN","NaN","LogCDF","-1.3308932682040548","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","2","9.9999999999999998e-201","NaN","NaN","LogCCDF","-0.3068528194400546","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","2","2e-200","NaN","NaN","LogPDF","459.21016577936911","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","2","2e-200","NaN","NaN","LogCDF","-0.52088580766434445","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","2","2e-200","NaN","NaN","LogCCDF","-0.90138771133189022","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","2","9.9999999999999999e-198","NaN","NaN","LogPDF","-532.57522612220873","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","2","9.9999999999999999e-198","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","2","9.9999999999999999e-198","NaN","NaN","LogCCDF","-993.09124522068475","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","2","0","NaN","NaN","LogPDF","-Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","2","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","2","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","2","1.0000000000000001e+190","NaN","NaN","LogPDF","-483.54286952884962","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","2","1.0000000000000001e+190","NaN","NaN","LogCDF","-46.744849040507525","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","2","1.0000000000000001e+190","NaN","NaN","LogCCDF","-4.9999999996666709e-21","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","2","1.0000000000000001e+199","NaN","NaN","LogPDF","-462.91960369180322","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","2","1.0000000000000001e+199","NaN","NaN","LogCDF","-5.3647050287321116","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","2","1.0000000000000001e+199","NaN","NaN","LogCCDF","-0.0046898201956751427","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","2","9.9999999999999997e+199","NaN","NaN","LogPDF","-461.51701859880916","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","2","9.9999999999999997e+199","NaN","NaN","LogCDF","-1.3308932682040548","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","2","9.9999999999999997e+199","NaN","NaN","LogCCDF","-0.3068528194400546","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","2","1.9999999999999999e+200","NaN","NaN","LogPDF","-461.82387141824921","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","2","1.9999999999999999e+200","NaN","NaN","LogCDF","-0.52088580766434445","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","2","1.9999999999999999e+200","NaN","NaN","LogCCDF","-0.90138771133189022","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","2","0.99999999999999999e+203","NaN","NaN","LogPDF","-1453.6092633198271","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","2","0.99999999999999999e+203","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","2","0.99999999999999999e+203","NaN","NaN","LogCCDF","-993.09124522068475","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10","0","NaN","NaN","LogPDF","-Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","1","10","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","1","10","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10","1e-10","NaN","NaN","LogPDF","-220.03448584964559","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10","1e-10","NaN","NaN","LogCDF","-245.36292187257101","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10","1e-10","NaN","NaN","LogCCDF","-2.7557319221480277e-107","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10","0.10000000000000001","NaN","NaN","LogPDF","-33.625093317027883","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10","0.10000000000000001","NaN","NaN","LogCDF","-38.221138013629556","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10","0.10000000000000001","NaN","NaN","LogCCDF","-2.5163478067703012e-17","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10","1","NaN","NaN","LogPDF","-13.801827480081471","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10","1","NaN","NaN","LogCDF","-16.009909825202023","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10","1","NaN","NaN","LogCCDF","-1.1142548454653967e-07","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10","10","NaN","NaN","LogPDF","-2.0785616431350582","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10","10","NaN","NaN","LogCDF","-0.61235960785375498","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10","10","NaN","NaN","LogCCDF","-0.78103956849627765","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10","1000","NaN","NaN","LogPDF","-950.63202996924224","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10","1000","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10","1000","NaN","NaN","LogCCDF","-950.6229983701561","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10","0","NaN","NaN","LogPDF","-Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10","1e-210","NaN","NaN","LogPDF","240.48253274916357","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10","1e-210","NaN","NaN","LogCDF","-245.36292187257101","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10","1e-210","NaN","NaN","LogCCDF","-2.7557319221480277e-107","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10","1.0000000000000001e-201","NaN","NaN","LogPDF","426.89192528178125","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10","1.0000000000000001e-201","NaN","NaN","LogCDF","-38.221138013629556","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10","1.0000000000000001e-201","NaN","NaN","LogCCDF","-2.5163478067703012e-17","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10","9.9999999999999998e-201","NaN","NaN","LogPDF","446.71519111872772","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10","9.9999999999999998e-201","NaN","NaN","LogCDF","-16.009909825202023","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10","9.9999999999999998e-201","NaN","NaN","LogCCDF","-1.1142548454653967e-07","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10","9.9999999999999998e-200","NaN","NaN","LogPDF","458.4384569556741","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10","9.9999999999999998e-200","NaN","NaN","LogCDF","-0.61235960785375498","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10","9.9999999999999998e-200","NaN","NaN","LogCCDF","-0.78103956849627765","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10","9.9999999999999999e-198","NaN","NaN","LogPDF","-490.11501137043308","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10","9.9999999999999999e-198","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10","9.9999999999999999e-198","NaN","NaN","LogCCDF","-950.6229983701561","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10","0","NaN","NaN","LogPDF","-Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10","1.0000000000000001e+190","NaN","NaN","LogPDF","-680.55150444845481","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10","1.0000000000000001e+190","NaN","NaN","LogCDF","-245.36292187257101","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10","1.0000000000000001e+190","NaN","NaN","LogCCDF","-2.7557319221480277e-107","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10","1.0000000000000001e+199","NaN","NaN","LogPDF","-494.14211191583706","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10","1.0000000000000001e+199","NaN","NaN","LogCDF","-38.221138013629556","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10","1.0000000000000001e+199","NaN","NaN","LogCCDF","-2.5163478067703012e-17","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10","9.9999999999999997e+199","NaN","NaN","LogPDF","-474.3188460788906","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10","9.9999999999999997e+199","NaN","NaN","LogCDF","-16.009909825202023","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10","9.9999999999999997e+199","NaN","NaN","LogCCDF","-1.1142548454653967e-07","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10","9.999999999999999e+200","NaN","NaN","LogPDF","-462.59558024194422","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10","9.999999999999999e+200","NaN","NaN","LogCDF","-0.61235960785375498","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10","9.999999999999999e+200","NaN","NaN","LogCCDF","-0.78103956849627765","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10","0.99999999999999999e+203","NaN","NaN","LogPDF","-1411.1490485680515","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10","0.99999999999999999e+203","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10","0.99999999999999999e+203","NaN","NaN","LogCCDF","-950.6229983701561","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","100","0","NaN","NaN","LogPDF","-Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","1","100","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","1","100","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","100","1e-10","NaN","NaN","LogPDF","-2638.6934474337804","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","100","1e-10","NaN","NaN","LogCDF","-2666.3244685497079","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","100","1e-10","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","100","0.10000000000000001","NaN","NaN","LogPDF","-587.19012957598591","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","100","0.10000000000000001","NaN","NaN","LogCDF","-594.0968942751158","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","100","0.10000000000000001","NaN","NaN","LogCCDF","-9.7050348771266356e-259","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","100","1","NaN","NaN","LogPDF","-360.1342053695754","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","100","1","NaN","NaN","LogCDF","-364.72942620471963","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","100","1","NaN","NaN","LogCCDF","-3.9812808189568126e-159","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","100","100","NaN","NaN","LogPDF","-3.2223569567543531","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","100","100","NaN","NaN","LogCDF","-0.66689715058528931","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","100","100","NaN","NaN","LogCCDF","-0.72010489302547409","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","100","1000","NaN","NaN","LogPDF","-675.26643275034382","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","100","1000","NaN","NaN","LogCDF","-6.0358275296316284e-294","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","100","1000","NaN","NaN","LogCCDF","-675.16230437337174","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","100","0","NaN","NaN","LogPDF","-Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","100","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","100","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","100","1e-210","NaN","NaN","LogPDF","-2178.1764288349714","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","100","1e-210","NaN","NaN","LogCDF","-2666.3244685497079","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","100","1e-210","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","100","1.0000000000000001e-201","NaN","NaN","LogPDF","-126.67311097717675","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","100","1.0000000000000001e-201","NaN","NaN","LogCDF","-594.0968942751158","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","100","1.0000000000000001e-201","NaN","NaN","LogCCDF","-9.7050348771266356e-259","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","100","9.9999999999999998e-201","NaN","NaN","LogPDF","100.38281322923376","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","100","9.9999999999999998e-201","NaN","NaN","LogCDF","-364.72942620471963","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","100","9.9999999999999998e-201","NaN","NaN","LogCCDF","-3.9812808189568126e-159","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","100","9.9999999999999991e-199","NaN","NaN","LogPDF","457.29466164205479","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","100","9.9999999999999991e-199","NaN","NaN","LogCDF","-0.66689715058528931","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","100","9.9999999999999991e-199","NaN","NaN","LogCCDF","-0.72010489302547409","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","100","9.9999999999999999e-198","NaN","NaN","LogPDF","-214.74941415153467","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","100","9.9999999999999999e-198","NaN","NaN","LogCDF","-6.0358275296316284e-294","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","100","9.9999999999999999e-198","NaN","NaN","LogCCDF","-675.16230437337174","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","100","0","NaN","NaN","LogPDF","-Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","100","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","100","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","100","1.0000000000000001e+190","NaN","NaN","LogPDF","-3099.2104660325895","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","100","1.0000000000000001e+190","NaN","NaN","LogCDF","-2666.3244685497079","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","100","1.0000000000000001e+190","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","100","1.0000000000000001e+199","NaN","NaN","LogPDF","-1047.7071481747951","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","100","1.0000000000000001e+199","NaN","NaN","LogCDF","-594.0968942751158","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","100","1.0000000000000001e+199","NaN","NaN","LogCCDF","-9.7050348771266356e-259","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","100","9.9999999999999997e+199","NaN","NaN","LogPDF","-820.65122396838456","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","100","9.9999999999999997e+199","NaN","NaN","LogCDF","-364.72942620471963","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","100","9.9999999999999997e+199","NaN","NaN","LogCCDF","-3.9812808189568126e-159","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","100","9.999999999999999e+201","NaN","NaN","LogPDF","-463.73937555556353","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","100","9.999999999999999e+201","NaN","NaN","LogCDF","-0.66689715058528931","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","100","9.999999999999999e+201","NaN","NaN","LogCCDF","-0.72010489302547409","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","100","0.99999999999999999e+203","NaN","NaN","LogPDF","-1135.783451349153","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","100","0.99999999999999999e+203","NaN","NaN","LogCDF","-6.0358275296316284e-294","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","100","0.99999999999999999e+203","NaN","NaN","LogCCDF","-675.16230437337174","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10000","0","NaN","NaN","LogPDF","-Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","1","10000","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","1","10000","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10000","1e-10","NaN","NaN","LogPDF","-312335.20094491716","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10000","1e-10","NaN","NaN","LogCDF","-312367.437136219","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10000","1e-10","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10000","0.10000000000000001","NaN","NaN","LogPDF","-105123.36584128984","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10000","0.10000000000000001","NaN","NaN","LogCDF","-105134.87875675576","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10000","0.10000000000000001","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10000","1","NaN","NaN","LogPDF","-82100.717496442376","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10000","1","NaN","NaN","LogCDF","-82109.927736819358","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10000","1","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10000","10000","NaN","NaN","LogPDF","-5.5241170525260941","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10000","10000","NaN","NaN","LogCDF","-0.69049109440197243","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10000","10000","NaN","NaN","LogCCDF","-0.69581034030382005","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10000","1000","NaN","NaN","LogPDF","-14029.072461899988","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10000","1000","NaN","NaN","LogCDF","-14031.269698821252","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","1","10000","1000","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10000","0","NaN","NaN","LogPDF","-Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10000","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10000","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10000","1e-210","NaN","NaN","LogPDF","-311874.68392631836","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10000","1e-210","NaN","NaN","LogCDF","-312367.437136219","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10000","1e-210","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10000","1.0000000000000001e-201","NaN","NaN","LogPDF","-104662.84882269103","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10000","1.0000000000000001e-201","NaN","NaN","LogCDF","-105134.87875675576","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10000","1.0000000000000001e-201","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10000","9.9999999999999998e-201","NaN","NaN","LogPDF","-81640.200477843566","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10000","9.9999999999999998e-201","NaN","NaN","LogCDF","-82109.927736819358","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10000","9.9999999999999998e-201","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10000","9.9999999999999993e-197","NaN","NaN","LogPDF","454.99290154628306","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10000","9.9999999999999993e-197","NaN","NaN","LogCDF","-0.69049109440197243","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10000","9.9999999999999993e-197","NaN","NaN","LogCCDF","-0.69581034030382005","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10000","9.9999999999999999e-198","NaN","NaN","LogPDF","-13568.555443301178","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10000","9.9999999999999999e-198","NaN","NaN","LogCDF","-14031.269698821252","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999998e-201","10000","9.9999999999999999e-198","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10000","0","NaN","NaN","LogPDF","-Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10000","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10000","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10000","1.0000000000000001e+190","NaN","NaN","LogPDF","-312795.71796351596","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10000","1.0000000000000001e+190","NaN","NaN","LogCDF","-312367.437136219","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10000","1.0000000000000001e+190","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10000","1.0000000000000001e+199","NaN","NaN","LogPDF","-105583.88285988865","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10000","1.0000000000000001e+199","NaN","NaN","LogCDF","-105134.87875675576","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10000","1.0000000000000001e+199","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10000","9.9999999999999997e+199","NaN","NaN","LogPDF","-82561.234515041186","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10000","9.9999999999999997e+199","NaN","NaN","LogCDF","-82109.927736819358","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10000","9.9999999999999997e+199","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10000","0.99999999999999999e+204","NaN","NaN","LogPDF","-466.04113565133525","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10000","0.99999999999999999e+204","NaN","NaN","LogCDF","-0.69049109440197243","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10000","0.99999999999999999e+204","NaN","NaN","LogCCDF","-0.69581034030382005","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10000","0.99999999999999999e+203","NaN","NaN","LogPDF","-14489.589480498798","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10000","0.99999999999999999e+203","NaN","NaN","LogCDF","-14031.269698821252","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","R-standardized-evaluation","0","9.9999999999999997e+199","10000","0.99999999999999999e+203","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.10000000000000001","0","NaN","NaN","LogPDF","Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","1","0.10000000000000001","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","1","0.10000000000000001","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.10000000000000001","9.9999999999999995e-21","NaN","NaN","LogPDF","39.13394658089878","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.10000000000000001","9.9999999999999995e-21","NaN","NaN","LogCDF","-4.6101660193248968","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.10000000000000001","9.9999999999999995e-21","NaN","NaN","LogCCDF","-0.009999999999999997","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.10000000000000001","0.10000000000000001","NaN","NaN","LogPDF","-1.024586744023686","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.10000000000000001","0.10000000000000001","NaN","NaN","LogCDF","-0.60126960071977242","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.10000000000000001","0.10000000000000001","NaN","NaN","LogCCDF","-0.79432823472428149","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.10000000000000001","1","NaN","NaN","LogPDF","-3.3025850929940455","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.10000000000000001","1","NaN","NaN","LogCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.10000000000000001","1","NaN","NaN","LogCCDF","-1","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.10000000000000001","10","NaN","NaN","LogPDF","-5.6338370884828537","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.10000000000000001","10","NaN","NaN","LogCDF","-0.33401785333002137","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.10000000000000001","10","NaN","NaN","LogCCDF","-1.2589254117941673","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.10000000000000001","1000","NaN","NaN","LogPDF","-10.514827159046849","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.10000000000000001","1000","NaN","NaN","LogCDF","-0.14615702488763918","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.10000000000000001","1000","NaN","NaN","LogCCDF","-1.9952623149688797","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","0","NaN","NaN","LogPDF","Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","9.9999999999999999e-221","NaN","NaN","LogPDF","499.65096517970795","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","9.9999999999999999e-221","NaN","NaN","LogCDF","-4.6101660193248968","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","9.9999999999999999e-221","NaN","NaN","LogCCDF","-0.009999999999999997","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","1.0000000000000001e-201","NaN","NaN","LogPDF","459.49243185478548","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","1.0000000000000001e-201","NaN","NaN","LogCDF","-0.60126960071977242","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","1.0000000000000001e-201","NaN","NaN","LogCCDF","-0.79432823472428149","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","9.9999999999999998e-201","NaN","NaN","LogPDF","457.21443350581512","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","9.9999999999999998e-201","NaN","NaN","LogCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","9.9999999999999998e-201","NaN","NaN","LogCCDF","-1","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","9.9999999999999998e-200","NaN","NaN","LogPDF","454.88318151032632","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","9.9999999999999998e-200","NaN","NaN","LogCDF","-0.33401785333002137","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","9.9999999999999998e-200","NaN","NaN","LogCCDF","-1.2589254117941673","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","9.9999999999999999e-198","NaN","NaN","LogPDF","450.00219143976233","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","9.9999999999999999e-198","NaN","NaN","LogCDF","-0.14615702488763918","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.10000000000000001","9.9999999999999999e-198","NaN","NaN","LogCCDF","-1.9952623149688797","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","0","NaN","NaN","LogPDF","Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","9.9999999999999989e+179","NaN","NaN","LogPDF","-421.38307201791037","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","9.9999999999999989e+179","NaN","NaN","LogCDF","-4.6101660193248968","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","9.9999999999999989e+179","NaN","NaN","LogCCDF","-0.009999999999999997","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","1.0000000000000001e+199","NaN","NaN","LogPDF","-461.54160534283284","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","1.0000000000000001e+199","NaN","NaN","LogCDF","-0.60126960071977242","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","1.0000000000000001e+199","NaN","NaN","LogCCDF","-0.79432823472428149","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","9.9999999999999997e+199","NaN","NaN","LogPDF","-463.8196036918032","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","9.9999999999999997e+199","NaN","NaN","LogCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","9.9999999999999997e+199","NaN","NaN","LogCCDF","-1","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","9.999999999999999e+200","NaN","NaN","LogPDF","-466.15085568729199","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","9.999999999999999e+200","NaN","NaN","LogCDF","-0.33401785333002137","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","9.999999999999999e+200","NaN","NaN","LogCCDF","-1.2589254117941673","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","0.99999999999999999e+203","NaN","NaN","LogPDF","-471.03184575785599","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","0.99999999999999999e+203","NaN","NaN","LogCDF","-0.14615702488763918","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.10000000000000001","0.99999999999999999e+203","NaN","NaN","LogCCDF","-1.9952623149688797","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.5","0","NaN","NaN","LogPDF","Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","1","0.5","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","1","0.5","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.5","9.9999999999999995e-21","NaN","NaN","LogPDF","22.332703749280512","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.5","9.9999999999999995e-21","NaN","NaN","LogCDF","-23.025850929990458","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.5","9.9999999999999995e-21","NaN","NaN","LogCCDF","-1e-10","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.5","0.10000000000000001","NaN","NaN","LogPDF","0.1419175999202395","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.5","0.10000000000000001","NaN","NaN","LogCDF","-1.3052432295598466","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.5","0.10000000000000001","NaN","NaN","LogCCDF","-0.31622776601683794","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.5","1","NaN","NaN","LogPDF","-1.6931471805599454","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.5","1","NaN","NaN","LogCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.5","1","NaN","NaN","LogCCDF","-1","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.5","10","NaN","NaN","LogPDF","-5.0067173872253479","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.5","10","NaN","NaN","LogCDF","-0.043251213124880235","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.5","10","NaN","NaN","LogCCDF","-3.1622776601683795","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.5","1000","NaN","NaN","LogPDF","-35.769801421734805","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.5","1000","NaN","NaN","LogCDF","-1.8467266624097115e-14","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","0.5","1000","NaN","NaN","LogCCDF","-31.622776601683793","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","0","NaN","NaN","LogPDF","Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","9.9999999999999999e-221","NaN","NaN","LogPDF","482.8497223480897","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","9.9999999999999999e-221","NaN","NaN","LogCDF","-23.025850929990458","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","9.9999999999999999e-221","NaN","NaN","LogCCDF","-1e-10","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","1.0000000000000001e-201","NaN","NaN","LogPDF","460.65893619872941","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","1.0000000000000001e-201","NaN","NaN","LogCDF","-1.3052432295598466","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","1.0000000000000001e-201","NaN","NaN","LogCCDF","-0.31622776601683794","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","9.9999999999999998e-201","NaN","NaN","LogPDF","458.82387141824921","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","9.9999999999999998e-201","NaN","NaN","LogCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","9.9999999999999998e-201","NaN","NaN","LogCCDF","-1","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","9.9999999999999998e-200","NaN","NaN","LogPDF","455.51030121158379","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","9.9999999999999998e-200","NaN","NaN","LogCDF","-0.043251213124880235","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","9.9999999999999998e-200","NaN","NaN","LogCCDF","-3.1622776601683795","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","9.9999999999999999e-198","NaN","NaN","LogPDF","424.74721717707433","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","9.9999999999999999e-198","NaN","NaN","LogCDF","-1.8467266624097115e-14","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","0.5","9.9999999999999999e-198","NaN","NaN","LogCCDF","-31.622776601683793","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","0","NaN","NaN","LogPDF","Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","9.9999999999999989e+179","NaN","NaN","LogPDF","-438.18431484952862","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","9.9999999999999989e+179","NaN","NaN","LogCDF","-23.025850929990458","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","9.9999999999999989e+179","NaN","NaN","LogCCDF","-1e-10","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","1.0000000000000001e+199","NaN","NaN","LogPDF","-460.37510099888891","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","1.0000000000000001e+199","NaN","NaN","LogCDF","-1.3052432295598466","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","1.0000000000000001e+199","NaN","NaN","LogCCDF","-0.31622776601683794","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","9.9999999999999997e+199","NaN","NaN","LogPDF","-462.21016577936911","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","9.9999999999999997e+199","NaN","NaN","LogCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","9.9999999999999997e+199","NaN","NaN","LogCCDF","-1","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","9.999999999999999e+200","NaN","NaN","LogPDF","-465.52373598603452","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","9.999999999999999e+200","NaN","NaN","LogCDF","-0.043251213124880235","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","9.999999999999999e+200","NaN","NaN","LogCCDF","-3.1622776601683795","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","0.99999999999999999e+203","NaN","NaN","LogPDF","-496.28682002054398","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","0.99999999999999999e+203","NaN","NaN","LogCDF","-1.8467266624097115e-14","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","0.5","0.99999999999999999e+203","NaN","NaN","LogCCDF","-31.622776601683793","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","1","0","NaN","NaN","LogPDF","0","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","1","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","1","1","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","1","9.9999999999999995e-21","NaN","NaN","LogPDF","-9.9999999999999995e-21","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","1","9.9999999999999995e-21","NaN","NaN","LogCDF","-46.051701859880914","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","1","9.9999999999999995e-21","NaN","NaN","LogCCDF","-9.9999999999999995e-21","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","1","0.10000000000000001","NaN","NaN","LogPDF","-0.10000000000000001","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","1","0.10000000000000001","NaN","NaN","LogCDF","-2.3521684610440907","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","1","0.10000000000000001","NaN","NaN","LogCCDF","-0.10000000000000001","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","1","1","NaN","NaN","LogPDF","-1","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","1","1","NaN","NaN","LogCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","1","1","NaN","NaN","LogCCDF","-1","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","1","10","NaN","NaN","LogPDF","-10","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","1","10","NaN","NaN","LogCDF","-4.5400960370489214e-05","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","1","10","NaN","NaN","LogCCDF","-10","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","1","1000","NaN","NaN","LogPDF","-1000","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","1","1000","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","1","1000","NaN","NaN","LogCCDF","-1000","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","1","0","NaN","NaN","LogPDF","460.51701859880916","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","1","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","1","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","1","9.9999999999999999e-221","NaN","NaN","LogPDF","460.51701859880916","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","1","9.9999999999999999e-221","NaN","NaN","LogCDF","-46.051701859880914","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","1","9.9999999999999999e-221","NaN","NaN","LogCCDF","-9.9999999999999995e-21","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","1","1.0000000000000001e-201","NaN","NaN","LogPDF","460.41701859880914","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","1","1.0000000000000001e-201","NaN","NaN","LogCDF","-2.3521684610440907","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","1","1.0000000000000001e-201","NaN","NaN","LogCCDF","-0.10000000000000001","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","1","9.9999999999999998e-201","NaN","NaN","LogPDF","459.51701859880916","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","1","9.9999999999999998e-201","NaN","NaN","LogCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","1","9.9999999999999998e-201","NaN","NaN","LogCCDF","-1","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","1","9.9999999999999998e-200","NaN","NaN","LogPDF","450.51701859880916","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","1","9.9999999999999998e-200","NaN","NaN","LogCDF","-4.5400960370489214e-05","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","1","9.9999999999999998e-200","NaN","NaN","LogCCDF","-10","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","1","9.9999999999999999e-198","NaN","NaN","LogPDF","-539.48298140119084","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","1","9.9999999999999999e-198","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","1","9.9999999999999999e-198","NaN","NaN","LogCCDF","-1000","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","1","0","NaN","NaN","LogPDF","-460.51701859880916","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","1","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","1","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","1","9.9999999999999989e+179","NaN","NaN","LogPDF","-460.51701859880916","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","1","9.9999999999999989e+179","NaN","NaN","LogCDF","-46.051701859880914","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","1","9.9999999999999989e+179","NaN","NaN","LogCCDF","-9.9999999999999995e-21","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","1","1.0000000000000001e+199","NaN","NaN","LogPDF","-460.61701859880918","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","1","1.0000000000000001e+199","NaN","NaN","LogCDF","-2.3521684610440907","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","1","1.0000000000000001e+199","NaN","NaN","LogCCDF","-0.10000000000000001","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","1","9.9999999999999997e+199","NaN","NaN","LogPDF","-461.51701859880916","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","1","9.9999999999999997e+199","NaN","NaN","LogCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","1","9.9999999999999997e+199","NaN","NaN","LogCCDF","-1","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","1","9.999999999999999e+200","NaN","NaN","LogPDF","-470.51701859880916","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","1","9.999999999999999e+200","NaN","NaN","LogCDF","-4.5400960370489214e-05","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","1","9.999999999999999e+200","NaN","NaN","LogCCDF","-10","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","1","0.99999999999999999e+203","NaN","NaN","LogPDF","-1460.517018598809","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","1","0.99999999999999999e+203","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","1","0.99999999999999999e+203","NaN","NaN","LogCCDF","-1000","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","2","0","NaN","NaN","LogPDF","-Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","1","2","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","1","2","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","2","9.9999999999999995e-21","NaN","NaN","LogPDF","-45.358554679320967","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","2","9.9999999999999995e-21","NaN","NaN","LogCDF","-92.103403719761829","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","2","9.9999999999999995e-21","NaN","NaN","LogCCDF","-9.9999999999999993e-41","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","2","0.10000000000000001","NaN","NaN","LogPDF","-1.6194379124341003","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","2","0.10000000000000001","NaN","NaN","LogCDF","-4.6101660193248968","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","2","0.10000000000000001","NaN","NaN","LogCCDF","-0.010000000000000002","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","2","1","NaN","NaN","LogPDF","-0.30685281944005471","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","2","1","NaN","NaN","LogCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","2","1","NaN","NaN","LogCCDF","-1","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","2","10","NaN","NaN","LogPDF","-97.004267726446002","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","2","10","NaN","NaN","LogCDF","-3.7200759760208361e-44","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","2","10","NaN","NaN","LogCCDF","-100","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","2","1000","NaN","NaN","LogPDF","-999992.39909754042","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","2","1000","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","2","1000","NaN","NaN","LogCCDF","-1000000","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","2","0","NaN","NaN","LogPDF","-Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","2","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","2","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","2","9.9999999999999999e-221","NaN","NaN","LogPDF","415.15846391948821","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","2","9.9999999999999999e-221","NaN","NaN","LogCDF","-92.103403719761829","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","2","9.9999999999999999e-221","NaN","NaN","LogCCDF","-9.9999999999999993e-41","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","2","1.0000000000000001e-201","NaN","NaN","LogPDF","458.89758068637508","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","2","1.0000000000000001e-201","NaN","NaN","LogCDF","-4.6101660193248968","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","2","1.0000000000000001e-201","NaN","NaN","LogCCDF","-0.010000000000000002","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","2","9.9999999999999998e-201","NaN","NaN","LogPDF","460.21016577936911","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","2","9.9999999999999998e-201","NaN","NaN","LogCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","2","9.9999999999999998e-201","NaN","NaN","LogCCDF","-1","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","2","9.9999999999999998e-200","NaN","NaN","LogPDF","363.51275087236314","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","2","9.9999999999999998e-200","NaN","NaN","LogCDF","-3.7200759760208361e-44","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","2","9.9999999999999998e-200","NaN","NaN","LogCCDF","-100","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","2","9.9999999999999999e-198","NaN","NaN","LogPDF","-999531.88207894156","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","2","9.9999999999999999e-198","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","2","9.9999999999999999e-198","NaN","NaN","LogCCDF","-1000000","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","2","0","NaN","NaN","LogPDF","-Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","2","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","2","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","2","9.9999999999999989e+179","NaN","NaN","LogPDF","-505.8755732781301","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","2","9.9999999999999989e+179","NaN","NaN","LogCDF","-92.103403719761829","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","2","9.9999999999999989e+179","NaN","NaN","LogCCDF","-9.9999999999999993e-41","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","2","1.0000000000000001e+199","NaN","NaN","LogPDF","-462.13645651124324","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","2","1.0000000000000001e+199","NaN","NaN","LogCDF","-4.6101660193248968","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","2","1.0000000000000001e+199","NaN","NaN","LogCCDF","-0.010000000000000002","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","2","9.9999999999999997e+199","NaN","NaN","LogPDF","-460.82387141824921","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","2","9.9999999999999997e+199","NaN","NaN","LogCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","2","9.9999999999999997e+199","NaN","NaN","LogCCDF","-1","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","2","9.999999999999999e+200","NaN","NaN","LogPDF","-557.52128632525512","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","2","9.999999999999999e+200","NaN","NaN","LogCDF","-3.7200759760208361e-44","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","2","9.999999999999999e+200","NaN","NaN","LogCCDF","-100","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","2","0.99999999999999999e+203","NaN","NaN","LogPDF","-1000452.9161161393","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","2","0.99999999999999999e+203","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","2","0.99999999999999999e+203","NaN","NaN","LogCCDF","-1000000","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","10","0","NaN","NaN","LogPDF","-Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","1","10","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","1","10","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","10","9.9999999999999995e-21","NaN","NaN","LogPDF","-412.16273164593417","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","10","9.9999999999999995e-21","NaN","NaN","LogCDF","-460.51701859880916","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","10","9.9999999999999995e-21","NaN","NaN","LogCCDF","-9.999999999999994e-201","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","10","0.10000000000000001","NaN","NaN","LogPDF","-18.420680744052362","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","10","0.10000000000000001","NaN","NaN","LogCDF","-23.025850929990455","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","10","0.10000000000000001","NaN","NaN","LogCCDF","-1.0000000000000011e-10","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","10","1","NaN","NaN","LogPDF","1.3025850929940459","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","10","1","NaN","NaN","LogCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","10","1","NaN","NaN","LogCCDF","-1","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","10","10","NaN","NaN","LogPDF","-9999999976.9741497","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","10","10","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","10","10","NaN","NaN","LogCCDF","-10000000000","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","10","1000","NaN","NaN","LogPDF","-1e+30","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","10","1000","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","10","1000","NaN","NaN","LogCCDF","-1e+30","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","10","0","NaN","NaN","LogPDF","-Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","10","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","10","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","10","9.9999999999999999e-221","NaN","NaN","LogPDF","48.354286952874986","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","10","9.9999999999999999e-221","NaN","NaN","LogCDF","-460.51701859880916","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","10","9.9999999999999999e-221","NaN","NaN","LogCCDF","-9.999999999999994e-201","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","10","1.0000000000000001e-201","NaN","NaN","LogPDF","442.09633785475683","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","10","1.0000000000000001e-201","NaN","NaN","LogCDF","-23.025850929990455","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","10","1.0000000000000001e-201","NaN","NaN","LogCCDF","-1.0000000000000011e-10","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","10","9.9999999999999998e-201","NaN","NaN","LogPDF","461.8196036918032","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","10","9.9999999999999998e-201","NaN","NaN","LogCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","10","9.9999999999999998e-201","NaN","NaN","LogCCDF","-1","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","10","9.9999999999999998e-200","NaN","NaN","LogPDF","-9999999516.4571304","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","10","9.9999999999999998e-200","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","10","9.9999999999999998e-200","NaN","NaN","LogCCDF","-10000000000","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","10","9.9999999999999999e-198","NaN","NaN","LogPDF","-1e+30","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","10","9.9999999999999999e-198","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","10","9.9999999999999999e-198","NaN","NaN","LogCCDF","-1e+30","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","10","0","NaN","NaN","LogPDF","-Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","10","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","10","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","10","9.9999999999999989e+179","NaN","NaN","LogPDF","-872.67975024474333","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","10","9.9999999999999989e+179","NaN","NaN","LogCDF","-460.51701859880916","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","10","9.9999999999999989e+179","NaN","NaN","LogCCDF","-9.999999999999994e-201","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","10","1.0000000000000001e+199","NaN","NaN","LogPDF","-478.93769934286149","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","10","1.0000000000000001e+199","NaN","NaN","LogCDF","-23.025850929990455","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","10","1.0000000000000001e+199","NaN","NaN","LogCCDF","-1.0000000000000011e-10","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","10","9.9999999999999997e+199","NaN","NaN","LogPDF","-459.21443350581512","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","10","9.9999999999999997e+199","NaN","NaN","LogCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","10","9.9999999999999997e+199","NaN","NaN","LogCCDF","-1","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","10","9.999999999999999e+200","NaN","NaN","LogPDF","-10000000437.491169","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","10","9.999999999999999e+200","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","10","9.999999999999999e+200","NaN","NaN","LogCCDF","-10000000000","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","10","0.99999999999999999e+203","NaN","NaN","LogPDF","-1e+30","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","10","0.99999999999999999e+203","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","10","0.99999999999999999e+203","NaN","NaN","LogCCDF","-1e+30","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","100","0","NaN","NaN","LogPDF","-Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","1","100","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","1","100","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","100","9.9999999999999995e-21","NaN","NaN","LogPDF","-4554.5133139422223","1.9999999999999999e-11","1.9999999999999999e-11","0","Weibull-defining-log-formula-R-underflow","finite" +"Weibull","R-standardized-evaluation","0","1","100","9.9999999999999995e-21","NaN","NaN","LogCDF","-4605.1701859880914","0","1.9999999999999999e-11","0","Weibull-defining-log-formula-R-underflow","finite" +"Weibull","R-standardized-evaluation","0","1","100","9.9999999999999995e-21","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","100","0.10000000000000001","NaN","NaN","LogPDF","-223.35075402042241","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","100","0.10000000000000001","NaN","NaN","LogCDF","-230.25850929940455","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","100","0.10000000000000001","NaN","NaN","LogCCDF","-1.0000000000000108e-100","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","100","1","NaN","NaN","LogPDF","3.6051701859880918","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","100","1","NaN","NaN","LogCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","100","1","NaN","NaN","LogCCDF","-1","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","100","10","NaN","NaN","LogPDF","-1e+100","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","100","10","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","100","10","NaN","NaN","LogCCDF","-1.0000000000000002e+100","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","100","1000","NaN","NaN","LogPDF","-1.0000000000000002e+300","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","100","1000","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","1","100","1000","NaN","NaN","LogCCDF","-1.0000000000000001e+300","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","100","0","NaN","NaN","LogPDF","-Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","100","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","100","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","100","9.9999999999999999e-221","NaN","NaN","LogPDF","-4093.9962953434133","1.9999999999999999e-11","1.9999999999999999e-11","0","Weibull-defining-log-formula-R-underflow","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","100","9.9999999999999999e-221","NaN","NaN","LogCDF","-4605.1701859880914","0","1.9999999999999999e-11","0","Weibull-defining-log-formula-R-underflow","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","100","9.9999999999999999e-221","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","100","1.0000000000000001e-201","NaN","NaN","LogPDF","237.16626457838674","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","100","1.0000000000000001e-201","NaN","NaN","LogCDF","-230.25850929940455","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","100","1.0000000000000001e-201","NaN","NaN","LogCCDF","-1.0000000000000108e-100","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","100","9.9999999999999998e-201","NaN","NaN","LogPDF","464.12218878479723","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","100","9.9999999999999998e-201","NaN","NaN","LogCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","100","9.9999999999999998e-201","NaN","NaN","LogCCDF","-1","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","100","9.9999999999999998e-200","NaN","NaN","LogPDF","-1e+100","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","100","9.9999999999999998e-200","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","100","9.9999999999999998e-200","NaN","NaN","LogCCDF","-1.0000000000000002e+100","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","100","9.9999999999999999e-198","NaN","NaN","LogPDF","-1.0000000000000002e+300","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","100","9.9999999999999999e-198","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999998e-201","100","9.9999999999999999e-198","NaN","NaN","LogCCDF","-1.0000000000000001e+300","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","100","0","NaN","NaN","LogPDF","-Inf","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","100","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","R-stats","infinite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","100","0","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","100","9.9999999999999989e+179","NaN","NaN","LogPDF","-5015.0303325410314","1.9999999999999999e-11","1.9999999999999999e-11","0","Weibull-defining-log-formula-R-underflow","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","100","9.9999999999999989e+179","NaN","NaN","LogCDF","-4605.1701859880914","0","1.9999999999999999e-11","0","Weibull-defining-log-formula-R-underflow","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","100","9.9999999999999989e+179","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","100","1.0000000000000001e+199","NaN","NaN","LogPDF","-683.86777261923157","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","100","1.0000000000000001e+199","NaN","NaN","LogCDF","-230.25850929940455","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","100","1.0000000000000001e+199","NaN","NaN","LogCCDF","-1.0000000000000108e-100","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","100","9.9999999999999997e+199","NaN","NaN","LogPDF","-456.91184841282109","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","100","9.9999999999999997e+199","NaN","NaN","LogCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","100","9.9999999999999997e+199","NaN","NaN","LogCCDF","-1","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","100","9.999999999999999e+200","NaN","NaN","LogPDF","-1e+100","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","100","9.999999999999999e+200","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","100","9.999999999999999e+200","NaN","NaN","LogCCDF","-1.0000000000000002e+100","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","100","0.99999999999999999e+203","NaN","NaN","LogPDF","-1.0000000000000002e+300","1.9999999999999999e-11","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","100","0.99999999999999999e+203","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","R-standardized-evaluation","0","9.9999999999999997e+199","100","0.99999999999999999e+203","NaN","NaN","LogCCDF","-1.0000000000000001e+300","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","R-quantile","0","1","0","NaN","9.9999999999999998e-13","NaN","InverseCDF","1.0000000000005e-12","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","analytic-quantile-gradient","0","1","0","NaN","9.9999999999999998e-13","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Exponential","analytic-quantile-gradient","0","1","0","NaN","9.9999999999999998e-13","NaN","Gradient2","1.0000000000005e-12","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Exponential","R-quantile","0","1","0","NaN","9.9999999999999995e-07","NaN","InverseCDF","1.0000005000003334e-06","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","analytic-quantile-gradient","0","1","0","NaN","9.9999999999999995e-07","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Exponential","analytic-quantile-gradient","0","1","0","NaN","9.9999999999999995e-07","NaN","Gradient2","1.0000005000003334e-06","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Exponential","R-quantile","0","1","0","NaN","0.01","NaN","InverseCDF","0.010050335853501442","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","analytic-quantile-gradient","0","1","0","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Exponential","analytic-quantile-gradient","0","1","0","NaN","0.01","NaN","Gradient2","0.010050335853501442","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Exponential","R-quantile","0","1","0","NaN","0.5","NaN","InverseCDF","0.69314718055994529","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","analytic-quantile-gradient","0","1","0","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Exponential","analytic-quantile-gradient","0","1","0","NaN","0.5","NaN","Gradient2","0.69314718055994529","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Exponential","R-quantile","0","1","0","NaN","0.98999999999999999","NaN","InverseCDF","4.6051701859880909","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","analytic-quantile-gradient","0","1","0","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Exponential","analytic-quantile-gradient","0","1","0","NaN","0.98999999999999999","NaN","Gradient2","4.6051701859880909","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Exponential","R-quantile","0","1","0","NaN","0.99999999999900002","NaN","InverseCDF","27.631043237893358","0","1.9999999999999999e-11","0","R-stats","finite" +"Exponential","analytic-quantile-gradient","0","1","0","NaN","0.99999999999900002","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Exponential","analytic-quantile-gradient","0","1","0","NaN","0.99999999999900002","NaN","Gradient2","27.631043237893358","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Gamma","R-quantile","0","1","0.001","NaN","9.9999999999999998e-13","NaN","InverseCDF","0","0","1.9999999999999999e-11","0","R-stats","underflowed" +"Gamma","exact-quantile-gradient","0","1","0.001","NaN","9.9999999999999998e-13","NaN","GradientScale","0","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","underflowed" +"Gamma","exact-quantile-gradient","0","1","0.001","NaN","9.9999999999999998e-13","NaN","GradientShape","NaN","0","4.9999999999999998e-08","NaN","R-qgamma-log-shape-Richardson-crosschecked-pgamma","unresolved-quantile-underflow" +"Gamma","R-quantile","0","1","0.001","NaN","9.9999999999999995e-07","NaN","InverseCDF","0","0","1.9999999999999999e-11","0","R-stats","underflowed" +"Gamma","exact-quantile-gradient","0","1","0.001","NaN","9.9999999999999995e-07","NaN","GradientScale","0","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","underflowed" +"Gamma","exact-quantile-gradient","0","1","0.001","NaN","9.9999999999999995e-07","NaN","GradientShape","NaN","0","4.9999999999999998e-08","NaN","R-qgamma-log-shape-Richardson-crosschecked-pgamma","unresolved-quantile-underflow" +"Gamma","R-quantile","0","1","0.001","NaN","0.01","NaN","InverseCDF","0","0","1.9999999999999999e-11","0","R-stats","underflowed" +"Gamma","exact-quantile-gradient","0","1","0.001","NaN","0.01","NaN","GradientScale","0","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","underflowed" +"Gamma","exact-quantile-gradient","0","1","0.001","NaN","0.01","NaN","GradientShape","NaN","0","4.9999999999999998e-08","NaN","R-qgamma-log-shape-Richardson-crosschecked-pgamma","unresolved-quantile-underflow" +"Gamma","R-quantile","0","1","0.001","NaN","0.5","NaN","InverseCDF","5.2442064082777997e-302","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","0.001","NaN","0.5","NaN","GradientScale","5.2442064082777997e-302","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","0.001","NaN","0.5","NaN","GradientShape","3.635011195160327e-296","0","4.9999999999999998e-08","7.7627698823407978e-309","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","0.001","NaN","0.98999999999999999","NaN","InverseCDF","2.4259428385577627e-05","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","0.001","NaN","0.98999999999999999","NaN","GradientScale","2.4259428385577627e-05","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","0.001","NaN","0.98999999999999999","NaN","GradientShape","0.24384124492875461","0","4.9999999999999998e-08","8.7707618945387367e-15","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","0.001","NaN","0.99999999999900002","NaN","InverseCDF","17.795747396039978","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","0.001","NaN","0.99999999999900002","NaN","GradientScale","17.795747396039978","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","0.001","NaN","0.99999999999900002","NaN","GradientShape","952.62197304629831","0","4.9999999999999998e-08","2.7535713797988137e-07","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","0.01","NaN","9.9999999999999998e-13","NaN","InverseCDF","0","0","1.9999999999999999e-11","0","R-stats","underflowed" +"Gamma","exact-quantile-gradient","0","1","0.01","NaN","9.9999999999999998e-13","NaN","GradientScale","0","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","underflowed" +"Gamma","exact-quantile-gradient","0","1","0.01","NaN","9.9999999999999998e-13","NaN","GradientShape","NaN","0","4.9999999999999998e-08","NaN","R-qgamma-log-shape-Richardson-crosschecked-pgamma","unresolved-quantile-underflow" +"Gamma","R-quantile","0","1","0.01","NaN","9.9999999999999995e-07","NaN","InverseCDF","0","0","1.9999999999999999e-11","0","R-stats","underflowed" +"Gamma","exact-quantile-gradient","0","1","0.01","NaN","9.9999999999999995e-07","NaN","GradientScale","0","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","underflowed" +"Gamma","exact-quantile-gradient","0","1","0.01","NaN","9.9999999999999995e-07","NaN","GradientShape","NaN","0","4.9999999999999998e-08","NaN","R-qgamma-log-shape-Richardson-crosschecked-pgamma","unresolved-quantile-underflow" +"Gamma","R-quantile","0","1","0.01","NaN","0.01","NaN","InverseCDF","5.660738147061956e-201","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","0.01","NaN","0.01","NaN","GradientScale","5.660738147061956e-201","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","0.01","NaN","0.01","NaN","GradientShape","2.6069123631718842e-196","0","4.9999999999999998e-08","3.6120739759608037e-210","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","0.01","NaN","0.5","NaN","InverseCDF","4.4655350189103723e-31","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","0.01","NaN","0.5","NaN","GradientScale","4.4655350189103723e-31","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","0.01","NaN","0.5","NaN","GradientShape","3.0956367409138942e-27","0","4.9999999999999998e-08","1.694357152429503e-40","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","0.01","NaN","0.98999999999999999","NaN","InverseCDF","0.26505255025158203","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","0.01","NaN","0.98999999999999999","NaN","GradientScale","0.26505255025158203","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","0.01","NaN","0.98999999999999999","NaN","GradientShape","34.842399829112587","0","4.9999999999999998e-08","9.7755644905079322e-12","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","0.01","NaN","0.99999999999900002","NaN","InverseCDF","20.018659942810039","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","0.01","NaN","0.99999999999900002","NaN","GradientScale","20.018659942810039","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","0.01","NaN","0.99999999999900002","NaN","GradientShape","98.925141051648907","0","4.9999999999999998e-08","2.2878041770624338e-10","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","0.10000000000000001","NaN","9.9999999999999998e-13","NaN","InverseCDF","6.0730483624079825e-121","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","0.10000000000000001","NaN","9.9999999999999998e-13","NaN","GradientScale","6.0730483624079825e-121","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","0.10000000000000001","NaN","9.9999999999999998e-13","NaN","GradientShape","1.6785005686277882e-117","0","4.9999999999999998e-08","1.1926463860076724e-131","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","0.10000000000000001","NaN","9.9999999999999995e-07","NaN","InverseCDF","6.073048362407954e-61","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","0.10000000000000001","NaN","9.9999999999999995e-07","NaN","GradientScale","6.073048362407954e-61","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","0.10000000000000001","NaN","9.9999999999999995e-07","NaN","GradientShape","8.3947793092902892e-58","0","4.9999999999999998e-08","1.249400722906873e-70","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","0.10000000000000001","NaN","0.01","NaN","InverseCDF","6.0730483624079208e-21","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","0.10000000000000001","NaN","0.01","NaN","GradientScale","6.0730483624079208e-21","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","0.10000000000000001","NaN","0.01","NaN","GradientShape","2.8012950579657724e-18","0","4.9999999999999998e-08","3.4474146004531522e-31","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","0.10000000000000001","NaN","0.5","NaN","InverseCDF","0.0005933911044602256","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","0.10000000000000001","NaN","0.5","NaN","GradientScale","0.0005933911044602256","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","0.10000000000000001","NaN","0.5","NaN","GradientShape","0.041597742364842163","0","4.9999999999999998e-08","3.5596525727044082e-15","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","0.10000000000000001","NaN","0.98999999999999999","NaN","InverseCDF","1.5884778179295043","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","0.10000000000000001","NaN","0.98999999999999999","NaN","GradientScale","1.5884778179295043","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","0.10000000000000001","NaN","0.98999999999999999","NaN","GradientShape","7.9356013006999602","0","4.9999999999999998e-08","1.5582557466586877e-10","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","0.10000000000000001","NaN","0.99999999999900002","NaN","InverseCDF","22.537047778047889","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","0.10000000000000001","NaN","0.99999999999900002","NaN","GradientScale","22.537047778047889","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","0.10000000000000001","NaN","0.99999999999900002","NaN","GradientShape","13.078384847595393","0","4.9999999999999998e-08","8.3433022508461941e-11","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","0.5","NaN","9.9999999999999998e-13","NaN","InverseCDF","7.8539816339744886e-25","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","0.5","NaN","9.9999999999999998e-13","NaN","GradientScale","7.8539816339744886e-25","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","0.5","NaN","9.9999999999999998e-13","NaN","GradientShape","8.7242179856514801e-23","0","4.9999999999999998e-08","6.1989297148922731e-37","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","0.5","NaN","9.9999999999999995e-07","NaN","InverseCDF","7.8539816339785822e-13","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","0.5","NaN","9.9999999999999995e-07","NaN","GradientScale","7.8539816339785822e-13","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","0.5","NaN","9.9999999999999995e-07","NaN","GradientShape","4.3839473382067759e-11","0","4.9999999999999998e-08","6.8500894477047604e-25","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","0.5","NaN","0.01","NaN","InverseCDF","7.8543928954851055e-05","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","0.5","NaN","0.01","NaN","GradientScale","7.8543928954851055e-05","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","0.5","NaN","0.01","NaN","GradientShape","0.0014905869250994492","0","4.9999999999999998e-08","1.0591257116483982e-17","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","0.5","NaN","0.5","NaN","InverseCDF","0.22746821155978642","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","0.5","NaN","0.5","NaN","GradientScale","0.22746821155978642","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","0.5","NaN","0.5","NaN","GradientShape","0.85836744474355675","0","4.9999999999999998e-08","1.461053500406706e-13","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","0.5","NaN","0.98999999999999999","NaN","InverseCDF","3.3174483005106064","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","0.5","NaN","0.98999999999999999","NaN","GradientScale","3.3174483005106064","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","0.5","NaN","0.98999999999999999","NaN","GradientShape","3.014830927649844","0","4.9999999999999998e-08","5.2778930277682645e-13","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","0.5","NaN","0.99999999999900002","NaN","InverseCDF","25.422085666224589","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","0.5","NaN","0.99999999999900002","NaN","GradientScale","25.422085666224589","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","0.5","NaN","0.99999999999900002","NaN","GradientShape","5.1389288070913075","0","4.9999999999999998e-08","7.7706729939563957e-12","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","1","NaN","9.9999999999999998e-13","NaN","InverseCDF","1.0000000000005e-12","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","1","NaN","9.9999999999999998e-13","NaN","GradientScale","1.0000000000005e-12","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","1","NaN","9.9999999999999998e-13","NaN","GradientShape","2.8053805451055021e-11","0","4.9999999999999998e-08","8.1425591548188662e-25","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","1","NaN","9.9999999999999995e-07","NaN","InverseCDF","1.0000005000003338e-06","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","1","NaN","9.9999999999999995e-07","NaN","GradientScale","1.0000005000003338e-06","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","1","NaN","9.9999999999999995e-07","NaN","GradientShape","1.4238308881371333e-05","0","4.9999999999999998e-08","2.557955127714139e-19","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","1","NaN","0.01","NaN","InverseCDF","0.010050335853501445","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","1","NaN","0.01","NaN","GradientScale","0.010050335853501445","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","1","NaN","0.01","NaN","GradientShape","0.050762096416328853","0","4.9999999999999998e-08","1.2050763411354871e-15","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","1","NaN","0.5","NaN","InverseCDF","0.69314718055994518","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","1","NaN","0.5","NaN","GradientScale","0.69314718055994518","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","1","NaN","0.5","NaN","GradientShape","0.96804483044203904","0","4.9999999999999998e-08","3.8302694349567901e-14","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","1","NaN","0.98999999999999999","NaN","InverseCDF","4.6051701859880909","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","1","NaN","0.98999999999999999","NaN","GradientScale","4.6051701859880909","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","1","NaN","0.98999999999999999","NaN","GradientShape","2.287369640671733","0","4.9999999999999998e-08","3.1085617114631862e-13","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","1","NaN","0.99999999999900002","NaN","InverseCDF","27.631043237893358","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","1","NaN","0.99999999999900002","NaN","GradientScale","27.631043237893358","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","1","NaN","0.99999999999900002","NaN","GradientShape","3.9311227175470456","0","4.9999999999999998e-08","6.6721649363741312e-13","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","2","NaN","9.9999999999999998e-13","NaN","InverseCDF","1.4142142290401945e-06","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","2","NaN","9.9999999999999998e-13","NaN","GradientScale","1.4142142290401945e-06","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","2","NaN","9.9999999999999998e-13","NaN","GradientShape","1.0176493098412205e-05","0","4.9999999999999998e-08","1.1418004128987969e-18","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","2","NaN","9.9999999999999995e-07","NaN","InverseCDF","0.0014148806614793434","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","2","NaN","9.9999999999999995e-07","NaN","GradientScale","0.0014148806614793434","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","2","NaN","9.9999999999999995e-07","NaN","GradientShape","0.005296734791236826","0","4.9999999999999998e-08","2.2724877535296173e-16","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","2","NaN","0.01","NaN","InverseCDF","0.14855474025326595","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","2","NaN","0.01","NaN","GradientScale","0.14855474025326595","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","2","NaN","0.01","NaN","GradientShape","0.22228895477242647","0","4.9999999999999998e-08","2.0622392682412283e-14","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","2","NaN","0.5","NaN","InverseCDF","1.678346990016661","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","2","NaN","0.5","NaN","GradientScale","1.678346990016661","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","2","NaN","0.5","NaN","GradientShape","0.99329489372687996","0","4.9999999999999998e-08","6.2683191970336338e-13","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","2","NaN","0.98999999999999999","NaN","InverseCDF","6.6383520679938117","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","2","NaN","0.98999999999999999","NaN","GradientScale","6.6383520679938117","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","2","NaN","0.98999999999999999","NaN","GradientShape","1.8621756294381873","0","4.9999999999999998e-08","6.4119446409656346e-14","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","2","NaN","0.99999999999900002","NaN","InverseCDF","31.099896029054484","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","2","NaN","0.99999999999900002","NaN","GradientScale","31.099896029054484","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","2","NaN","0.99999999999900002","NaN","GradientShape","3.1445042228424795","0","4.9999999999999998e-08","1.5536332220733584e-09","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","10","NaN","9.9999999999999998e-13","NaN","InverseCDF","0.29346037331774016","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","10","NaN","9.9999999999999998e-13","NaN","GradientScale","0.29346037331774016","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","10","NaN","9.9999999999999998e-13","NaN","GradientShape","0.10793896373192462","0","4.9999999999999998e-08","4.3918711249126138e-15","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","10","NaN","9.9999999999999995e-07","NaN","InverseCDF","1.2768187878644079","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","10","NaN","9.9999999999999995e-07","NaN","GradientScale","1.2768187878644079","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","10","NaN","9.9999999999999995e-07","NaN","GradientShape","0.30565519727128893","0","4.9999999999999998e-08","4.829470157119431e-15","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","10","NaN","0.01","NaN","InverseCDF","4.1301991662731989","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","10","NaN","0.01","NaN","GradientScale","4.1301991662731989","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","10","NaN","0.01","NaN","GradientShape","0.6323466073218178","0","4.9999999999999998e-08","2.8643754035329039e-14","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","10","NaN","0.5","NaN","InverseCDF","9.6687146147141352","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","10","NaN","0.5","NaN","GradientScale","9.6687146147141352","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","10","NaN","0.5","NaN","GradientShape","0.99978790183312916","0","4.9999999999999998e-08","2.4880097981849758e-13","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","10","NaN","0.98999999999999999","NaN","InverseCDF","18.783117393312519","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","10","NaN","0.98999999999999999","NaN","GradientScale","18.783117393312519","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","10","NaN","0.98999999999999999","NaN","GradientShape","1.3705316944158759","0","4.9999999999999998e-08","5.0048278570302744e-14","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","10","NaN","0.99999999999900002","NaN","InverseCDF","50.279911306030037","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","10","NaN","0.99999999999900002","NaN","GradientScale","50.279911306030037","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","10","NaN","0.99999999999900002","NaN","GradientShape","2.0473726029665387","0","4.9999999999999998e-08","5.2629722802066681e-10","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","100","NaN","9.9999999999999998e-13","NaN","InverseCDF","44.886022030017614","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","100","NaN","9.9999999999999998e-13","NaN","GradientScale","44.886022030017614","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","100","NaN","9.9999999999999998e-13","NaN","GradientShape","0.65345260855637488","0","4.9999999999999998e-08","5.1292303737682232e-14","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","100","NaN","9.9999999999999995e-07","NaN","InverseCDF","59.436320698122898","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","100","NaN","9.9999999999999995e-07","NaN","GradientScale","59.436320698122898","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","100","NaN","9.9999999999999995e-07","NaN","GradientShape","0.76358291054739869","0","4.9999999999999998e-08","6.361577931102147e-14","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","100","NaN","0.01","NaN","InverseCDF","78.215983053795824","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","100","NaN","0.01","NaN","GradientScale","78.215983053795824","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","100","NaN","0.01","NaN","GradientShape","0.88364602465163722","0","4.9999999999999998e-08","5.8286708792820718e-14","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","100","NaN","0.5","NaN","InverseCDF","99.666864919315429","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","100","NaN","0.5","NaN","GradientScale","99.666864919315429","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","100","NaN","0.5","NaN","GradientShape","0.99999801024936996","0","4.9999999999999998e-08","1.6409096303959814e-13","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","100","NaN","0.98999999999999999","NaN","InverseCDF","124.72256149072082","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","100","NaN","0.98999999999999999","NaN","GradientScale","124.72256149072082","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","100","NaN","0.98999999999999999","NaN","GradientShape","1.1163812449434809","0","4.9999999999999998e-08","5.8295810831491793e-14","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","100","NaN","0.99999999999900002","NaN","InverseCDF","187.24800173921616","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","100","NaN","0.99999999999900002","NaN","GradientScale","187.24800173921616","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","100","NaN","0.99999999999900002","NaN","GradientShape","1.3484246609827324","0","4.9999999999999998e-08","2.1996404697688376e-11","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","10000","NaN","9.9999999999999998e-13","NaN","InverseCDF","9312.6289698542259","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","10000","NaN","9.9999999999999998e-13","NaN","GradientScale","9312.6289698542259","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","10000","NaN","9.9999999999999998e-13","NaN","GradientShape","0.96483182729797701","0","4.9999999999999998e-08","2.7222668563808838e-13","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","10000","NaN","9.9999999999999995e-07","NaN","InverseCDF","9531.8351171898066","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","10000","NaN","9.9999999999999995e-07","NaN","GradientScale","9531.8351171898066","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","10000","NaN","9.9999999999999995e-07","NaN","GradientShape","0.97623392888348337","0","4.9999999999999998e-08","1.3644640972643174e-13","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","10000","NaN","0.01","NaN","InverseCDF","9768.836856696591","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","10000","NaN","0.01","NaN","GradientScale","9768.836856696591","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","10000","NaN","0.01","NaN","GradientShape","0.98836821068336922","0","4.9999999999999998e-08","6.9022565440945982e-13","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","10000","NaN","0.5","NaN","InverseCDF","9999.6666686420467","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","10000","NaN","0.5","NaN","GradientScale","9999.6666686420467","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","10000","NaN","0.5","NaN","GradientShape","0.99999999980229648","0","4.9999999999999998e-08","6.0085270092713472e-13","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","10000","NaN","0.98999999999999999","NaN","InverseCDF","10234.104379158054","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","10000","NaN","0.98999999999999999","NaN","GradientScale","10234.104379158054","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","10000","NaN","0.98999999999999999","NaN","GradientShape","1.0116317920265285","0","4.9999999999999998e-08","7.2035956937124344e-14","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Gamma","R-quantile","0","1","10000","NaN","0.99999999999900002","NaN","InverseCDF","10719.692099677319","0","1.9999999999999999e-11","0","R-stats","finite" +"Gamma","exact-quantile-gradient","0","1","10000","NaN","0.99999999999900002","NaN","GradientScale","10719.692099677319","0","1.9999999999999999e-11","0","R-qgamma-scale-identity","finite" +"Gamma","exact-quantile-gradient","0","1","10000","NaN","0.99999999999900002","NaN","GradientShape","1.0351683776435263","0","4.9999999999999998e-08","3.2489566592630581e-12","R-qgamma-log-shape-Richardson-crosschecked-pgamma","finite" +"Weibull","R-quantile","0","1","0.10000000000000001","NaN","9.9999999999999998e-13","NaN","InverseCDF","1.0000000000049999e-120","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","0.10000000000000001","NaN","9.9999999999999998e-13","NaN","Gradient1","1.0000000000050118e-120","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","0.10000000000000001","NaN","9.9999999999999998e-13","NaN","Gradient2","2.7631021116066523e-117","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","0.10000000000000001","NaN","9.9999999999999995e-07","NaN","InverseCDF","1.0000050000145838e-60","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","0.10000000000000001","NaN","9.9999999999999995e-07","NaN","Gradient1","1.0000050000145954e-60","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","0.10000000000000001","NaN","9.9999999999999995e-07","NaN","Gradient2","1.3815579135715996e-57","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","0.10000000000000001","NaN","0.01","NaN","InverseCDF","1.0514914576008534e-20","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","0.10000000000000001","NaN","0.01","NaN","Gradient1","1.0514914576008546e-20","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","0.10000000000000001","NaN","0.01","NaN","Gradient2","4.8370176156447497e-18","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","0.10000000000000001","NaN","0.5","NaN","InverseCDF","0.025600863289563104","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","0.10000000000000001","NaN","0.5","NaN","Gradient1","0.025600863289563118","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","0.10000000000000001","NaN","0.5","NaN","Gradient2","0.93830471736696919","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","0.10000000000000001","NaN","0.98999999999999999","NaN","InverseCDF","4289995.5691822227","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","0.10000000000000001","NaN","0.98999999999999999","NaN","Gradient1","4289995.5691822274","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","0.10000000000000001","NaN","0.98999999999999999","NaN","Gradient2","-655159382.80612659","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","0.10000000000000001","NaN","0.99999999999900002","NaN","InverseCDF","259401703939736.09","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","0.10000000000000001","NaN","0.99999999999900002","NaN","Gradient1","259401703939735.69","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","0.10000000000000001","NaN","0.99999999999900002","NaN","Gradient2","-86093866420679680","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","0.5","NaN","9.9999999999999998e-13","NaN","InverseCDF","1.000000000001e-24","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","0.5","NaN","9.9999999999999998e-13","NaN","Gradient1","1.0000000000009967e-24","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","0.5","NaN","9.9999999999999998e-13","NaN","Gradient2","1.1052408446382235e-22","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","0.5","NaN","9.9999999999999995e-07","NaN","InverseCDF","1.0000010000009168e-12","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","0.5","NaN","9.9999999999999995e-07","NaN","Gradient1","1.0000010000009176e-12","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","0.5","NaN","9.9999999999999995e-07","NaN","Gradient2","5.5262095493947199e-11","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","0.5","NaN","0.01","NaN","InverseCDF","0.00010100925076817656","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","0.5","NaN","0.01","NaN","Gradient1","0.00010100925076817659","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","0.5","NaN","0.01","NaN","Gradient2","0.0018586305072740368","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","0.5","NaN","0.5","NaN","InverseCDF","0.48045301391820139","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","0.5","NaN","0.5","NaN","Gradient1","0.48045301391820139","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","0.5","NaN","0.5","NaN","Gradient2","0.7043689493336921","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","0.5","NaN","0.98999999999999999","NaN","InverseCDF","21.207592441913587","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","0.5","NaN","0.98999999999999999","NaN","Gradient1","21.207592441913594","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","0.5","NaN","0.98999999999999999","NaN","Gradient2","-129.5512123589123","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","0.5","NaN","0.99999999999900002","NaN","InverseCDF","763.47455041433227","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","0.5","NaN","0.99999999999900002","NaN","Gradient1","763.47455041433204","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","0.5","NaN","0.99999999999900002","NaN","Gradient2","-10135.70457875335","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","1","NaN","9.9999999999999998e-13","NaN","InverseCDF","1.0000000000005e-12","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","1","NaN","9.9999999999999998e-13","NaN","Gradient1","1.0000000000004984e-12","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","1","NaN","9.9999999999999998e-13","NaN","Gradient2","2.763102111594182e-11","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","1","NaN","9.9999999999999995e-07","NaN","InverseCDF","1.0000005000003334e-06","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","1","NaN","9.9999999999999995e-07","NaN","Gradient1","1.0000005000003338e-06","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","1","NaN","9.9999999999999995e-07","NaN","Gradient2","1.3815516965723706e-05","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","1","NaN","0.01","NaN","InverseCDF","0.010050335853501442","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","1","NaN","0.01","NaN","Gradient1","0.010050335853501444","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","1","NaN","0.01","NaN","Gradient2","0.046233044705329601","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","1","NaN","0.5","NaN","InverseCDF","0.69314718055994529","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","1","NaN","0.5","NaN","Gradient1","0.69314718055994529","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","1","NaN","0.5","NaN","Gradient2","0.2540473975399718","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","1","NaN","0.98999999999999999","NaN","InverseCDF","4.6051701859880909","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","1","NaN","0.98999999999999999","NaN","Gradient1","4.6051701859880918","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","1","NaN","0.98999999999999999","NaN","Gradient2","-7.0329220814189961","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","1","NaN","0.99999999999900002","NaN","InverseCDF","27.631043237893358","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","1","NaN","0.99999999999900002","NaN","Gradient1","27.631043237893355","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","1","NaN","0.99999999999900002","NaN","Gradient2","-91.705771760846801","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","2","NaN","9.9999999999999998e-13","NaN","InverseCDF","1.00000000000025e-06","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","2","NaN","9.9999999999999998e-13","NaN","Gradient1","1.0000000000002492e-06","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","2","NaN","9.9999999999999998e-13","NaN","Gradient2","6.9077552789837338e-06","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","2","NaN","9.9999999999999995e-07","NaN","InverseCDF","0.0010000002500001354","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","2","NaN","9.9999999999999995e-07","NaN","Gradient1","0.0010000002500001356","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","2","NaN","9.9999999999999995e-07","NaN","Gradient2","0.0034538783779608632","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","2","NaN","0.01","NaN","InverseCDF","0.100251363349839","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","2","NaN","0.01","NaN","Gradient1","0.100251363349839","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","2","NaN","0.01","NaN","Gradient2","0.11529280789926495","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","2","NaN","0.5","NaN","InverseCDF","0.83255461115769769","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","2","NaN","0.5","NaN","Gradient1","0.8325546111576978","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","2","NaN","0.5","NaN","Gradient2","0.076285505519784941","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","2","NaN","0.98999999999999999","NaN","InverseCDF","2.1459660262893472","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","2","NaN","0.98999999999999999","NaN","Gradient1","2.1459660262893472","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","2","NaN","0.98999999999999999","NaN","Gradient2","-0.81931889825625848","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","2","NaN","0.99999999999900002","NaN","InverseCDF","5.2565238739963274","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","2","NaN","0.99999999999900002","NaN","Gradient1","5.2565238739963274","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","2","NaN","0.99999999999900002","NaN","Gradient2","-4.3615216994689749","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","10","NaN","9.9999999999999998e-13","NaN","InverseCDF","0.063095734448022467","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","10","NaN","9.9999999999999998e-13","NaN","Gradient1","0.063095734448022453","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","10","NaN","9.9999999999999998e-13","NaN","Gradient2","0.017433995708582971","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","10","NaN","9.9999999999999995e-07","NaN","InverseCDF","0.25118865571039573","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","10","NaN","9.9999999999999995e-07","NaN","Gradient1","0.25118865571039573","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","10","NaN","9.9999999999999995e-07","NaN","Gradient2","0.03470299399413445","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","10","NaN","0.01","NaN","InverseCDF","0.63127422513486209","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","10","NaN","0.01","NaN","Gradient1","0.63127422513486209","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","10","NaN","0.01","NaN","Gradient2","0.029039556386381206","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","10","NaN","0.5","NaN","InverseCDF","0.96401223546778969","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","10","NaN","0.5","NaN","Gradient1","0.96401223546778969","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","10","NaN","0.5","NaN","Gradient2","0.0035332293989775872","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","10","NaN","0.98999999999999999","NaN","InverseCDF","1.1649963600367286","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","10","NaN","0.98999999999999999","NaN","Gradient1","1.1649963600367286","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","10","NaN","0.98999999999999999","NaN","Gradient2","-0.017791587051884581","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","10","NaN","0.99999999999900002","NaN","InverseCDF","1.3936051039990014","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","10","NaN","0.99999999999900002","NaN","Gradient1","1.3936051039990014","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","10","NaN","0.99999999999900002","NaN","Gradient2","-0.046252915784524486","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","100","NaN","9.9999999999999998e-13","NaN","InverseCDF","0.75857757502918755","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","100","NaN","9.9999999999999998e-13","NaN","Gradient1","0.75857757502918755","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","100","NaN","9.9999999999999998e-13","NaN","Gradient2","0.0020960272993700976","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","100","NaN","9.9999999999999995e-07","NaN","InverseCDF","0.87096359431090042","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","100","NaN","9.9999999999999995e-07","NaN","Gradient1","0.87096359431090042","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","100","NaN","9.9999999999999995e-07","NaN","Gradient2","0.001203280629732278","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","100","NaN","0.01","NaN","InverseCDF","0.95504053701344283","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","100","NaN","0.01","NaN","Gradient1","0.95504053701344283","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","100","NaN","0.01","NaN","Gradient2","0.00043933289878826784","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","100","NaN","0.5","NaN","InverseCDF","0.99634157918202571","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","100","NaN","0.5","NaN","Gradient1","0.99634157918202571","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","100","NaN","0.5","NaN","Gradient2","3.6517206208295186e-05","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","100","NaN","0.98999999999999999","NaN","InverseCDF","1.0153890060464341","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","100","NaN","0.98999999999999999","NaN","Gradient1","1.0153890060464341","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","100","NaN","0.98999999999999999","NaN","Gradient2","-0.00015506814023034498","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","R-quantile","0","1","100","NaN","0.99999999999900002","NaN","InverseCDF","1.0337463111739069","0","1.9999999999999999e-11","0","R-stats","finite" +"Weibull","analytic-quantile-gradient","0","1","100","NaN","0.99999999999900002","NaN","Gradient1","1.0337463111739069","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Weibull","analytic-quantile-gradient","0","1","100","NaN","0.99999999999900002","NaN","Gradient2","-0.00034309418741425486","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","-1","NaN","9.9999999999999998e-13","NaN","InverseCDF","-0.96380879317472912","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","-1","NaN","9.9999999999999998e-13","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-1","NaN","9.9999999999999998e-13","NaN","Gradient2","-0.96380879317472912","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-1","NaN","9.9999999999999998e-13","NaN","Gradient3","-0.84369238194580554","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","-1","NaN","0.01","NaN","InverseCDF","-0.78285275904837393","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","-1","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-1","NaN","0.01","NaN","Gradient2","-0.78285275904837393","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-1","NaN","0.01","NaN","Gradient3","-0.45122991686665181","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","-1","NaN","0.5","NaN","InverseCDF","0.4426950408889635","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","-1","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-1","NaN","0.5","NaN","Gradient2","0.4426950408889635","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-1","NaN","0.5","NaN","Gradient3","-0.086071332055934188","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","-1","NaN","0.98999999999999999","NaN","InverseCDF","98.499162473422061","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","-1","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-1","NaN","0.98999999999999999","NaN","Gradient2","98.499162473422061","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-1","NaN","0.98999999999999999","NaN","Gradient3","-359.21183284360762","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","-1","NaN","0.99999999999900002","NaN","InverseCDF","1000022122208.0017","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","-1","NaN","0.99999999999900002","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-1","NaN","0.99999999999900002","NaN","Gradient2","1000022122208.0017","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-1","NaN","0.99999999999900002","NaN","Gradient3","-26631632375398.301","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","support","0","1","-1","NaN","NaN","NaN","Minimum","-1","0","1.9999999999999999e-11","0","mathematical-support","finite" +"GEV","support","0","1","-1","NaN","NaN","NaN","Maximum","Inf","0","1.9999999999999999e-11","0","mathematical-support","infinite" +"GEV","boundary-and-evaluation","0","1","-1","-1","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","-1","-1","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","-1","-1","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","-1","Inf","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","-1","Inf","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","-1","Inf","NaN","NaN","LogCCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","-1","0","NaN","NaN","LogPDF","-1","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","-1","0","NaN","NaN","LogCDF","-1","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","-1","0","NaN","NaN","LogCCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","-1","0.10000000000000001","NaN","NaN","LogPDF","-1.0997112686995587","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","-1","0.10000000000000001","NaN","NaN","LogCDF","-0.90909090909090906","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","-1","0.10000000000000001","NaN","NaN","LogCCDF","-0.51565446643165547","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","defining-quantile","0","1","-0.5","NaN","9.9999999999999998e-13","NaN","InverseCDF","-1.6195202668983748","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","-0.5","NaN","9.9999999999999998e-13","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-0.5","NaN","9.9999999999999998e-13","NaN","Gradient2","-1.6195202668983748","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-0.5","NaN","9.9999999999999998e-13","NaN","Gradient3","-1.9762514727369194","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","-0.5","NaN","0.01","NaN","InverseCDF","-1.068018796430688","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","-0.5","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-0.5","NaN","0.01","NaN","Gradient2","-1.068018796430688","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-0.5","NaN","0.01","NaN","Gradient3","-0.71273488713439692","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","-0.5","NaN","0.5","NaN","InverseCDF","0.40224481757289965","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","-0.5","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-0.5","NaN","0.5","NaN","Gradient2","0.40224481757289965","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-0.5","NaN","0.5","NaN","Gradient3","-0.075964128895011626","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","-0.5","NaN","0.98999999999999999","NaN","InverseCDF","17.949853380255412","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","-0.5","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-0.5","NaN","0.98999999999999999","NaN","Gradient2","17.949853380255412","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-0.5","NaN","0.98999999999999999","NaN","Gradient3","-55.872595840977226","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","-0.5","NaN","0.99999999999900002","NaN","InverseCDF","2000020.1220866549","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","-0.5","NaN","0.99999999999900002","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-0.5","NaN","0.99999999999900002","NaN","Gradient2","2000020.1220866549","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-0.5","NaN","0.99999999999900002","NaN","Gradient3","-51262657.487945281","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","support","0","1","-0.5","NaN","NaN","NaN","Minimum","-2","0","1.9999999999999999e-11","0","mathematical-support","finite" +"GEV","support","0","1","-0.5","NaN","NaN","NaN","Maximum","Inf","0","1.9999999999999999e-11","0","mathematical-support","infinite" +"GEV","boundary-and-evaluation","0","1","-0.5","-2","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","-0.5","-2","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","-0.5","-2","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","-0.5","Inf","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","-0.5","Inf","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","-0.5","Inf","NaN","NaN","LogCCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","-0.5","0","NaN","NaN","LogPDF","-1","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","-0.5","0","NaN","NaN","LogCDF","-1","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","-0.5","0","NaN","NaN","LogCCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","-0.5","0.10000000000000001","NaN","NaN","LogPDF","-1.053399970966346","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","-0.5","0.10000000000000001","NaN","NaN","LogCDF","-0.90702947845804993","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","-0.5","0.10000000000000001","NaN","NaN","LogCCDF","-0.51704778903077897","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","defining-quantile","0","1","-0.20000000000000001","NaN","9.9999999999999998e-13","NaN","InverseCDF","-2.425513508564682","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","9.9999999999999998e-13","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","9.9999999999999998e-13","NaN","Gradient2","-2.425513508564682","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","9.9999999999999998e-13","NaN","Gradient3","-3.5830036767567819","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","-0.20000000000000001","NaN","0.01","NaN","InverseCDF","-1.3159894310329097","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","0.01","NaN","Gradient2","-1.3159894310329097","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","0.01","NaN","Gradient3","-0.95380127297703476","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","-0.20000000000000001","NaN","0.5","NaN","InverseCDF","0.38028042569502568","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","0.5","NaN","Gradient2","0.38028042569502568","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","0.5","NaN","Gradient3","-0.070540163894716013","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","-0.20000000000000001","NaN","0.98999999999999999","NaN","InverseCDF","7.5468264085857797","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","0.98999999999999999","NaN","Gradient2","7.5468264085857797","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","0.98999999999999999","NaN","Gradient3","-19.983141759026935","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","-0.20000000000000001","NaN","0.99999999999900002","NaN","InverseCDF","1250.9487725532817","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","0.99999999999900002","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","0.99999999999900002","NaN","Gradient2","1250.9487725532817","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","0.99999999999900002","NaN","Gradient3","-28448.430976231783","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","support","0","1","-0.20000000000000001","NaN","NaN","NaN","Minimum","-5","0","1.9999999999999999e-11","0","mathematical-support","finite" +"GEV","support","0","1","-0.20000000000000001","NaN","NaN","NaN","Maximum","Inf","0","1.9999999999999999e-11","0","mathematical-support","infinite" +"GEV","boundary-and-evaluation","0","1","-0.20000000000000001","-5","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","-0.20000000000000001","-5","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","-0.20000000000000001","-5","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","-0.20000000000000001","Inf","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","-0.20000000000000001","Inf","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","-0.20000000000000001","Inf","NaN","NaN","LogCCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","-0.20000000000000001","0","NaN","NaN","LogPDF","-1","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","-0.20000000000000001","0","NaN","NaN","LogCDF","-1","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","-0.20000000000000001","0","NaN","NaN","LogCCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","-0.20000000000000001","0.10000000000000001","NaN","NaN","LogPDF","-1.0245465736069943","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","-0.20000000000000001","0.10000000000000001","NaN","NaN","LogCDF","-0.90573080982991594","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","-0.20000000000000001","0.10000000000000001","NaN","NaN","LogCCDF","-0.51792803615664906","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","defining-quantile","0","1","-9.9999999999999995e-07","NaN","9.9999999999999998e-13","NaN","InverseCDF","-3.3189335873636909","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","9.9999999999999998e-13","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","9.9999999999999998e-13","NaN","Gradient2","-3.3189335873636909","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","9.9999999999999998e-13","NaN","Gradient3","-5.5076661718615281","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","-9.9999999999999995e-07","NaN","0.01","NaN","InverseCDF","-1.52717845966969","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","0.01","NaN","Gradient2","-1.52717845966969","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","0.01","NaN","Gradient3","-1.1661376174731115","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","-9.9999999999999995e-07","NaN","0.5","NaN","InverseCDF","0.36651298774753305","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","0.5","NaN","Gradient2","0.36651298774753305","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","0.5","NaN","Gradient3","-0.067165876888090073","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","-9.9999999999999995e-07","NaN","0.98999999999999999","NaN","InverseCDF","4.6001598074792582","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","0.98999999999999999","NaN","Gradient2","4.6001598074792582","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","0.98999999999999999","NaN","Gradient3","-10.580718902853629","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","-9.9999999999999995e-07","NaN","0.99999999999900002","NaN","InverseCDF","27.631424978684024","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","0.99999999999900002","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","0.99999999999900002","NaN","Gradient2","27.631424978684024","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","0.99999999999900002","NaN","Gradient3","-381.74430714611907","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","support","0","1","-9.9999999999999995e-07","NaN","NaN","NaN","Minimum","-1000000","0","1.9999999999999999e-11","0","mathematical-support","finite" +"GEV","support","0","1","-9.9999999999999995e-07","NaN","NaN","NaN","Maximum","Inf","0","1.9999999999999999e-11","0","mathematical-support","infinite" +"GEV","boundary-and-evaluation","0","1","-9.9999999999999995e-07","-1000000","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","-9.9999999999999995e-07","-1000000","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","-9.9999999999999995e-07","-1000000","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","-9.9999999999999995e-07","Inf","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","-9.9999999999999995e-07","Inf","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","-9.9999999999999995e-07","Inf","NaN","NaN","LogCCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","-9.9999999999999995e-07","0","NaN","NaN","LogPDF","-1","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","-9.9999999999999995e-07","0","NaN","NaN","LogCDF","-1","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","-9.9999999999999995e-07","0","NaN","NaN","LogCCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","-9.9999999999999995e-07","0.10000000000000001","NaN","NaN","LogPDF","-1.0048375175601416","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","-9.9999999999999995e-07","0.10000000000000001","NaN","NaN","LogCDF","-0.90483742256014632","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","-9.9999999999999995e-07","0.10000000000000001","NaN","NaN","LogCCDF","-0.51853469550439713","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","defining-quantile","0","1","0","NaN","9.9999999999999998e-13","NaN","InverseCDF","-3.3189390950359559","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","0","NaN","9.9999999999999998e-13","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0","NaN","9.9999999999999998e-13","NaN","Gradient2","-3.3189390950359559","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0","NaN","9.9999999999999998e-13","NaN","Gradient3","-5.507678358279045","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","0","NaN","0.01","NaN","InverseCDF","-1.5271796258079011","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","0","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0","NaN","0.01","NaN","Gradient2","-1.5271796258079011","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0","NaN","0.01","NaN","Gradient3","-1.1661388047413805","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","0","NaN","0.5","NaN","InverseCDF","0.36651292058166435","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","0","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0","NaN","0.5","NaN","Gradient2","0.36651292058166435","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0","NaN","0.5","NaN","Gradient3","-0.067165860476650699","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","0","NaN","0.98999999999999999","NaN","InverseCDF","4.6001492267765789","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","0","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0","NaN","0.98999999999999999","NaN","Gradient2","4.6001492267765789","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0","NaN","0.98999999999999999","NaN","Gradient3","-10.580686454306578","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","0","NaN","0.99999999999900002","NaN","InverseCDF","27.631043237892857","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","0","NaN","0.99999999999900002","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0","NaN","0.99999999999900002","NaN","Gradient2","27.631043237892857","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0","NaN","0.99999999999900002","NaN","Gradient3","-381.73727520715232","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","support","0","1","0","NaN","NaN","NaN","Minimum","-Inf","0","1.9999999999999999e-11","0","mathematical-support","infinite" +"GEV","support","0","1","0","NaN","NaN","NaN","Maximum","Inf","0","1.9999999999999999e-11","0","mathematical-support","infinite" +"GEV","boundary-and-evaluation","0","1","0","-Inf","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","0","-Inf","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","0","-Inf","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","0","Inf","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","0","Inf","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","0","Inf","NaN","NaN","LogCCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","0","0","NaN","NaN","LogPDF","-1","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","0","0","NaN","NaN","LogCDF","-1","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","0","0","NaN","NaN","LogCCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","0","0.10000000000000001","NaN","NaN","LogPDF","-1.0048374180359596","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","0","0.10000000000000001","NaN","NaN","LogCDF","-0.90483741803595952","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","0","0.10000000000000001","NaN","NaN","LogCCDF","-0.51853469857887524","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","defining-quantile","0","1","9.9999999999999995e-07","NaN","9.9999999999999998e-13","NaN","InverseCDF","-3.3189446027204075","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","9.9999999999999998e-13","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","9.9999999999999998e-13","NaN","Gradient2","-3.3189446027204075","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","9.9999999999999998e-13","NaN","Gradient3","-5.5076905447268967","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","9.9999999999999995e-07","NaN","0.01","NaN","InverseCDF","-1.5271807919472993","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","0.01","NaN","Gradient2","-1.5271807919472993","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","0.01","NaN","Gradient3","-1.1661399920110092","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","9.9999999999999995e-07","NaN","0.5","NaN","InverseCDF","0.36651285341581208","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","0.5","NaN","Gradient2","0.36651285341581208","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","0.5","NaN","Gradient3","-0.067165844065215835","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","9.9999999999999995e-07","NaN","0.98999999999999999","NaN","InverseCDF","4.6001386461063491","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","0.98999999999999999","NaN","Gradient2","4.6001386461063491","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","0.98999999999999999","NaN","Gradient3","-10.58065400587148","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","9.9999999999999995e-07","NaN","0.99999999999900002","NaN","InverseCDF","27.63066150413356","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","0.99999999999900002","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","0.99999999999900002","NaN","Gradient2","27.63066150413356","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","0.99999999999900002","NaN","Gradient3","-381.73024341390897","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","support","0","1","9.9999999999999995e-07","NaN","NaN","NaN","Minimum","-Inf","0","1.9999999999999999e-11","0","mathematical-support","infinite" +"GEV","support","0","1","9.9999999999999995e-07","NaN","NaN","NaN","Maximum","1000000","0","1.9999999999999999e-11","0","mathematical-support","finite" +"GEV","boundary-and-evaluation","0","1","9.9999999999999995e-07","-Inf","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","9.9999999999999995e-07","-Inf","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","9.9999999999999995e-07","-Inf","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","9.9999999999999995e-07","1000000","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","9.9999999999999995e-07","1000000","NaN","NaN","LogCDF","0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","9.9999999999999995e-07","1000000","NaN","NaN","LogCCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","9.9999999999999995e-07","0","NaN","NaN","LogPDF","-1","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","9.9999999999999995e-07","0","NaN","NaN","LogCDF","-1","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","9.9999999999999995e-07","0","NaN","NaN","LogCCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","9.9999999999999995e-07","0.10000000000000001","NaN","NaN","LogPDF","-1.0048373185117674","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","9.9999999999999995e-07","0.10000000000000001","NaN","NaN","LogCDF","-0.90483741351177216","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","9.9999999999999995e-07","0.10000000000000001","NaN","NaN","LogCCDF","-0.51853470165335358","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","defining-quantile","0","1","0.20000000000000001","NaN","9.9999999999999998e-13","NaN","InverseCDF","-4.7106743745476383","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","9.9999999999999998e-13","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","9.9999999999999998e-13","NaN","Gradient2","-4.7106743745476383","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","9.9999999999999998e-13","NaN","Gradient3","-8.6757649481117962","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","0.20000000000000001","NaN","0.01","NaN","InverseCDF","-1.7860825944941336","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","0.01","NaN","Gradient2","-1.7860825944941336","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","0.01","NaN","Gradient3","-1.4331541048903946","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","0.20000000000000001","NaN","0.5","NaN","InverseCDF","0.35340204934197361","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","0.5","NaN","Gradient2","0.35340204934197361","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","0.5","NaN","Gradient3","-0.063972061045418527","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","0.20000000000000001","NaN","0.98999999999999999","NaN","InverseCDF","3.0074642634018969","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","0.98999999999999999","NaN","Gradient2","3.0074642634018969","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","0.98999999999999999","NaN","Gradient3","-5.8713595889730206","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","0.20000000000000001","NaN","0.99999999999900002","NaN","InverseCDF","4.980094729541257","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","0.99999999999900002","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","0.99999999999900002","NaN","Gradient2","4.980094729541257","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","0.99999999999900002","NaN","Gradient3","-24.35047025899879","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","support","0","1","0.20000000000000001","NaN","NaN","NaN","Minimum","-Inf","0","1.9999999999999999e-11","0","mathematical-support","infinite" +"GEV","support","0","1","0.20000000000000001","NaN","NaN","NaN","Maximum","5","0","1.9999999999999999e-11","0","mathematical-support","finite" +"GEV","boundary-and-evaluation","0","1","0.20000000000000001","-Inf","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","0.20000000000000001","-Inf","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","0.20000000000000001","-Inf","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","0.20000000000000001","5","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","0.20000000000000001","5","NaN","NaN","LogCDF","0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","0.20000000000000001","5","NaN","NaN","LogCCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","0.20000000000000001","0","NaN","NaN","LogPDF","-1","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","0.20000000000000001","0","NaN","NaN","LogCDF","-1","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","0.20000000000000001","0","NaN","NaN","LogCCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","0.20000000000000001","0.10000000000000001","NaN","NaN","LogPDF","-0.9847316260700778","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","0.20000000000000001","0.10000000000000001","NaN","NaN","LogCDF","-0.90392079680000004","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","0.20000000000000001","0.10000000000000001","NaN","NaN","LogCCDF","-0.51915808191830037","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","defining-quantile","0","1","0.5","NaN","9.9999999999999998e-13","NaN","InverseCDF","-8.5130435395138626","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","0.5","NaN","9.9999999999999998e-13","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0.5","NaN","9.9999999999999998e-13","NaN","Gradient2","-8.5130435395138626","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0.5","NaN","9.9999999999999998e-13","NaN","Gradient3","-17.86606413208002","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","0.5","NaN","0.01","NaN","InverseCDF","-2.2919320525786944","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","0.5","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0.5","NaN","0.01","NaN","Gradient2","-2.2919320525786944","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0.5","NaN","0.01","NaN","Gradient3","-1.9706870808926791","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","0.5","NaN","0.5","NaN","InverseCDF","0.33489077768460451","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","0.5","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0.5","NaN","0.5","NaN","Gradient2","0.33489077768460451","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0.5","NaN","0.5","NaN","Gradient3","-0.059497511210929596","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","0.5","NaN","0.98999999999999999","NaN","InverseCDF","1.7994972733003218","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","0.5","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0.5","NaN","0.98999999999999999","NaN","Gradient2","1.7994972733003218","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0.5","NaN","0.98999999999999999","NaN","Gradient3","-2.6766520834065228","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","0.5","NaN","0.99999999999900002","NaN","InverseCDF","1.9999980000221218","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","0.5","NaN","0.99999999999900002","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0.5","NaN","0.99999999999900002","NaN","Gradient2","1.9999980000221218","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","0.5","NaN","0.99999999999900002","NaN","Gradient3","-3.9999407385690176","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","support","0","1","0.5","NaN","NaN","NaN","Minimum","-Inf","0","1.9999999999999999e-11","0","mathematical-support","infinite" +"GEV","support","0","1","0.5","NaN","NaN","NaN","Maximum","2","0","1.9999999999999999e-11","0","mathematical-support","finite" +"GEV","boundary-and-evaluation","0","1","0.5","-Inf","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","0.5","-Inf","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","0.5","-Inf","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","0.5","2","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","0.5","2","NaN","NaN","LogCDF","0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","0.5","2","NaN","NaN","LogCCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","0.5","0","NaN","NaN","LogPDF","-1","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","0.5","0","NaN","NaN","LogCDF","-1","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","0.5","0","NaN","NaN","LogCCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","0.5","0.10000000000000001","NaN","NaN","LogPDF","-0.95379329438755045","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","0.5","0.10000000000000001","NaN","NaN","LogCDF","-0.90249999999999997","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","0.5","0.10000000000000001","NaN","NaN","LogCCDF","-0.52012624924666662","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","defining-quantile","0","1","1","NaN","9.9999999999999998e-13","NaN","InverseCDF","-26.63102111592854","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","1","NaN","9.9999999999999998e-13","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","1","NaN","9.9999999999999998e-13","NaN","Gradient2","-26.63102111592854","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","1","NaN","9.9999999999999998e-13","NaN","Gradient3","-65.07465510149072","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","1","NaN","0.01","NaN","InverseCDF","-3.6051701859880918","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","1","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","1","NaN","0.01","NaN","Gradient2","-3.6051701859880918","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","1","NaN","0.01","NaN","Gradient3","-3.4277518954309052","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","1","NaN","0.5","NaN","InverseCDF","0.30685281944005471","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","1","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","1","NaN","0.5","NaN","Gradient2","0.30685281944005471","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","1","NaN","0.5","NaN","Gradient3","-0.052805421900082937","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","1","NaN","0.98999999999999999","NaN","InverseCDF","0.98994966414649854","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","1","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","1","NaN","0.98999999999999999","NaN","Gradient2","0.98994966414649854","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","1","NaN","0.98999999999999999","NaN","Gradient3","-0.94371661944116847","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","1","NaN","0.99999999999900002","NaN","InverseCDF","0.99999999999899991","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","1","NaN","0.99999999999900002","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","1","NaN","0.99999999999900002","NaN","Gradient2","0.99999999999899991","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","1","NaN","0.99999999999900002","NaN","Gradient3","-0.99999999997137057","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","support","0","1","1","NaN","NaN","NaN","Minimum","-Inf","0","1.9999999999999999e-11","0","mathematical-support","infinite" +"GEV","support","0","1","1","NaN","NaN","NaN","Maximum","1","0","1.9999999999999999e-11","0","mathematical-support","finite" +"GEV","boundary-and-evaluation","0","1","1","-Inf","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","1","-Inf","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","1","-Inf","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","1","1","NaN","NaN","LogPDF","0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","1","1","NaN","NaN","LogCDF","0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","1","1","NaN","NaN","LogCCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","1","0","NaN","NaN","LogPDF","-1","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","1","0","NaN","NaN","LogCDF","-1","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","1","0","NaN","NaN","LogCCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","1","0.10000000000000001","NaN","NaN","LogPDF","-0.90000000000000002","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","1","0.10000000000000001","NaN","NaN","LogCDF","-0.90000000000000002","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","1","0.10000000000000001","NaN","NaN","LogCCDF","-0.5218354429087223","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","defining-quantile","0","1","2","NaN","9.9999999999999998e-13","NaN","InverseCDF","-381.23666395444445","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","2","NaN","9.9999999999999998e-13","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","2","NaN","9.9999999999999998e-13","NaN","Gradient2","-381.23666395444445","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","2","NaN","9.9999999999999998e-13","NaN","Gradient3","-1076.3424060297864","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","2","NaN","0.01","NaN","InverseCDF","-10.103796220956797","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","2","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","2","NaN","0.01","NaN","Gradient2","-10.103796220956797","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","2","NaN","0.01","NaN","Gradient3","-11.142003434385639","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","2","NaN","0.5","NaN","InverseCDF","0.25977349304089931","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","2","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","2","NaN","0.5","NaN","Gradient2","0.25977349304089931","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","2","NaN","0.5","NaN","Gradient3","-0.041840627853738148","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","2","NaN","0.98999999999999999","NaN","InverseCDF","0.49994949537461592","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","2","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","2","NaN","0.98999999999999999","NaN","Gradient2","0.49994949537461592","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","2","NaN","0.98999999999999999","NaN","Gradient3","-0.24974241887389875","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","defining-quantile","0","1","2","NaN","0.99999999999900002","NaN","InverseCDF","0.5","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","analytic-quantile-gradient","0","1","2","NaN","0.99999999999900002","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","2","NaN","0.99999999999900002","NaN","Gradient2","0.5","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","analytic-quantile-gradient","0","1","2","NaN","0.99999999999900002","NaN","Gradient3","-0.25000000000000111","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","support","0","1","2","NaN","NaN","NaN","Minimum","-Inf","0","1.9999999999999999e-11","0","mathematical-support","infinite" +"GEV","support","0","1","2","NaN","NaN","NaN","Maximum","0.5","0","1.9999999999999999e-11","0","mathematical-support","finite" +"GEV","boundary-and-evaluation","0","1","2","-Inf","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","2","-Inf","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","2","-Inf","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","2","0.5","NaN","NaN","LogPDF","Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","2","0.5","NaN","NaN","LogCDF","0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","2","0.5","NaN","NaN","LogCCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GEV","boundary-and-evaluation","0","1","2","0","NaN","NaN","LogPDF","-1","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","2","0","NaN","NaN","LogCDF","-1","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","2","0","NaN","NaN","LogCCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","2","0.10000000000000001","NaN","NaN","LogPDF","-0.782855415342811","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","2","0.10000000000000001","NaN","NaN","LogCDF","-0.89442719099991586","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GEV","boundary-and-evaluation","0","1","2","0.10000000000000001","NaN","NaN","LogCCDF","-0.52567147983176243","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","defining-quantile","0","1","-1","NaN","9.9999999999999998e-13","NaN","InverseCDF","1.000000000001001e-12","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","-1","NaN","9.9999999999999998e-13","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-1","NaN","9.9999999999999998e-13","NaN","Gradient2","1.000000000001e-12","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-1","NaN","9.9999999999999998e-13","NaN","Gradient3","-5.0000000000083337e-25","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","-1","NaN","0.01","NaN","InverseCDF","0.010101010101010105","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","-1","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-1","NaN","0.01","NaN","Gradient2","0.010101010101010102","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-1","NaN","0.01","NaN","Gradient3","-5.0844296466102105e-05","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","-1","NaN","0.5","NaN","InverseCDF","1","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","-1","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-1","NaN","0.5","NaN","Gradient2","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-1","NaN","0.5","NaN","Gradient3","-0.38629436111989052","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","-1","NaN","0.98999999999999999","NaN","InverseCDF","98.999999999999957","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","-1","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-1","NaN","0.98999999999999999","NaN","Gradient2","98.999999999999957","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-1","NaN","0.98999999999999999","NaN","Gradient3","-361.51701859880887","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","-1","NaN","0.99999999999900002","NaN","InverseCDF","1000022122208.5027","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","-1","NaN","0.99999999999900002","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-1","NaN","0.99999999999900002","NaN","Gradient2","1000022122208.5027","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-1","NaN","0.99999999999900002","NaN","Gradient3","-26631632375412.137","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","support","0","1","-1","NaN","NaN","NaN","Minimum","0","0","1.9999999999999999e-11","0","mathematical-support","finite" +"GPA","support","0","1","-1","NaN","NaN","NaN","Maximum","Inf","0","1.9999999999999999e-11","0","mathematical-support","infinite" +"GPA","boundary-and-evaluation","0","1","-1","0","NaN","NaN","LogPDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","-1","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","-1","0","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","-1","Inf","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","-1","Inf","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","-1","Inf","NaN","NaN","LogCCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","-1","0.10000000000000001","NaN","NaN","LogPDF","-0.19062035960864973","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","-1","0.10000000000000001","NaN","NaN","LogCDF","-2.3978952727983707","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","-1","0.10000000000000001","NaN","NaN","LogCCDF","-0.095310179804324865","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","defining-quantile","0","1","-0.5","NaN","9.9999999999999998e-13","NaN","InverseCDF","1.000000000000751e-12","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","-0.5","NaN","9.9999999999999998e-13","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-0.5","NaN","9.9999999999999998e-13","NaN","Gradient2","1.00000000000075e-12","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-0.5","NaN","9.9999999999999998e-13","NaN","Gradient3","-5.0000000000066669e-25","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","-0.5","NaN","0.01","NaN","InverseCDF","0.010075630518424155","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","-0.5","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-0.5","NaN","0.01","NaN","Gradient2","0.010075630518424151","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-0.5","NaN","0.01","NaN","Gradient3","-5.0674140800529584e-05","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","-0.5","NaN","0.5","NaN","InverseCDF","0.82842712474619007","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","-0.5","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-0.5","NaN","0.5","NaN","Gradient2","0.82842712474619007","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-0.5","NaN","0.5","NaN","Gradient3","-0.30366203744471415","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","-0.5","NaN","0.98999999999999999","NaN","InverseCDF","17.999999999999996","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","-0.5","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-0.5","NaN","0.98999999999999999","NaN","Gradient2","17.999999999999996","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-0.5","NaN","0.98999999999999999","NaN","Gradient3","-56.103403719761801","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","-0.5","NaN","0.99999999999900002","NaN","InverseCDF","2000020.1220871559","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","-0.5","NaN","0.99999999999900002","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-0.5","NaN","0.99999999999900002","NaN","Gradient2","2000020.1220871559","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-0.5","NaN","0.99999999999900002","NaN","Gradient3","-51262657.487959124","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","support","0","1","-0.5","NaN","NaN","NaN","Minimum","0","0","1.9999999999999999e-11","0","mathematical-support","finite" +"GPA","support","0","1","-0.5","NaN","NaN","NaN","Maximum","Inf","0","1.9999999999999999e-11","0","mathematical-support","infinite" +"GPA","boundary-and-evaluation","0","1","-0.5","0","NaN","NaN","LogPDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","-0.5","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","-0.5","0","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","-0.5","Inf","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","-0.5","Inf","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","-0.5","Inf","NaN","NaN","LogCCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","-0.5","0.10000000000000001","NaN","NaN","LogPDF","-0.14637049250829604","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","-0.5","0.10000000000000001","NaN","NaN","LogCDF","-2.3754728087425381","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","-0.5","0.10000000000000001","NaN","NaN","LogCCDF","-0.097580328338864014","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","defining-quantile","0","1","-0.20000000000000001","NaN","9.9999999999999998e-13","NaN","InverseCDF","1.000000000000601e-12","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","9.9999999999999998e-13","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","9.9999999999999998e-13","NaN","Gradient2","1.0000000000006e-12","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","9.9999999999999998e-13","NaN","Gradient3","-5.0000000000056659e-25","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","-0.20000000000000001","NaN","0.01","NaN","InverseCDF","0.010060443549826549","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","0.01","NaN","Gradient2","0.010060443549826546","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","0.01","NaN","Gradient3","-5.0572354885420404e-05","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","-0.20000000000000001","NaN","0.5","NaN","InverseCDF","0.74349177498517494","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","0.5","NaN","Gradient2","0.74349177498517494","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","0.5","NaN","Gradient3","-0.26362625547433488","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","-0.20000000000000001","NaN","0.98999999999999999","NaN","InverseCDF","7.5594321575479002","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","0.98999999999999999","NaN","Gradient2","7.5594321575479002","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","0.98999999999999999","NaN","Gradient3","-20.041161737140172","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","-0.20000000000000001","NaN","0.99999999999900002","NaN","InverseCDF","1250.9487725534077","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","0.99999999999900002","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","0.99999999999900002","NaN","Gradient2","1250.9487725534077","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-0.20000000000000001","NaN","0.99999999999900002","NaN","Gradient3","-28448.430976235257","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","support","0","1","-0.20000000000000001","NaN","NaN","NaN","Minimum","0","0","1.9999999999999999e-11","0","mathematical-support","finite" +"GPA","support","0","1","-0.20000000000000001","NaN","NaN","NaN","Maximum","Inf","0","1.9999999999999999e-11","0","mathematical-support","infinite" +"GPA","boundary-and-evaluation","0","1","-0.20000000000000001","0","NaN","NaN","LogPDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","-0.20000000000000001","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","-0.20000000000000001","0","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","-0.20000000000000001","Inf","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","-0.20000000000000001","Inf","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","-0.20000000000000001","Inf","NaN","NaN","LogCCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","-0.20000000000000001","0.10000000000000001","NaN","NaN","LogPDF","-0.11881576377707828","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","-0.20000000000000001","0.10000000000000001","NaN","NaN","LogCDF","-2.3616008641479014","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","-0.20000000000000001","0.10000000000000001","NaN","NaN","LogCCDF","-0.099013136480898573","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","defining-quantile","0","1","-9.9999999999999995e-07","NaN","9.9999999999999998e-13","NaN","InverseCDF","1.000000000000501e-12","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","9.9999999999999998e-13","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","9.9999999999999998e-13","NaN","Gradient2","1.0000000000005e-12","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","9.9999999999999998e-13","NaN","Gradient3","-5.0000000000050001e-25","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","-9.9999999999999995e-07","NaN","0.01","NaN","InverseCDF","0.01005033590400607","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","0.01","NaN","Gradient2","0.010050335904006067","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","0.01","NaN","Gradient3","-5.0504625722480573e-05","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","-9.9999999999999995e-07","NaN","0.5","NaN","InverseCDF","0.69314742078650782","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","0.5","NaN","Gradient2","0.69314742078650782","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","0.5","NaN","Gradient3","-0.2402266179673469","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","-9.9999999999999995e-07","NaN","0.98999999999999999","NaN","InverseCDF","4.6051807898005892","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","0.98999999999999999","NaN","Gradient2","4.6051807898005892","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","0.98999999999999999","NaN","Gradient3","-10.60382877587049","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","-9.9999999999999995e-07","NaN","0.99999999999900002","NaN","InverseCDF","27.631424978684525","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","0.99999999999900002","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","0.99999999999900002","NaN","Gradient2","27.631424978684525","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","-9.9999999999999995e-07","NaN","0.99999999999900002","NaN","Gradient3","-381.74430714613288","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","support","0","1","-9.9999999999999995e-07","NaN","NaN","NaN","Minimum","0","0","1.9999999999999999e-11","0","mathematical-support","finite" +"GPA","support","0","1","-9.9999999999999995e-07","NaN","NaN","NaN","Maximum","Inf","0","1.9999999999999999e-11","0","mathematical-support","infinite" +"GPA","boundary-and-evaluation","0","1","-9.9999999999999995e-07","0","NaN","NaN","LogPDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","-9.9999999999999995e-07","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","-9.9999999999999995e-07","0","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","-9.9999999999999995e-07","Inf","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","-9.9999999999999995e-07","Inf","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","-9.9999999999999995e-07","Inf","NaN","NaN","LogCCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","-9.9999999999999995e-07","0.10000000000000001","NaN","NaN","LogPDF","-0.10000009499999533","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","-9.9999999999999995e-07","0.10000000000000001","NaN","NaN","LogCDF","-2.3521685085857484","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","-9.9999999999999995e-07","0.10000000000000001","NaN","NaN","LogCCDF","-0.099999995000000341","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","defining-quantile","0","1","0","NaN","9.9999999999999998e-13","NaN","InverseCDF","1.000000000000501e-12","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","0","NaN","9.9999999999999998e-13","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0","NaN","9.9999999999999998e-13","NaN","Gradient2","1.0000000000005e-12","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0","NaN","9.9999999999999998e-13","NaN","Gradient3","-5.0000000000050001e-25","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","0","NaN","0.01","NaN","InverseCDF","0.010050335853501445","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","0","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0","NaN","0.01","NaN","Gradient2","0.010050335853501442","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0","NaN","0.01","NaN","Gradient3","-5.0504625384088282e-05","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","0","NaN","0.5","NaN","InverseCDF","0.69314718055994529","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","0","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0","NaN","0.5","NaN","Gradient2","0.69314718055994529","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0","NaN","0.5","NaN","Gradient3","-0.24022650695910069","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","0","NaN","0.98999999999999999","NaN","InverseCDF","4.6051701859880909","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","0","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0","NaN","0.98999999999999999","NaN","Gradient2","4.6051701859880909","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0","NaN","0.98999999999999999","NaN","Gradient3","-10.603796220956793","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","0","NaN","0.99999999999900002","NaN","InverseCDF","27.631043237893358","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","0","NaN","0.99999999999900002","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0","NaN","0.99999999999900002","NaN","Gradient2","27.631043237893358","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0","NaN","0.99999999999900002","NaN","Gradient3","-381.73727520716614","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","support","0","1","0","NaN","NaN","NaN","Minimum","0","0","1.9999999999999999e-11","0","mathematical-support","finite" +"GPA","support","0","1","0","NaN","NaN","NaN","Maximum","Inf","0","1.9999999999999999e-11","0","mathematical-support","infinite" +"GPA","boundary-and-evaluation","0","1","0","0","NaN","NaN","LogPDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","0","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","0","0","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","0","Inf","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","0","Inf","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","0","Inf","NaN","NaN","LogCCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","0","0.10000000000000001","NaN","NaN","LogPDF","-0.10000000000000001","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","0","0.10000000000000001","NaN","NaN","LogCDF","-2.3521684610440907","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","0","0.10000000000000001","NaN","NaN","LogCCDF","-0.10000000000000001","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","defining-quantile","0","1","9.9999999999999995e-07","NaN","9.9999999999999998e-13","NaN","InverseCDF","1.000000000000501e-12","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","9.9999999999999998e-13","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","9.9999999999999998e-13","NaN","Gradient2","1.0000000000005e-12","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","9.9999999999999998e-13","NaN","Gradient3","-5.0000000000050001e-25","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","9.9999999999999995e-07","NaN","0.01","NaN","InverseCDF","0.010050335802996821","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","0.01","NaN","Gradient2","0.010050335802996817","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","0.01","NaN","Gradient3","-5.0504625045695983e-05","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","9.9999999999999995e-07","NaN","0.5","NaN","InverseCDF","0.69314694033349378","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","0.5","NaN","Gradient2","0.69314694033349378","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","0.5","NaN","Gradient3","-0.24022639595091222","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","9.9999999999999995e-07","NaN","0.98999999999999999","NaN","InverseCDF","4.605159582208147","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","0.98999999999999999","NaN","Gradient2","4.605159582208147","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","0.98999999999999999","NaN","Gradient3","-10.603763666155537","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","9.9999999999999995e-07","NaN","0.99999999999900002","NaN","InverseCDF","27.630661504134061","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","0.99999999999900002","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","0.99999999999900002","NaN","Gradient2","27.630661504134061","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","9.9999999999999995e-07","NaN","0.99999999999900002","NaN","Gradient3","-381.73024341392278","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","support","0","1","9.9999999999999995e-07","NaN","NaN","NaN","Minimum","0","0","1.9999999999999999e-11","0","mathematical-support","finite" +"GPA","support","0","1","9.9999999999999995e-07","NaN","NaN","NaN","Maximum","1000000","0","1.9999999999999999e-11","0","mathematical-support","finite" +"GPA","boundary-and-evaluation","0","1","9.9999999999999995e-07","0","NaN","NaN","LogPDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","9.9999999999999995e-07","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","9.9999999999999995e-07","0","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","9.9999999999999995e-07","1000000","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","9.9999999999999995e-07","1000000","NaN","NaN","LogCDF","0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","9.9999999999999995e-07","1000000","NaN","NaN","LogCCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","9.9999999999999995e-07","0.10000000000000001","NaN","NaN","LogPDF","-0.099999904999995337","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","9.9999999999999995e-07","0.10000000000000001","NaN","NaN","LogCDF","-2.352168413502429","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","9.9999999999999995e-07","0.10000000000000001","NaN","NaN","LogCCDF","-0.10000000500000034","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","defining-quantile","0","1","0.20000000000000001","NaN","9.9999999999999998e-13","NaN","InverseCDF","1.000000000000401e-12","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","9.9999999999999998e-13","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","9.9999999999999998e-13","NaN","Gradient2","1.0000000000004e-12","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","9.9999999999999998e-13","NaN","Gradient3","-5.0000000000043324e-25","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","0.20000000000000001","NaN","0.01","NaN","InverseCDF","0.010040241692871","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","0.01","NaN","Gradient2","0.010040241692870997","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","0.01","NaN","Gradient3","-5.0436997911460086e-05","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","0.20000000000000001","NaN","0.5","NaN","InverseCDF","0.64724718351937927","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","0.5","NaN","Gradient2","0.64724718351937927","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","0.5","NaN","Gradient3","-0.2191375751789931","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","0.20000000000000001","NaN","0.98999999999999999","NaN","InverseCDF","3.0094641472325132","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","0.98999999999999999","NaN","Gradient2","3.0094641472325132","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","0.98999999999999999","NaN","Gradient3","-5.8805643728573562","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","0.20000000000000001","NaN","0.99999999999900002","NaN","InverseCDF","4.9800947295412579","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","0.99999999999900002","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","0.99999999999900002","NaN","Gradient2","4.9800947295412579","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0.20000000000000001","NaN","0.99999999999900002","NaN","Gradient3","-24.350470258998826","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","support","0","1","0.20000000000000001","NaN","NaN","NaN","Minimum","0","0","1.9999999999999999e-11","0","mathematical-support","finite" +"GPA","support","0","1","0.20000000000000001","NaN","NaN","NaN","Maximum","5","0","1.9999999999999999e-11","0","mathematical-support","finite" +"GPA","boundary-and-evaluation","0","1","0.20000000000000001","0","NaN","NaN","LogPDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","0.20000000000000001","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","0.20000000000000001","0","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","0.20000000000000001","5","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","0.20000000000000001","5","NaN","NaN","LogCDF","0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","0.20000000000000001","5","NaN","NaN","LogCCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","0.20000000000000001","0.10000000000000001","NaN","NaN","LogPDF","-0.080810829270077808","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","0.20000000000000001","0.10000000000000001","NaN","NaN","LogCDF","-2.3425823943338893","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","0.20000000000000001","0.10000000000000001","NaN","NaN","LogCCDF","-0.10101353658759725","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","defining-quantile","0","1","0.5","NaN","9.9999999999999998e-13","NaN","InverseCDF","1.000000000000251e-12","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","0.5","NaN","9.9999999999999998e-13","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0.5","NaN","9.9999999999999998e-13","NaN","Gradient2","1.00000000000025e-12","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0.5","NaN","9.9999999999999998e-13","NaN","Gradient3","-5.0000000000033332e-25","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","0.5","NaN","0.01","NaN","InverseCDF","0.010025125786760096","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","0.5","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0.5","NaN","0.01","NaN","Gradient2","0.010025125786760092","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0.5","NaN","0.01","NaN","Gradient3","-5.0335747647837288e-05","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","0.5","NaN","0.5","NaN","InverseCDF","0.58578643762690497","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","0.5","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0.5","NaN","0.5","NaN","Gradient2","0.58578643762690497","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0.5","NaN","0.5","NaN","Gradient3","-0.19131473178526276","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","0.5","NaN","0.98999999999999999","NaN","InverseCDF","1.8","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","0.5","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0.5","NaN","0.98999999999999999","NaN","Gradient2","1.8","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0.5","NaN","0.98999999999999999","NaN","Gradient3","-2.6789659628023812","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","0.5","NaN","0.99999999999900002","NaN","InverseCDF","1.9999980000221218","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","0.5","NaN","0.99999999999900002","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0.5","NaN","0.99999999999900002","NaN","Gradient2","1.9999980000221218","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","0.5","NaN","0.99999999999900002","NaN","Gradient3","-3.999940738569014","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","support","0","1","0.5","NaN","NaN","NaN","Minimum","0","0","1.9999999999999999e-11","0","mathematical-support","finite" +"GPA","support","0","1","0.5","NaN","NaN","NaN","Maximum","2","0","1.9999999999999999e-11","0","mathematical-support","finite" +"GPA","boundary-and-evaluation","0","1","0.5","0","NaN","NaN","LogPDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","0.5","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","0.5","0","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","0.5","2","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","0.5","2","NaN","NaN","LogCDF","0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","0.5","2","NaN","NaN","LogCCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","0.5","0.10000000000000001","NaN","NaN","LogPDF","-0.051293294387550536","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","0.5","0.10000000000000001","NaN","NaN","LogCDF","-2.3279029009783354","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","0.5","0.10000000000000001","NaN","NaN","LogCCDF","-0.10258658877510107","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","defining-quantile","0","1","1","NaN","9.9999999999999998e-13","NaN","InverseCDF","1.000000000000001e-12","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","1","NaN","9.9999999999999998e-13","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","1","NaN","9.9999999999999998e-13","NaN","Gradient2","9.9999999999999998e-13","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","1","NaN","9.9999999999999998e-13","NaN","Gradient3","-5.0000000000016664e-25","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","1","NaN","0.01","NaN","InverseCDF","0.010000000000000005","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","1","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","1","NaN","0.01","NaN","Gradient2","0.010000000000000002","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","1","NaN","0.01","NaN","Gradient3","-5.0167505033574472e-05","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","1","NaN","0.5","NaN","InverseCDF","0.5","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","1","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","1","NaN","0.5","NaN","Gradient2","0.5","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","1","NaN","0.5","NaN","Gradient3","-0.15342640972002733","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","1","NaN","0.98999999999999999","NaN","InverseCDF","0.98999999999999999","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","1","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","1","NaN","0.98999999999999999","NaN","Gradient2","0.98999999999999999","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","1","NaN","0.98999999999999999","NaN","Gradient3","-0.94394829814011916","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","1","NaN","0.99999999999900002","NaN","InverseCDF","0.99999999999900002","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","1","NaN","0.99999999999900002","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","1","NaN","0.99999999999900002","NaN","Gradient2","0.99999999999900002","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","1","NaN","0.99999999999900002","NaN","Gradient3","-0.99999999997136968","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","support","0","1","1","NaN","NaN","NaN","Minimum","0","0","1.9999999999999999e-11","0","mathematical-support","finite" +"GPA","support","0","1","1","NaN","NaN","NaN","Maximum","1","0","1.9999999999999999e-11","0","mathematical-support","finite" +"GPA","boundary-and-evaluation","0","1","1","0","NaN","NaN","LogPDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","1","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","1","0","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","1","1","NaN","NaN","LogPDF","0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","1","1","NaN","NaN","LogCDF","0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","1","1","NaN","NaN","LogCCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","1","0.10000000000000001","NaN","NaN","LogPDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","1","0.10000000000000001","NaN","NaN","LogCDF","-2.3025850929940455","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","1","0.10000000000000001","NaN","NaN","LogCCDF","-0.10536051565782631","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","defining-quantile","0","1","2","NaN","9.9999999999999998e-13","NaN","InverseCDF","9.9999999999950097e-13","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","2","NaN","9.9999999999999998e-13","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","2","NaN","9.9999999999999998e-13","NaN","Gradient2","9.9999999999949996e-13","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","2","NaN","9.9999999999999998e-13","NaN","Gradient3","-4.9999999999983337e-25","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","2","NaN","0.01","NaN","InverseCDF","0.0099500000000000057","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","2","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","2","NaN","0.01","NaN","Gradient2","0.0099500000000000022","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","2","NaN","0.01","NaN","Gradient3","-4.983291499161954e-05","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","2","NaN","0.5","NaN","InverseCDF","0.375","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","2","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","2","NaN","0.5","NaN","Gradient2","0.375","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","2","NaN","0.5","NaN","Gradient3","-0.10085660243000685","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","2","NaN","0.98999999999999999","NaN","InverseCDF","0.49995000000000001","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","2","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","2","NaN","0.98999999999999999","NaN","Gradient2","0.49995000000000001","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","2","NaN","0.98999999999999999","NaN","Gradient3","-0.24974474149070053","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","defining-quantile","0","1","2","NaN","0.99999999999900002","NaN","InverseCDF","0.49999999999999994","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","analytic-quantile-gradient","0","1","2","NaN","0.99999999999900002","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","2","NaN","0.99999999999900002","NaN","Gradient2","0.49999999999999994","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","analytic-quantile-gradient","0","1","2","NaN","0.99999999999900002","NaN","Gradient3","-0.24999999999999956","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","support","0","1","2","NaN","NaN","NaN","Minimum","0","0","1.9999999999999999e-11","0","mathematical-support","finite" +"GPA","support","0","1","2","NaN","NaN","NaN","Maximum","0.5","0","1.9999999999999999e-11","0","mathematical-support","finite" +"GPA","boundary-and-evaluation","0","1","2","0","NaN","NaN","LogPDF","0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","2","0","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","2","0","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","2","0.5","NaN","NaN","LogPDF","Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","2","0.5","NaN","NaN","LogCDF","0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","2","0.5","NaN","NaN","LogCCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"GPA","boundary-and-evaluation","0","1","2","0.10000000000000001","NaN","NaN","LogPDF","0.11157177565710488","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","2","0.10000000000000001","NaN","NaN","LogCDF","-2.2483544313958608","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"GPA","boundary-and-evaluation","0","1","2","0.10000000000000001","NaN","NaN","LogCCDF","-0.11157177565710488","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"Gumbel","defining-quantile","0","1","0","NaN","9.9999999999999998e-13","NaN","InverseCDF","-3.3189390950359559","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"Gumbel","analytic-quantile-gradient","0","1","0","NaN","9.9999999999999998e-13","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Gumbel","analytic-quantile-gradient","0","1","0","NaN","9.9999999999999998e-13","NaN","Gradient2","-3.3189390950359559","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Gumbel","defining-quantile","0","1","0","NaN","0.01","NaN","InverseCDF","-1.5271796258079011","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"Gumbel","analytic-quantile-gradient","0","1","0","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Gumbel","analytic-quantile-gradient","0","1","0","NaN","0.01","NaN","Gradient2","-1.5271796258079011","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Gumbel","defining-quantile","0","1","0","NaN","0.5","NaN","InverseCDF","0.36651292058166435","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"Gumbel","analytic-quantile-gradient","0","1","0","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Gumbel","analytic-quantile-gradient","0","1","0","NaN","0.5","NaN","Gradient2","0.36651292058166435","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Gumbel","defining-quantile","0","1","0","NaN","0.98999999999999999","NaN","InverseCDF","4.6001492267765789","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"Gumbel","analytic-quantile-gradient","0","1","0","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Gumbel","analytic-quantile-gradient","0","1","0","NaN","0.98999999999999999","NaN","Gradient2","4.6001492267765789","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Gumbel","defining-quantile","0","1","0","NaN","0.99999999999900002","NaN","InverseCDF","27.631043237892857","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"Gumbel","analytic-quantile-gradient","0","1","0","NaN","0.99999999999900002","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Gumbel","analytic-quantile-gradient","0","1","0","NaN","0.99999999999900002","NaN","Gradient2","27.631043237892857","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"Gumbel","support","0","1","0","NaN","NaN","NaN","Minimum","-Inf","0","1.9999999999999999e-11","0","mathematical-support","infinite" +"Gumbel","support","0","1","0","NaN","NaN","NaN","Maximum","Inf","0","1.9999999999999999e-11","0","mathematical-support","infinite" +"Gumbel","boundary-and-evaluation","0","1","0","-Inf","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"Gumbel","boundary-and-evaluation","0","1","0","-Inf","NaN","NaN","LogCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"Gumbel","boundary-and-evaluation","0","1","0","-Inf","NaN","NaN","LogCCDF","0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"Gumbel","boundary-and-evaluation","0","1","0","Inf","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"Gumbel","boundary-and-evaluation","0","1","0","Inf","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"Gumbel","boundary-and-evaluation","0","1","0","Inf","NaN","NaN","LogCCDF","-Inf","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","infinite" +"Gumbel","boundary-and-evaluation","0","1","0","-10","NaN","NaN","LogPDF","-22016.465794806718","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"Gumbel","boundary-and-evaluation","0","1","0","-10","NaN","NaN","LogCDF","-22026.465794806718","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"Gumbel","boundary-and-evaluation","0","1","0","-10","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"Gumbel","boundary-and-evaluation","0","1","0","0","NaN","NaN","LogPDF","-1","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"Gumbel","boundary-and-evaluation","0","1","0","0","NaN","NaN","LogCDF","-1","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"Gumbel","boundary-and-evaluation","0","1","0","0","NaN","NaN","LogCCDF","-0.45867514538708193","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"Gumbel","boundary-and-evaluation","0","1","0","10","NaN","NaN","LogPDF","-10.000045399929762","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"Gumbel","boundary-and-evaluation","0","1","0","10","NaN","NaN","LogCDF","-4.5399929762484854e-05","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"Gumbel","boundary-and-evaluation","0","1","0","10","NaN","NaN","LogCCDF","-10.000022699879","0","1.9999999999999999e-11","0","defining-density-and-one-sided-limits","finite" +"Exponential","subnormal-scale-log-density","0","9.9998886718268301e-321","0","9.9998886718268301e-321","NaN","NaN","LogPDF","735.82724089097394","1.9999999999999999e-11","1.9999999999999999e-11","0","unit-exponential-density-plus-log-scale","finite" +"Weibull","subnormal-scale-log-density","0","9.9998886718268301e-321","1","9.9998886718268301e-321","NaN","NaN","LogPDF","735.82724089097394","1.9999999999999999e-11","1.9999999999999999e-11","0","unit-exponential-density-plus-log-scale","finite" +"GEV","overflowing-tail-transform","0","9.9999999999999998e-201","-20","9.9999999999999997e+199","NaN","NaN","LogPDF","-509.7142393459219","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GEV","overflowing-tail-transform","0","9.9999999999999998e-201","-20","9.9999999999999997e+199","NaN","NaN","LogCDF","-8.6089165933172952e-21","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GEV","overflowing-tail-transform","0","9.9999999999999998e-201","-20","9.9999999999999997e+199","NaN","NaN","LogCCDF","-46.201488473558619","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GEV","overflowing-tail-transform","0","1","-20","1e+308","NaN","NaN","LogPDF","-747.80153796150603","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GEV","overflowing-tail-transform","0","1","-20","1e+308","NaN","NaN","LogCDF","-3.4272714264966212e-16","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GEV","overflowing-tail-transform","0","1","-20","1e+308","NaN","NaN","LogCCDF","-35.609597045786003","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GEV","overflowing-tail-transform","-1e+308","9.9999999999999998e-201","-20","1e+308","NaN","NaN","LogPDF","-771.55519343103435","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GEV","overflowing-tail-transform","-1e+308","9.9999999999999998e-201","-20","1e+308","NaN","NaN","LogCDF","-3.3105259799391796e-26","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GEV","overflowing-tail-transform","-1e+308","9.9999999999999998e-201","-20","1e+308","NaN","NaN","LogCCDF","-58.670105334754453","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GEV","overflowing-tail-transform","0","9.9999999999999998e-201","-9.9999999999999998e-201","9.9999999999999997e+199","NaN","NaN","LogPDF","-4.6051701859880918e+202","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GEV","overflowing-tail-transform","0","9.9999999999999998e-201","-9.9999999999999998e-201","9.9999999999999997e+199","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GEV","overflowing-tail-transform","0","9.9999999999999998e-201","-9.9999999999999998e-201","9.9999999999999997e+199","NaN","NaN","LogCCDF","-4.6051701859880918e+202","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GEV","overflowing-tail-transform","0","9.9999999999999998e-201","0","9.9999999999999997e+199","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","infinite" +"GEV","overflowing-tail-transform","0","9.9999999999999998e-201","0","9.9999999999999997e+199","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GEV","overflowing-tail-transform","0","9.9999999999999998e-201","0","9.9999999999999997e+199","NaN","NaN","LogCCDF","-Inf","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","infinite" +"GEV","overflowing-tail-transform","0","9.9999999999999998e-201","20","-9.9999999999999997e+199","NaN","NaN","LogPDF","-1.1615863496415494e+20","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GEV","overflowing-tail-transform","0","9.9999999999999998e-201","20","-9.9999999999999997e+199","NaN","NaN","LogCDF","-1.1615863496415494e+20","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GEV","overflowing-tail-transform","0","9.9999999999999998e-201","20","-9.9999999999999997e+199","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GEV","overflowing-tail-transform","1e+308","9.9999999999999998e-201","20","-1e+308","NaN","NaN","LogPDF","-3.0206680329944784e+25","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GEV","overflowing-tail-transform","1e+308","9.9999999999999998e-201","20","-1e+308","NaN","NaN","LogCDF","-3.0206680329944784e+25","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GEV","overflowing-tail-transform","1e+308","9.9999999999999998e-201","20","-1e+308","NaN","NaN","LogCCDF","-0","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GPA","overflowing-tail-transform","0","9.9999999999999998e-201","-20","9.9999999999999997e+199","NaN","NaN","LogPDF","-509.7142393459219","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GPA","overflowing-tail-transform","0","9.9999999999999998e-201","-20","9.9999999999999997e+199","NaN","NaN","LogCDF","-8.6089165933172952e-21","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GPA","overflowing-tail-transform","0","9.9999999999999998e-201","-20","9.9999999999999997e+199","NaN","NaN","LogCCDF","-46.201488473558619","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GPA","overflowing-tail-transform","0","1","-20","1e+308","NaN","NaN","LogPDF","-747.80153796150603","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GPA","overflowing-tail-transform","0","1","-20","1e+308","NaN","NaN","LogCDF","-3.4272714264966217e-16","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GPA","overflowing-tail-transform","0","1","-20","1e+308","NaN","NaN","LogCCDF","-35.609597045786003","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GPA","overflowing-tail-transform","-1e+308","9.9999999999999998e-201","-20","1e+308","NaN","NaN","LogPDF","-771.55519343103435","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GPA","overflowing-tail-transform","-1e+308","9.9999999999999998e-201","-20","1e+308","NaN","NaN","LogCDF","-3.3105259799391796e-26","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GPA","overflowing-tail-transform","-1e+308","9.9999999999999998e-201","-20","1e+308","NaN","NaN","LogCCDF","-58.670105334754453","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GPA","overflowing-tail-transform","0","9.9999999999999998e-201","-9.9999999999999998e-201","9.9999999999999997e+199","NaN","NaN","LogPDF","-4.6051701859880918e+202","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GPA","overflowing-tail-transform","0","9.9999999999999998e-201","-9.9999999999999998e-201","9.9999999999999997e+199","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GPA","overflowing-tail-transform","0","9.9999999999999998e-201","-9.9999999999999998e-201","9.9999999999999997e+199","NaN","NaN","LogCCDF","-4.6051701859880918e+202","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GPA","overflowing-tail-transform","0","9.9999999999999998e-201","0","9.9999999999999997e+199","NaN","NaN","LogPDF","-Inf","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","infinite" +"GPA","overflowing-tail-transform","0","9.9999999999999998e-201","0","9.9999999999999997e+199","NaN","NaN","LogCDF","-0","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","finite" +"GPA","overflowing-tail-transform","0","9.9999999999999998e-201","0","9.9999999999999997e+199","NaN","NaN","LogCCDF","-Inf","0","1.9999999999999999e-11","0","R-defining-log-Hosking-physical-coordinates","infinite" +"Weibull","MLE-mismatched-range-variance","0","1e+100","1e+100","NaN","0.5","100","QuantileVariance","0.013787326478714588","0","1.9999999999999999e-11","0","R-analytical-Weibull-variance-retained-coefficients","finite" +"Gamma","MLE-large-shape-Fisher-residual","0","1","10000000000000000","NaN","NaN","100","Covariance11","0.019999999999999993","0","2.0000000000000001e-10","0","R-integrate-scaled-Fisher-residual-kernel","finite" +"Gamma","MLE-large-shape-Fisher-residual","0","1","10000000000000000","NaN","NaN","100","Covariance12","-200000000000000.62","0","2.0000000000000001e-10","0","R-integrate-scaled-Fisher-residual-kernel","finite" +"Gamma","MLE-large-shape-Fisher-residual","0","1","10000000000000000","NaN","NaN","100","Covariance22","2.0000000000000273e+30","0","2.0000000000000001e-10","0","R-integrate-scaled-Fisher-residual-kernel","finite" +"Gamma","MLE-large-shape-Fisher-residual","0","0.99999999999999998e+150","10000000000000000","NaN","NaN","100","Covariance11","1.999999999999881e+298","0","2.0000000000000001e-10","0","R-integrate-scaled-Fisher-residual-kernel","finite" +"Gamma","MLE-large-shape-Fisher-residual","0","0.99999999999999998e+150","10000000000000000","NaN","NaN","100","Covariance12","-2.0000000000000537e+164","0","2.0000000000000001e-10","0","R-integrate-scaled-Fisher-residual-kernel","finite" +"Gamma","MLE-large-shape-Fisher-residual","0","0.99999999999999998e+150","10000000000000000","NaN","NaN","100","Covariance22","2.0000000000000273e+30","0","2.0000000000000001e-10","0","R-integrate-scaled-Fisher-residual-kernel","finite" +"Gamma","MLE-large-shape-Fisher-residual","0","1e-150","10000000000000000","NaN","NaN","100","Covariance11","1.9999999999999756e-302","0","2.0000000000000001e-10","0","R-integrate-scaled-Fisher-residual-kernel","finite" +"Gamma","MLE-large-shape-Fisher-residual","0","1e-150","10000000000000000","NaN","NaN","100","Covariance12","-2.0000000000001008e-136","0","2.0000000000000001e-10","0","R-integrate-scaled-Fisher-residual-kernel","finite" +"Gamma","MLE-large-shape-Fisher-residual","0","1e-150","10000000000000000","NaN","NaN","100","Covariance22","2.0000000000000273e+30","0","2.0000000000000001e-10","0","R-integrate-scaled-Fisher-residual-kernel","finite" +"Gamma","MLE-large-shape-Fisher-residual","0","1","1e+155","NaN","NaN","2000000000","Covariance11","9.9999999999999717e-10","0","2.0000000000000001e-10","0","R-integrate-scaled-Fisher-residual-kernel","finite" +"Gamma","MLE-large-shape-Fisher-residual","0","1","1e+155","NaN","NaN","2000000000","Covariance12","-1.0000000000000263e+146","0","2.0000000000000001e-10","0","R-integrate-scaled-Fisher-residual-kernel","finite" +"Gamma","MLE-large-shape-Fisher-residual","0","1","1e+155","NaN","NaN","2000000000","Covariance22","9.9999999999996674e+300","0","2.0000000000000001e-10","0","R-integrate-scaled-Fisher-residual-kernel","finite" +"Gamma","MLE-large-shape-median-variance","0","1","10000000000000000","NaN","0.5","100","QuantileVariance","100000000000000","0","2.0000000000000001e-10","0","R-defining-concentrated-Gamma-median-variance-limit","finite" +"Gamma","MoM-large-shape-median-variance","0","1","10000000000000000","NaN","0.5","100","QuantileVariance","100000000000000","0","2.0000000000000001e-10","0","R-defining-concentrated-Gamma-median-variance-limit","finite" +"Weibull","scale-rescued-median","0","9.9999999999999997e+199","0.00040000000000000002","NaN","0.5","NaN","Median","1.157850834101045e-198","0","1.9999999999999999e-11","0","R-defining-log-Weibull-median","finite" +"GPA","large-positive-shape-scaled-higher-moments","0","1","9.9999999999999997e+199","NaN","NaN","NaN","Skewness","-9.4280904158206342e+99","0","1.9999999999999999e-11","0","R-Hosking-rational-moments-in-reciprocal-shape","finite" +"GPA","large-positive-shape-scaled-higher-moments","0","1","9.9999999999999997e+199","NaN","NaN","NaN","Kurtosis","9.9999999999999997e+199","0","1.9999999999999999e-11","0","R-Hosking-rational-moments-in-reciprocal-shape","finite" +"GPA","large-positive-shape-scaled-higher-moments","0","1","1e+308","NaN","NaN","NaN","Skewness","-9.4280904158206346e+153","0","1.9999999999999999e-11","0","R-Hosking-rational-moments-in-reciprocal-shape","finite" +"GPA","large-positive-shape-scaled-higher-moments","0","1","1e+308","NaN","NaN","NaN","Kurtosis","1e+308","0","1.9999999999999999e-11","0","R-Hosking-rational-moments-in-reciprocal-shape","finite" +"GEV","nonzero-small-shape-support","0","1","-9.9999999999999998e-13","NaN","NaN","NaN","Minimum","-1000000000000","0","1.9999999999999999e-11","0","exact-nonzero-shape-support","finite" +"GEV","nonzero-small-shape-support","0","1","-9.9999999999999998e-13","NaN","NaN","NaN","Maximum","Inf","0","1.9999999999999999e-11","0","exact-nonzero-shape-support","infinite" +"GEV","nonzero-small-shape-quantile","0","1","-9.9999999999999998e-13","NaN","0.01","NaN","InverseCDF","-1.5271796258067349","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","nonzero-small-shape-gradient","0","1","-9.9999999999999998e-13","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","-9.9999999999999998e-13","NaN","0.01","NaN","Gradient2","-1.5271796258067349","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","-9.9999999999999998e-13","NaN","0.01","NaN","Gradient3","-1.1661388047401933","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-quantile","0","1","-9.9999999999999998e-13","NaN","0.5","NaN","InverseCDF","0.36651292058173146","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","nonzero-small-shape-gradient","0","1","-9.9999999999999998e-13","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","-9.9999999999999998e-13","NaN","0.5","NaN","Gradient2","0.36651292058173146","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","-9.9999999999999998e-13","NaN","0.5","NaN","Gradient3","-0.067165860476667102","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-quantile","0","1","-9.9999999999999998e-13","NaN","0.98999999999999999","NaN","InverseCDF","4.6001492267871598","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","nonzero-small-shape-gradient","0","1","-9.9999999999999998e-13","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","-9.9999999999999998e-13","NaN","0.98999999999999999","NaN","Gradient2","4.6001492267871598","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","-9.9999999999999998e-13","NaN","0.98999999999999999","NaN","Gradient3","-10.580686454339029","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-support","0","1","9.9999999999999998e-13","NaN","NaN","NaN","Minimum","-Inf","0","1.9999999999999999e-11","0","exact-nonzero-shape-support","infinite" +"GEV","nonzero-small-shape-support","0","1","9.9999999999999998e-13","NaN","NaN","NaN","Maximum","1000000000000","0","1.9999999999999999e-11","0","exact-nonzero-shape-support","finite" +"GEV","nonzero-small-shape-quantile","0","1","9.9999999999999998e-13","NaN","0.01","NaN","InverseCDF","-1.5271796258090673","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","nonzero-small-shape-gradient","0","1","9.9999999999999998e-13","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","9.9999999999999998e-13","NaN","0.01","NaN","Gradient2","-1.5271796258090673","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","9.9999999999999998e-13","NaN","0.01","NaN","Gradient3","-1.1661388047425678","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-quantile","0","1","9.9999999999999998e-13","NaN","0.5","NaN","InverseCDF","0.36651292058159718","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","nonzero-small-shape-gradient","0","1","9.9999999999999998e-13","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","9.9999999999999998e-13","NaN","0.5","NaN","Gradient2","0.36651292058159718","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","9.9999999999999998e-13","NaN","0.5","NaN","Gradient3","-0.067165860476634281","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-quantile","0","1","9.9999999999999998e-13","NaN","0.98999999999999999","NaN","InverseCDF","4.600149226765998","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","nonzero-small-shape-gradient","0","1","9.9999999999999998e-13","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","9.9999999999999998e-13","NaN","0.98999999999999999","NaN","Gradient2","4.600149226765998","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","9.9999999999999998e-13","NaN","0.98999999999999999","NaN","Gradient3","-10.580686454274129","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-support","0","1","-1e-08","NaN","NaN","NaN","Minimum","-100000000","0","1.9999999999999999e-11","0","exact-nonzero-shape-support","finite" +"GEV","nonzero-small-shape-support","0","1","-1e-08","NaN","NaN","NaN","Maximum","Inf","0","1.9999999999999999e-11","0","exact-nonzero-shape-support","infinite" +"GEV","nonzero-small-shape-quantile","0","1","-1e-08","NaN","0.01","NaN","InverseCDF","-1.527179614146513","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","nonzero-small-shape-gradient","0","1","-1e-08","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","-1e-08","NaN","0.01","NaN","Gradient2","-1.527179614146513","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","-1e-08","NaN","0.01","NaN","Gradient3","-1.166138792868691","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-quantile","0","1","-1e-08","NaN","0.5","NaN","InverseCDF","0.36651292125332291","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","nonzero-small-shape-gradient","0","1","-1e-08","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","-1e-08","NaN","0.5","NaN","Gradient2","0.36651292125332291","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","-1e-08","NaN","0.5","NaN","Gradient3","-0.06716586064076506","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-quantile","0","1","-1e-08","NaN","0.98999999999999999","NaN","InverseCDF","4.6001493325834444","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","nonzero-small-shape-gradient","0","1","-1e-08","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","-1e-08","NaN","0.98999999999999999","NaN","Gradient2","4.6001493325834444","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","-1e-08","NaN","0.98999999999999999","NaN","Gradient3","-10.580686778791494","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-support","0","1","1e-08","NaN","NaN","NaN","Minimum","-Inf","0","1.9999999999999999e-11","0","exact-nonzero-shape-support","infinite" +"GEV","nonzero-small-shape-support","0","1","1e-08","NaN","NaN","NaN","Maximum","100000000","0","1.9999999999999999e-11","0","exact-nonzero-shape-support","finite" +"GEV","nonzero-small-shape-quantile","0","1","1e-08","NaN","0.01","NaN","InverseCDF","-1.5271796374692892","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","nonzero-small-shape-gradient","0","1","1e-08","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","1e-08","NaN","0.01","NaN","Gradient2","-1.5271796374692892","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","1e-08","NaN","0.01","NaN","Gradient3","-1.16613881661407","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-quantile","0","1","1e-08","NaN","0.5","NaN","InverseCDF","0.36651291991000573","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","nonzero-small-shape-gradient","0","1","1e-08","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","1e-08","NaN","0.5","NaN","Gradient2","0.36651291991000573","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","1e-08","NaN","0.5","NaN","Gradient3","-0.067165860312536324","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-quantile","0","1","1e-08","NaN","0.98999999999999999","NaN","InverseCDF","4.6001491209697161","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","nonzero-small-shape-gradient","0","1","1e-08","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","1e-08","NaN","0.98999999999999999","NaN","Gradient2","4.6001491209697161","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","1e-08","NaN","0.98999999999999999","NaN","Gradient3","-10.580686129821673","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-support","0","1","-0.0001","NaN","NaN","NaN","Minimum","-10000","0","1.9999999999999999e-11","0","exact-nonzero-shape-support","finite" +"GEV","nonzero-small-shape-support","0","1","-0.0001","NaN","NaN","NaN","Maximum","Inf","0","1.9999999999999999e-11","0","exact-nonzero-shape-support","infinite" +"GEV","nonzero-small-shape-quantile","0","1","-0.0001","NaN","0.01","NaN","InverseCDF","-1.5270630178635451","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","nonzero-small-shape-gradient","0","1","-0.0001","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","-0.0001","NaN","0.01","NaN","Gradient2","-1.5270630178635451","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","-0.0001","NaN","0.01","NaN","Gradient3","-1.1660200846456046","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-quantile","0","1","-0.0001","NaN","0.5","NaN","InverseCDF","0.36651963724976994","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","nonzero-small-shape-gradient","0","1","-0.0001","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","-0.0001","NaN","0.5","NaN","Gradient2","0.36651963724976994","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","-0.0001","NaN","0.5","NaN","Gradient3","-0.067167501642919625","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-quantile","0","1","-0.0001","NaN","0.98999999999999999","NaN","InverseCDF","4.6012074576831248","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","nonzero-small-shape-gradient","0","1","-0.0001","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","-0.0001","NaN","0.98999999999999999","NaN","Gradient2","4.6012074576831248","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","-0.0001","NaN","0.98999999999999999","NaN","Gradient3","-10.583931863237312","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-support","0","1","0.0001","NaN","NaN","NaN","Minimum","-Inf","0","1.9999999999999999e-11","0","exact-nonzero-shape-support","infinite" +"GEV","nonzero-small-shape-support","0","1","0.0001","NaN","NaN","NaN","Maximum","10000","0","1.9999999999999999e-11","0","exact-nonzero-shape-support","finite" +"GEV","nonzero-small-shape-quantile","0","1","0.0001","NaN","0.01","NaN","InverseCDF","-1.5272962456249466","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","nonzero-small-shape-gradient","0","1","0.0001","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","0.0001","NaN","0.01","NaN","Gradient2","-1.5272962456249466","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","0.0001","NaN","0.01","NaN","Gradient3","-1.1662575384359533","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-quantile","0","1","0.0001","NaN","0.5","NaN","InverseCDF","0.36650620407767309","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","nonzero-small-shape-gradient","0","1","0.0001","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","0.0001","NaN","0.5","NaN","Gradient2","0.36650620407767309","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","0.0001","NaN","0.5","NaN","Gradient3","-0.067164219355494298","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-quantile","0","1","0.0001","NaN","0.98999999999999999","NaN","InverseCDF","4.5990913203549466","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GEV","nonzero-small-shape-gradient","0","1","0.0001","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","0.0001","NaN","0.98999999999999999","NaN","Gradient2","4.5990913203549466","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","nonzero-small-shape-gradient","0","1","0.0001","NaN","0.98999999999999999","NaN","Gradient3","-10.577442164885111","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-support","0","1","-9.9999999999999998e-13","NaN","NaN","NaN","Minimum","0","0","1.9999999999999999e-11","0","exact-nonzero-shape-support","finite" +"GPA","nonzero-small-shape-support","0","1","-9.9999999999999998e-13","NaN","NaN","NaN","Maximum","Inf","0","1.9999999999999999e-11","0","exact-nonzero-shape-support","infinite" +"GPA","nonzero-small-shape-quantile","0","1","-9.9999999999999998e-13","NaN","0.01","NaN","InverseCDF","0.010050335853501497","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","nonzero-small-shape-gradient","0","1","-9.9999999999999998e-13","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","-9.9999999999999998e-13","NaN","0.01","NaN","Gradient2","0.010050335853501494","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","-9.9999999999999998e-13","NaN","0.01","NaN","Gradient3","-5.0504625384088627e-05","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-quantile","0","1","-9.9999999999999998e-13","NaN","0.5","NaN","InverseCDF","0.69314718056018554","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","nonzero-small-shape-gradient","0","1","-9.9999999999999998e-13","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","-9.9999999999999998e-13","NaN","0.5","NaN","Gradient2","0.69314718056018554","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","-9.9999999999999998e-13","NaN","0.5","NaN","Gradient3","-0.24022650695921169","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-quantile","0","1","-9.9999999999999998e-13","NaN","0.98999999999999999","NaN","InverseCDF","4.6051701859986949","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","nonzero-small-shape-gradient","0","1","-9.9999999999999998e-13","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","-9.9999999999999998e-13","NaN","0.98999999999999999","NaN","Gradient2","4.6051701859986949","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","-9.9999999999999998e-13","NaN","0.98999999999999999","NaN","Gradient3","-10.603796220989349","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-support","0","1","9.9999999999999998e-13","NaN","NaN","NaN","Minimum","0","0","1.9999999999999999e-11","0","exact-nonzero-shape-support","finite" +"GPA","nonzero-small-shape-support","0","1","9.9999999999999998e-13","NaN","NaN","NaN","Maximum","1000000000000","0","1.9999999999999999e-11","0","exact-nonzero-shape-support","finite" +"GPA","nonzero-small-shape-quantile","0","1","9.9999999999999998e-13","NaN","0.01","NaN","InverseCDF","0.010050335853501395","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","nonzero-small-shape-gradient","0","1","9.9999999999999998e-13","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","9.9999999999999998e-13","NaN","0.01","NaN","Gradient2","0.010050335853501392","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","9.9999999999999998e-13","NaN","0.01","NaN","Gradient3","-5.0504625384087943e-05","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-quantile","0","1","9.9999999999999998e-13","NaN","0.5","NaN","InverseCDF","0.69314718055970503","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","nonzero-small-shape-gradient","0","1","9.9999999999999998e-13","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","9.9999999999999998e-13","NaN","0.5","NaN","Gradient2","0.69314718055970503","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","9.9999999999999998e-13","NaN","0.5","NaN","Gradient3","-0.24022650695898967","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-quantile","0","1","9.9999999999999998e-13","NaN","0.98999999999999999","NaN","InverseCDF","4.605170185977487","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","nonzero-small-shape-gradient","0","1","9.9999999999999998e-13","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","9.9999999999999998e-13","NaN","0.98999999999999999","NaN","Gradient2","4.605170185977487","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","9.9999999999999998e-13","NaN","0.98999999999999999","NaN","Gradient3","-10.603796220924238","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-support","0","1","-1e-08","NaN","NaN","NaN","Minimum","0","0","1.9999999999999999e-11","0","exact-nonzero-shape-support","finite" +"GPA","nonzero-small-shape-support","0","1","-1e-08","NaN","NaN","NaN","Maximum","Inf","0","1.9999999999999999e-11","0","exact-nonzero-shape-support","infinite" +"GPA","nonzero-small-shape-quantile","0","1","-1e-08","NaN","0.01","NaN","InverseCDF","0.010050335854006491","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","nonzero-small-shape-gradient","0","1","-1e-08","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","-1e-08","NaN","0.01","NaN","Gradient2","0.010050335854006488","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","-1e-08","NaN","0.01","NaN","Gradient3","-5.0504625387472192e-05","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-quantile","0","1","-1e-08","NaN","0.5","NaN","InverseCDF","0.69314718296221034","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","nonzero-small-shape-gradient","0","1","-1e-08","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","-1e-08","NaN","0.5","NaN","Gradient2","0.69314718296221034","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","-1e-08","NaN","0.5","NaN","Gradient3","-0.24022650806918286","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-quantile","0","1","-1e-08","NaN","0.98999999999999999","NaN","InverseCDF","4.6051702920260542","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","nonzero-small-shape-gradient","0","1","-1e-08","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","-1e-08","NaN","0.98999999999999999","NaN","Gradient2","4.6051702920260542","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","-1e-08","NaN","0.98999999999999999","NaN","Gradient3","-10.603796546505372","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-support","0","1","1e-08","NaN","NaN","NaN","Minimum","0","0","1.9999999999999999e-11","0","exact-nonzero-shape-support","finite" +"GPA","nonzero-small-shape-support","0","1","1e-08","NaN","NaN","NaN","Maximum","100000000","0","1.9999999999999999e-11","0","exact-nonzero-shape-support","finite" +"GPA","nonzero-small-shape-quantile","0","1","1e-08","NaN","0.01","NaN","InverseCDF","0.0100503358529964","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","nonzero-small-shape-gradient","0","1","1e-08","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","1e-08","NaN","0.01","NaN","Gradient2","0.010050335852996396","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","1e-08","NaN","0.01","NaN","Gradient3","-5.0504625380704358e-05","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-quantile","0","1","1e-08","NaN","0.5","NaN","InverseCDF","0.69314717815768023","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","nonzero-small-shape-gradient","0","1","1e-08","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","1e-08","NaN","0.5","NaN","Gradient2","0.69314717815768023","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","1e-08","NaN","0.5","NaN","Gradient3","-0.2402265058490185","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-quantile","0","1","1e-08","NaN","0.98999999999999999","NaN","InverseCDF","4.6051700799501303","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","nonzero-small-shape-gradient","0","1","1e-08","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","1e-08","NaN","0.98999999999999999","NaN","Gradient2","4.6051700799501303","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","1e-08","NaN","0.98999999999999999","NaN","Gradient3","-10.603795895408226","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-support","0","1","-0.0001","NaN","NaN","NaN","Minimum","0","0","1.9999999999999999e-11","0","exact-nonzero-shape-support","finite" +"GPA","nonzero-small-shape-support","0","1","-0.0001","NaN","NaN","NaN","Maximum","Inf","0","1.9999999999999999e-11","0","exact-nonzero-shape-support","infinite" +"GPA","nonzero-small-shape-quantile","0","1","-0.0001","NaN","0.01","NaN","InverseCDF","0.010050340903965675","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","nonzero-small-shape-gradient","0","1","-0.0001","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","-0.0001","NaN","0.01","NaN","Gradient2","0.010050340903965671","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","-0.0001","NaN","0.01","NaN","Gradient3","-5.050465922333084e-05","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-quantile","0","1","-0.0001","NaN","0.5","NaN","InverseCDF","0.69317120376569197","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","nonzero-small-shape-gradient","0","1","-0.0001","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","-0.0001","NaN","0.5","NaN","Gradient2","0.69317120376569197","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","-0.0001","NaN","0.5","NaN","Gradient3","-0.24023760806938291","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-quantile","0","1","-0.0001","NaN","0.98999999999999999","NaN","InverseCDF","4.606230728403216","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","nonzero-small-shape-gradient","0","1","-0.0001","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","-0.0001","NaN","0.98999999999999999","NaN","Gradient2","4.606230728403216","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","-0.0001","NaN","0.98999999999999999","NaN","Gradient3","-10.607052268975982","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-support","0","1","0.0001","NaN","NaN","NaN","Minimum","0","0","1.9999999999999999e-11","0","exact-nonzero-shape-support","finite" +"GPA","nonzero-small-shape-support","0","1","0.0001","NaN","NaN","NaN","Maximum","10000","0","1.9999999999999999e-11","0","exact-nonzero-shape-support","finite" +"GPA","nonzero-small-shape-quantile","0","1","0.0001","NaN","0.01","NaN","InverseCDF","0.010050330803040599","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","nonzero-small-shape-gradient","0","1","0.0001","NaN","0.01","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","0.0001","NaN","0.01","NaN","Gradient2","0.010050330803040596","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","0.0001","NaN","0.01","NaN","Gradient3","-5.0504591544871222e-05","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-quantile","0","1","0.0001","NaN","0.5","NaN","InverseCDF","0.69312315846428085","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","nonzero-small-shape-gradient","0","1","0.0001","NaN","0.5","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","0.0001","NaN","0.5","NaN","Gradient2","0.69312315846428085","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","0.0001","NaN","0.5","NaN","Gradient3","-0.24021540642590627","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-quantile","0","1","0.0001","NaN","0.98999999999999999","NaN","InverseCDF","4.6041099691215441","0","1.9999999999999999e-11","0","Hosking-quantile-expm1","finite" +"GPA","nonzero-small-shape-gradient","0","1","0.0001","NaN","0.98999999999999999","NaN","Gradient1","1","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","0.0001","NaN","0.98999999999999999","NaN","Gradient2","4.6041099691215441","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GPA","nonzero-small-shape-gradient","0","1","0.0001","NaN","0.98999999999999999","NaN","Gradient3","-10.600541297342561","0","1.9999999999999999e-11","0","defining-quantile-derivative","finite" +"GEV","full-probability-central-moments","0","1","-1","NaN","NaN","NaN","Mean","NaN","2.0000000000000001e-09","2e-08","0","R-integrate-full-probability-Hosking-quantile","undefined" +"GEV","full-probability-central-moments","0","1","-1","NaN","NaN","NaN","StandardDeviation","NaN","2.0000000000000001e-09","2e-08","0","R-integrate-full-probability-Hosking-quantile","undefined" +"GEV","full-probability-central-moments","0","1","-1","NaN","NaN","NaN","Skewness","NaN","2.0000000000000001e-09","2e-08","0","R-integrate-full-probability-Hosking-quantile","undefined" +"GEV","full-probability-central-moments","0","1","-1","NaN","NaN","NaN","Kurtosis","NaN","2.0000000000000001e-09","2e-08","0","R-integrate-full-probability-Hosking-quantile","undefined" +"GEV","full-probability-central-moments","0","1","-0.5","NaN","NaN","NaN","Mean","1.5449077018110322","2.0000000000000001e-09","2e-08","8.4051489435663631e-12","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","-0.5","NaN","NaN","NaN","StandardDeviation","NaN","2.0000000000000001e-09","2e-08","0","R-integrate-full-probability-Hosking-quantile","undefined" +"GEV","full-probability-central-moments","0","1","-0.5","NaN","NaN","NaN","Skewness","NaN","2.0000000000000001e-09","2e-08","0","R-integrate-full-probability-Hosking-quantile","undefined" +"GEV","full-probability-central-moments","0","1","-0.5","NaN","NaN","NaN","Kurtosis","NaN","2.0000000000000001e-09","2e-08","0","R-integrate-full-probability-Hosking-quantile","undefined" +"GEV","full-probability-central-moments","0","1","-0.20000000000000001","NaN","NaN","NaN","Mean","0.82114856862651697","2.0000000000000001e-09","2e-08","1.6119886247654247e-12","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","-0.20000000000000001","NaN","NaN","NaN","StandardDeviation","1.8286704356712671","2.0000000000000001e-09","2e-08","1.3161377144245581e-11","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","-0.20000000000000001","NaN","NaN","NaN","Skewness","3.5350716046207777","2.0000000000000001e-09","2e-08","3.2341297463725808e-10","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","-0.20000000000000001","NaN","NaN","NaN","Kurtosis","48.091512125808265","2.0000000000000001e-09","2e-08","2.3135910964811046e-09","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","-9.9999999999999995e-07","NaN","NaN","NaN","Mean","0.57721665395843558","2.0000000000000001e-09","2e-08","3.7821860498417878e-13","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","-9.9999999999999995e-07","NaN","NaN","NaN","StandardDeviation","1.2825515077131107","2.0000000000000001e-09","2e-08","2.1041491808360173e-12","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","-9.9999999999999995e-07","NaN","NaN","NaN","Skewness","1.1395530660293405","2.0000000000000001e-09","2e-08","7.2712265360484389e-12","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","-9.9999999999999995e-07","NaN","NaN","NaN","Kurtosis","5.4000289178409488","2.0000000000000001e-09","2e-08","2.0185211904276138e-11","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","0","NaN","NaN","NaN","Mean","0.57721566490153275","2.0000000000000001e-09","2e-08","3.7821575508640664e-13","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","0","NaN","NaN","NaN","StandardDeviation","1.2825498301618641","2.0000000000000001e-09","2e-08","2.104139811847604e-12","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","0","NaN","NaN","NaN","Skewness","1.1395470994046484","2.0000000000000001e-09","2e-08","7.2712247929737475e-12","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","0","NaN","NaN","NaN","Kurtosis","5.3999999999999924","2.0000000000000001e-09","2e-08","2.0183999963539802e-11","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","9.9999999999999995e-07","NaN","NaN","NaN","Mean","0.57721467584644504","2.0000000000000001e-09","2e-08","3.7821799600057693e-13","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","9.9999999999999995e-07","NaN","NaN","NaN","StandardDeviation","1.2825481526175599","2.0000000000000001e-09","2e-08","2.1041338243783657e-12","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","9.9999999999999995e-07","NaN","NaN","NaN","Skewness","1.1395411328045155","2.0000000000000001e-09","2e-08","7.2712272428312589e-12","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","9.9999999999999995e-07","NaN","NaN","NaN","Kurtosis","5.3999710824642673","2.0000000000000001e-09","2e-08","2.0182758226976942e-11","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","0.20000000000000001","NaN","NaN","NaN","Mean","0.40915628800119697","2.0000000000000001e-09","2e-08","3.4405020927593962e-14","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","0.20000000000000001","NaN","NaN","NaN","StandardDeviation","1.0515462184697035","2.0000000000000001e-09","2e-08","1.2919597219262743e-12","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","0.20000000000000001","NaN","NaN","NaN","Skewness","0.2541096037067474","2.0000000000000001e-09","2e-08","2.9778892228702598e-12","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","0.20000000000000001","NaN","NaN","NaN","Kurtosis","2.8802900637822013","2.0000000000000001e-09","2e-08","2.0482822097697841e-11","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","0.5","NaN","NaN","NaN","Mean","0.22754614909448395","2.0000000000000001e-09","2e-08","1.083730569000204e-13","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","0.5","NaN","NaN","NaN","StandardDeviation","0.92650275035220853","2.0000000000000001e-09","2e-08","1.0480140700234159e-12","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","0.5","NaN","NaN","NaN","Skewness","-0.63111065781893716","2.0000000000000001e-09","2e-08","6.1508114017734713e-12","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","0.5","NaN","NaN","NaN","Kurtosis","3.2450893006876376","2.0000000000000001e-09","2e-08","1.948714185929272e-11","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","1","NaN","NaN","NaN","Mean","1.3877787807814457e-17","2.0000000000000001e-09","2e-08","4.3456216117997015e-13","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","1","NaN","NaN","NaN","StandardDeviation","1","2.0000000000000001e-09","2e-08","2.2910277788350118e-12","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","1","NaN","NaN","NaN","Skewness","-1.9999999999999989","2.0000000000000001e-09","2e-08","1.1189450695626708e-11","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","1","NaN","NaN","NaN","Kurtosis","8.9999999999999805","2.0000000000000001e-09","2e-08","2.7753909478242054e-11","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","2","NaN","NaN","NaN","Mean","-0.5","2.0000000000000001e-09","2e-08","2.1135470532199398e-12","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","2","NaN","NaN","NaN","StandardDeviation","2.2360679774997885","2.0000000000000001e-09","2e-08","4.6861217262880285e-12","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","2","NaN","NaN","NaN","Skewness","-6.6187612133993925","2.0000000000000001e-09","2e-08","6.9474967635165676e-12","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","full-probability-central-moments","0","1","2","NaN","NaN","NaN","Kurtosis","87.72000000000385","2.0000000000000001e-09","2e-08","2.7520457142908157e-09","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","-1","NaN","NaN","NaN","Mean","NaN","2.0000000000000001e-09","2e-08","0","R-integrate-full-probability-Hosking-quantile","undefined" +"GPA","full-probability-central-moments","0","1","-1","NaN","NaN","NaN","StandardDeviation","NaN","2.0000000000000001e-09","2e-08","0","R-integrate-full-probability-Hosking-quantile","undefined" +"GPA","full-probability-central-moments","0","1","-1","NaN","NaN","NaN","Skewness","NaN","2.0000000000000001e-09","2e-08","0","R-integrate-full-probability-Hosking-quantile","undefined" +"GPA","full-probability-central-moments","0","1","-1","NaN","NaN","NaN","Kurtosis","NaN","2.0000000000000001e-09","2e-08","0","R-integrate-full-probability-Hosking-quantile","undefined" +"GPA","full-probability-central-moments","0","1","-0.5","NaN","NaN","NaN","Mean","2","2.0000000000000001e-09","2e-08","4.565265696384349e-12","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","-0.5","NaN","NaN","NaN","StandardDeviation","NaN","2.0000000000000001e-09","2e-08","0","R-integrate-full-probability-Hosking-quantile","undefined" +"GPA","full-probability-central-moments","0","1","-0.5","NaN","NaN","NaN","Skewness","NaN","2.0000000000000001e-09","2e-08","0","R-integrate-full-probability-Hosking-quantile","undefined" +"GPA","full-probability-central-moments","0","1","-0.5","NaN","NaN","NaN","Kurtosis","NaN","2.0000000000000001e-09","2e-08","0","R-integrate-full-probability-Hosking-quantile","undefined" +"GPA","full-probability-central-moments","0","1","-0.20000000000000001","NaN","NaN","NaN","Mean","1.25","2.0000000000000001e-09","2e-08","1.0814470283319436e-12","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","-0.20000000000000001","NaN","NaN","NaN","StandardDeviation","1.6137430609198375","2.0000000000000001e-09","2e-08","1.5066632650733385e-11","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","-0.20000000000000001","NaN","NaN","NaN","Skewness","4.6475800154481721","2.0000000000000001e-09","2e-08","5.4657267380280158e-11","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","-0.20000000000000001","NaN","NaN","NaN","Kurtosis","73.799999999985346","2.0000000000000001e-09","2e-08","4.0435708115182933e-09","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","-9.9999999999999995e-07","NaN","NaN","NaN","Mean","1.0000010000010002","2.0000000000000001e-09","2e-08","4.130566198124381e-13","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","-9.9999999999999995e-07","NaN","NaN","NaN","StandardDeviation","1.0000020000034999","2.0000000000000001e-09","2e-08","2.2910337812434352e-12","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","-9.9999999999999995e-07","NaN","NaN","NaN","Skewness","2.0000060000149982","2.0000000000000001e-09","2e-08","1.1189405347825287e-11","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","-9.9999999999999995e-07","NaN","NaN","NaN","Kurtosis","9.0000480002279808","2.0000000000000001e-09","2e-08","2.7757861624410561e-11","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","0","NaN","NaN","NaN","Mean","1","2.0000000000000001e-09","2e-08","4.1304430428591162e-13","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","0","NaN","NaN","NaN","StandardDeviation","1","2.0000000000000001e-09","2e-08","2.2910252885978515e-12","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","0","NaN","NaN","NaN","Skewness","1.9999999999999989","2.0000000000000001e-09","2e-08","1.1189447935609554e-11","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","0","NaN","NaN","NaN","Kurtosis","8.9999999999999805","2.0000000000000001e-09","2e-08","2.7753954715206235e-11","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","9.9999999999999995e-07","NaN","NaN","NaN","Mean","0.99999900000100006","2.0000000000000001e-09","2e-08","4.1305241022081764e-13","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","9.9999999999999995e-07","NaN","NaN","NaN","StandardDeviation","0.99999800000350014","2.0000000000000001e-09","2e-08","2.2910143071291951e-12","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","9.9999999999999995e-07","NaN","NaN","NaN","Skewness","1.9999940000149978","2.0000000000000001e-09","2e-08","1.1189482461241584e-11","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","9.9999999999999995e-07","NaN","NaN","NaN","Kurtosis","8.9999520002279745","2.0000000000000001e-09","2e-08","2.7750025480369052e-11","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","0.20000000000000001","NaN","NaN","NaN","Mean","0.83333333333333337","2.0000000000000001e-09","2e-08","1.4950962803211255e-13","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","0.20000000000000001","NaN","NaN","NaN","StandardDeviation","0.70429521227376379","2.0000000000000001e-09","2e-08","1.1434614228302181e-12","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","0.20000000000000001","NaN","NaN","NaN","Skewness","1.183215956619923","2.0000000000000001e-09","2e-08","1.3092791461078712e-11","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","0.20000000000000001","NaN","NaN","NaN","Kurtosis","4.1999999999999993","2.0000000000000001e-09","2e-08","2.5004341171414424e-11","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","0.5","NaN","NaN","NaN","Mean","0.66666666666666685","2.0000000000000001e-09","2e-08","2.6045680832312975e-14","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","0.5","NaN","NaN","NaN","StandardDeviation","0.47140452079103168","2.0000000000000001e-09","2e-08","2.4819031531551107e-13","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","0.5","NaN","NaN","NaN","Skewness","0.5656854249492369","2.0000000000000001e-09","2e-08","7.6501829607412615e-12","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","0.5","NaN","NaN","NaN","Kurtosis","2.3999999999999995","2.0000000000000001e-09","2e-08","2.3129372252389634e-11","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","1","NaN","NaN","NaN","Mean","0.50000000000000011","2.0000000000000001e-09","2e-08","5.5511151231257843e-15","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","1","NaN","NaN","NaN","StandardDeviation","0.28867513459481287","2.0000000000000001e-09","2e-08","1.6024689053047125e-15","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","1","NaN","NaN","NaN","Skewness","-9.8004243895675532e-16","2.0000000000000001e-09","2e-08","6.5090831133615137e-16","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","1","NaN","NaN","NaN","Kurtosis","1.8000000000000003","2.0000000000000001e-09","2e-08","1.998391717839331e-14","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","2","NaN","NaN","NaN","Mean","0.33333333333333343","2.0000000000000001e-09","2e-08","2.5297381674156509e-11","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","2","NaN","NaN","NaN","StandardDeviation","0.14907119849998596","2.0000000000000001e-09","2e-08","8.2751092107358944e-16","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","2","NaN","NaN","NaN","Skewness","-0.63887656499994205","2.0000000000000001e-09","2e-08","8.4169190291953852e-14","R-integrate-full-probability-Hosking-quantile","finite" +"GPA","full-probability-central-moments","0","1","2","NaN","NaN","NaN","Kurtosis","2.1428571428571459","2.0000000000000001e-09","2e-08","1.0989778657005721e-11","R-integrate-full-probability-Hosking-quantile","finite" +"GEV","large-positive-shape-lgamma-moments","0","1","50","NaN","NaN","NaN","Mean","-6.0828186403425583e+62","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","1","50","NaN","NaN","NaN","StandardDeviation","1.9321098875989519e+77","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","1","50","NaN","NaN","NaN","Skewness","-6.3370616015546359e+25","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","1","50","NaN","NaN","NaN","Kurtosis","9.0548514656102745e+58","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","9.9999999999999998e-201","50","NaN","NaN","NaN","Mean","-6.0828186403424247e-138","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","9.9999999999999998e-201","50","NaN","NaN","NaN","StandardDeviation","1.9321098875989639e-123","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","9.9999999999999998e-201","50","NaN","NaN","NaN","Skewness","-6.3370616015546359e+25","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","9.9999999999999998e-201","50","NaN","NaN","NaN","Kurtosis","9.0548514656102745e+58","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","9.9999999999999997e+199","50","NaN","NaN","NaN","Mean","-6.0828186403423477e+262","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","9.9999999999999997e+199","50","NaN","NaN","NaN","StandardDeviation","1.9321098875988298e+277","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","9.9999999999999997e+199","50","NaN","NaN","NaN","Skewness","-6.3370616015546359e+25","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","9.9999999999999997e+199","50","NaN","NaN","NaN","Kurtosis","9.0548514656102745e+58","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","1","100","NaN","NaN","NaN","Mean","-9.3326215443944039e+155","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","1","100","NaN","NaN","NaN","StandardDeviation","2.8083053027845529e+185","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","1","100","NaN","NaN","NaN","Skewness","-1.3818792121545016e+52","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","1","100","NaN","NaN","NaN","Kurtosis","1.0295250013540998e+119","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","9.9999999999999998e-201","100","NaN","NaN","NaN","Mean","-9.3326215443940649e-45","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","9.9999999999999998e-201","100","NaN","NaN","NaN","StandardDeviation","2.808305302784431e-15","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","9.9999999999999998e-201","100","NaN","NaN","NaN","Skewness","-1.3818792121545016e+52","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","9.9999999999999998e-201","100","NaN","NaN","NaN","Kurtosis","1.0295250013540998e+119","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","9.9999999999999997e+199","100","NaN","NaN","NaN","Mean","-Inf","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","infinite" +"GEV","large-positive-shape-lgamma-moments","0","9.9999999999999997e+199","100","NaN","NaN","NaN","StandardDeviation","Inf","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","infinite" +"GEV","large-positive-shape-lgamma-moments","0","9.9999999999999997e+199","100","NaN","NaN","NaN","Skewness","-1.3818792121545016e+52","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","9.9999999999999997e+199","100","NaN","NaN","NaN","Kurtosis","1.0295250013540998e+119","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","1","200","NaN","NaN","NaN","Mean","-Inf","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","infinite" +"GEV","large-positive-shape-lgamma-moments","0","1","200","NaN","NaN","NaN","StandardDeviation","Inf","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","infinite" +"GEV","large-positive-shape-lgamma-moments","0","1","200","NaN","NaN","NaN","Skewness","-7.8102637425480772e+104","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","1","200","NaN","NaN","NaN","Kurtosis","1.8804244186832607e+239","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","9.9999999999999998e-201","200","NaN","NaN","NaN","Mean","-3.9432893368237494e+172","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","9.9999999999999998e-201","200","NaN","NaN","NaN","StandardDeviation","1.2652521769059247e+232","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","9.9999999999999998e-201","200","NaN","NaN","NaN","Skewness","-7.8102637425480772e+104","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","9.9999999999999998e-201","200","NaN","NaN","NaN","Kurtosis","1.8804244186832607e+239","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","9.9999999999999997e+199","200","NaN","NaN","NaN","Mean","-Inf","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","infinite" +"GEV","large-positive-shape-lgamma-moments","0","9.9999999999999997e+199","200","NaN","NaN","NaN","StandardDeviation","Inf","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","infinite" +"GEV","large-positive-shape-lgamma-moments","0","9.9999999999999997e+199","200","NaN","NaN","NaN","Skewness","-7.8102637425480772e+104","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"GEV","large-positive-shape-lgamma-moments","0","9.9999999999999997e+199","200","NaN","NaN","NaN","Kurtosis","1.8804244186832607e+239","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-moments","finite" +"Weibull","small-shape-lgamma-moments","0","1","0.0050000000000000001","NaN","NaN","NaN","Mean","Inf","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","infinite" +"Weibull","small-shape-lgamma-moments","0","1","0.0050000000000000001","NaN","NaN","NaN","StandardDeviation","Inf","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","infinite" +"Weibull","small-shape-lgamma-moments","0","1","0.0050000000000000001","NaN","NaN","NaN","Skewness","7.8102637425480772e+104","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","1","0.0050000000000000001","NaN","NaN","NaN","Kurtosis","1.8804244186832607e+239","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","9.9999999999999998e-201","0.0050000000000000001","NaN","NaN","NaN","Mean","7.8865786736473631e+174","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","9.9999999999999998e-201","0.0050000000000000001","NaN","NaN","NaN","StandardDeviation","2.5305043538119495e+234","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","9.9999999999999998e-201","0.0050000000000000001","NaN","NaN","NaN","Skewness","7.8102637425480772e+104","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","9.9999999999999998e-201","0.0050000000000000001","NaN","NaN","NaN","Kurtosis","1.8804244186832607e+239","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","9.9999999999999997e+199","0.0050000000000000001","NaN","NaN","NaN","Mean","Inf","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","infinite" +"Weibull","small-shape-lgamma-moments","0","9.9999999999999997e+199","0.0050000000000000001","NaN","NaN","NaN","StandardDeviation","Inf","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","infinite" +"Weibull","small-shape-lgamma-moments","0","9.9999999999999997e+199","0.0050000000000000001","NaN","NaN","NaN","Skewness","7.8102637425480772e+104","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","9.9999999999999997e+199","0.0050000000000000001","NaN","NaN","NaN","Kurtosis","1.8804244186832607e+239","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","1","0.01","NaN","NaN","NaN","Mean","9.3326215443942249e+157","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","1","0.01","NaN","NaN","NaN","StandardDeviation","2.808305302784499e+187","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","1","0.01","NaN","NaN","NaN","Skewness","1.3818792121545016e+52","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","1","0.01","NaN","NaN","NaN","Kurtosis","1.0295250013540998e+119","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","9.9999999999999998e-201","0.01","NaN","NaN","NaN","Mean","9.3326215443940188e-43","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","9.9999999999999998e-201","0.01","NaN","NaN","NaN","StandardDeviation","2.8083053027844368e-13","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","9.9999999999999998e-201","0.01","NaN","NaN","NaN","Skewness","1.3818792121545016e+52","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","9.9999999999999998e-201","0.01","NaN","NaN","NaN","Kurtosis","1.0295250013540998e+119","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","9.9999999999999997e+199","0.01","NaN","NaN","NaN","Mean","Inf","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","infinite" +"Weibull","small-shape-lgamma-moments","0","9.9999999999999997e+199","0.01","NaN","NaN","NaN","StandardDeviation","Inf","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","infinite" +"Weibull","small-shape-lgamma-moments","0","9.9999999999999997e+199","0.01","NaN","NaN","NaN","Skewness","1.3818792121545016e+52","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","9.9999999999999997e+199","0.01","NaN","NaN","NaN","Kurtosis","1.0295250013540998e+119","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","1","0.02","NaN","NaN","NaN","Mean","3.0414093201713019e+64","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","1","0.02","NaN","NaN","NaN","StandardDeviation","9.6605494379948314e+78","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","1","0.02","NaN","NaN","NaN","Skewness","6.3370616015546359e+25","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","1","0.02","NaN","NaN","NaN","Kurtosis","9.0548514656102745e+58","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","9.9999999999999998e-201","0.02","NaN","NaN","NaN","Mean","3.0414093201711485e-136","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","9.9999999999999998e-201","0.02","NaN","NaN","NaN","StandardDeviation","9.6605494379946177e-122","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","9.9999999999999998e-201","0.02","NaN","NaN","NaN","Skewness","6.3370616015546359e+25","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","9.9999999999999998e-201","0.02","NaN","NaN","NaN","Kurtosis","9.0548514656102745e+58","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","9.9999999999999997e+199","0.02","NaN","NaN","NaN","Mean","3.0414093201712828e+264","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","9.9999999999999997e+199","0.02","NaN","NaN","NaN","StandardDeviation","9.6605494379944961e+278","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","9.9999999999999997e+199","0.02","NaN","NaN","NaN","Skewness","6.3370616015546359e+25","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","small-shape-lgamma-moments","0","9.9999999999999997e+199","0.02","NaN","NaN","NaN","Kurtosis","9.0548514656102745e+58","0","2.0000000000000001e-10","0","R-lgamma-normalized-analytical-power-moments","finite" +"Weibull","large-shape-full-probability-moments","0","1","1000","NaN","NaN","NaN","Mean","0.99942377248459557","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","1","1000","NaN","NaN","NaN","StandardDeviation","0.0012808757478713505","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","1","1000","NaN","NaN","NaN","Skewness","-1.133592730660135","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","1","1000","NaN","NaN","NaN","Kurtosis","5.3712342641096749","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999998e-201","1000","NaN","NaN","NaN","Mean","9.9942377248456271e-201","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999998e-201","1000","NaN","NaN","NaN","StandardDeviation","1.2808757478713926e-203","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999998e-201","1000","NaN","NaN","NaN","Skewness","-1.133592730660135","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999998e-201","1000","NaN","NaN","NaN","Kurtosis","5.3712342641096749","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999997e+199","1000","NaN","NaN","NaN","Mean","9.9942377248460685e+199","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999997e+199","1000","NaN","NaN","NaN","StandardDeviation","1.2808757478714493e+197","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999997e+199","1000","NaN","NaN","NaN","Skewness","-1.133592730660135","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999997e+199","1000","NaN","NaN","NaN","Kurtosis","5.3712342641096749","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","1","1000000","NaN","NaN","NaN","Mean","0.99999942278532417","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","1","1000000","NaN","NaN","NaN","StandardDeviation","1.2825481526175598e-06","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","1","1000000","NaN","NaN","NaN","Skewness","-1.1395411328045153","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","1","1000000","NaN","NaN","NaN","Kurtosis","5.3999710824642673","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999998e-201","1000000","NaN","NaN","NaN","Mean","9.9999942278529606e-201","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999998e-201","1000000","NaN","NaN","NaN","StandardDeviation","1.2825481526175453e-206","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999998e-201","1000000","NaN","NaN","NaN","Skewness","-1.1395411328045153","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999998e-201","1000000","NaN","NaN","NaN","Kurtosis","5.3999710824642673","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999997e+199","1000000","NaN","NaN","NaN","Mean","9.9999942278534029e+199","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999997e+199","1000000","NaN","NaN","NaN","StandardDeviation","1.2825481526176019e+194","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999997e+199","1000000","NaN","NaN","NaN","Skewness","-1.1395411328045153","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999997e+199","1000000","NaN","NaN","NaN","Kurtosis","5.3999710824642673","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","1","1000000000000","NaN","NaN","NaN","Mean","0.99999999999942268","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","1","1000000000000","NaN","NaN","NaN","StandardDeviation","1.2825498301601897e-12","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","1","1000000000000","NaN","NaN","NaN","Skewness","-1.139547099398682","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","1","1000000000000","NaN","NaN","NaN","Kurtosis","5.3999999999710768","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999998e-201","1000000000000","NaN","NaN","NaN","Mean","9.9999999999940952e-201","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999998e-201","1000000000000","NaN","NaN","NaN","StandardDeviation","1.2825498301601794e-212","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999998e-201","1000000000000","NaN","NaN","NaN","Skewness","-1.139547099398682","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999998e-201","1000000000000","NaN","NaN","NaN","Kurtosis","5.3999999999710768","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999997e+199","1000000000000","NaN","NaN","NaN","Mean","9.9999999999945371e+199","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999997e+199","1000000000000","NaN","NaN","NaN","StandardDeviation","1.2825498301602363e+188","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999997e+199","1000000000000","NaN","NaN","NaN","Skewness","-1.139547099398682","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999997e+199","1000000000000","NaN","NaN","NaN","Kurtosis","5.3999999999710768","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","1","9.9999999999999997e+199","NaN","NaN","NaN","Mean","1","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","1","9.9999999999999997e+199","NaN","NaN","NaN","StandardDeviation","1.2825498301618551e-200","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","1","9.9999999999999997e+199","NaN","NaN","NaN","Skewness","-1.1395470994046486","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","1","9.9999999999999997e+199","NaN","NaN","NaN","Kurtosis","5.3999999999999932","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999998e-201","9.9999999999999997e+199","NaN","NaN","NaN","Mean","9.9999999999997794e-201","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999998e-201","9.9999999999999997e+199","NaN","NaN","NaN","StandardDeviation","0","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999998e-201","9.9999999999999997e+199","NaN","NaN","NaN","Skewness","-1.1395470994046486","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999998e-201","9.9999999999999997e+199","NaN","NaN","NaN","Kurtosis","5.3999999999999932","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999997e+199","9.9999999999999997e+199","NaN","NaN","NaN","Mean","1.0000000000000221e+200","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999997e+199","9.9999999999999997e+199","NaN","NaN","NaN","StandardDeviation","1.2825498301618641","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999997e+199","9.9999999999999997e+199","NaN","NaN","NaN","Skewness","-1.1395470994046486","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Weibull","large-shape-full-probability-moments","0","9.9999999999999997e+199","9.9999999999999997e+199","NaN","NaN","NaN","Kurtosis","5.3999999999999932","0","2e-08","0","R-integrate-centered-scaled-exponential-power","finite" +"Exponential","analytic-scale-separated-moments","0","1","0","NaN","NaN","NaN","Mean","1","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Exponential","analytic-scale-separated-moments","0","1","0","NaN","NaN","NaN","StandardDeviation","1","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Exponential","analytic-scale-separated-moments","0","1","0","NaN","NaN","NaN","Skewness","2","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Exponential","analytic-scale-separated-moments","0","1","0","NaN","NaN","NaN","Kurtosis","9","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Exponential","analytic-scale-separated-moments","0","9.9999999999999998e-201","0","NaN","NaN","NaN","Mean","9.9999999999999998e-201","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Exponential","analytic-scale-separated-moments","0","9.9999999999999998e-201","0","NaN","NaN","NaN","StandardDeviation","9.9999999999999998e-201","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Exponential","analytic-scale-separated-moments","0","9.9999999999999998e-201","0","NaN","NaN","NaN","Skewness","2","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Exponential","analytic-scale-separated-moments","0","9.9999999999999998e-201","0","NaN","NaN","NaN","Kurtosis","9","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Exponential","analytic-scale-separated-moments","0","9.9999999999999997e+199","0","NaN","NaN","NaN","Mean","9.9999999999999997e+199","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Exponential","analytic-scale-separated-moments","0","9.9999999999999997e+199","0","NaN","NaN","NaN","StandardDeviation","9.9999999999999997e+199","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Exponential","analytic-scale-separated-moments","0","9.9999999999999997e+199","0","NaN","NaN","NaN","Skewness","2","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Exponential","analytic-scale-separated-moments","0","9.9999999999999997e+199","0","NaN","NaN","NaN","Kurtosis","9","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","1","0.10000000000000001","NaN","NaN","NaN","Mean","0.10000000000000001","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","1","0.10000000000000001","NaN","NaN","NaN","StandardDeviation","0.31622776601683794","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","1","0.10000000000000001","NaN","NaN","NaN","Skewness","6.3245553203367582","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","1","0.10000000000000001","NaN","NaN","NaN","Kurtosis","63","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","1","1","NaN","NaN","NaN","Mean","1","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","1","1","NaN","NaN","NaN","StandardDeviation","1","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","1","1","NaN","NaN","NaN","Skewness","2","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","1","1","NaN","NaN","NaN","Kurtosis","9","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","1","2","NaN","NaN","NaN","Mean","2","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","1","2","NaN","NaN","NaN","StandardDeviation","1.4142135623730951","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","1","2","NaN","NaN","NaN","Skewness","1.4142135623730949","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","1","2","NaN","NaN","NaN","Kurtosis","6","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","1","100","NaN","NaN","NaN","Mean","100","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","1","100","NaN","NaN","NaN","StandardDeviation","10","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","1","100","NaN","NaN","NaN","Skewness","0.20000000000000001","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","1","100","NaN","NaN","NaN","Kurtosis","3.0600000000000001","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999998e-201","0.10000000000000001","NaN","NaN","NaN","Mean","1.0000000000000001e-201","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999998e-201","0.10000000000000001","NaN","NaN","NaN","StandardDeviation","3.1622776601683792e-201","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999998e-201","0.10000000000000001","NaN","NaN","NaN","Skewness","6.3245553203367582","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999998e-201","0.10000000000000001","NaN","NaN","NaN","Kurtosis","63","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999998e-201","1","NaN","NaN","NaN","Mean","9.9999999999999998e-201","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999998e-201","1","NaN","NaN","NaN","StandardDeviation","9.9999999999999998e-201","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999998e-201","1","NaN","NaN","NaN","Skewness","2","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999998e-201","1","NaN","NaN","NaN","Kurtosis","9","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999998e-201","2","NaN","NaN","NaN","Mean","2e-200","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999998e-201","2","NaN","NaN","NaN","StandardDeviation","1.414213562373095e-200","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999998e-201","2","NaN","NaN","NaN","Skewness","1.4142135623730949","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999998e-201","2","NaN","NaN","NaN","Kurtosis","6","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999998e-201","100","NaN","NaN","NaN","Mean","9.9999999999999991e-199","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999998e-201","100","NaN","NaN","NaN","StandardDeviation","9.9999999999999998e-200","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999998e-201","100","NaN","NaN","NaN","Skewness","0.20000000000000001","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999998e-201","100","NaN","NaN","NaN","Kurtosis","3.0600000000000001","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999997e+199","0.10000000000000001","NaN","NaN","NaN","Mean","1.0000000000000001e+199","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999997e+199","0.10000000000000001","NaN","NaN","NaN","StandardDeviation","3.1622776601683793e+199","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999997e+199","0.10000000000000001","NaN","NaN","NaN","Skewness","6.3245553203367582","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999997e+199","0.10000000000000001","NaN","NaN","NaN","Kurtosis","63","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999997e+199","1","NaN","NaN","NaN","Mean","9.9999999999999997e+199","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999997e+199","1","NaN","NaN","NaN","StandardDeviation","9.9999999999999997e+199","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999997e+199","1","NaN","NaN","NaN","Skewness","2","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999997e+199","1","NaN","NaN","NaN","Kurtosis","9","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999997e+199","2","NaN","NaN","NaN","Mean","1.9999999999999999e+200","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999997e+199","2","NaN","NaN","NaN","StandardDeviation","1.414213562373095e+200","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999997e+199","2","NaN","NaN","NaN","Skewness","1.4142135623730949","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999997e+199","2","NaN","NaN","NaN","Kurtosis","6","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999997e+199","100","NaN","NaN","NaN","Mean","9.999999999999999e+201","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999997e+199","100","NaN","NaN","NaN","StandardDeviation","9.999999999999999e+200","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999997e+199","100","NaN","NaN","NaN","Skewness","0.20000000000000001","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gamma","analytic-scale-separated-moments","0","9.9999999999999997e+199","100","NaN","NaN","NaN","Kurtosis","3.0600000000000001","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gumbel","analytic-scale-separated-moments","0","1","0","NaN","NaN","NaN","Mean","0.57721566490153231","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gumbel","analytic-scale-separated-moments","0","1","0","NaN","NaN","NaN","StandardDeviation","1.2825498301618641","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gumbel","analytic-scale-separated-moments","0","1","0","NaN","NaN","NaN","Skewness","1.1395470994046486","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gumbel","analytic-scale-separated-moments","0","1","0","NaN","NaN","NaN","Kurtosis","5.4000000000000004","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gumbel","analytic-scale-separated-moments","0","9.9999999999999998e-201","0","NaN","NaN","NaN","Mean","5.7721566490153227e-201","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gumbel","analytic-scale-separated-moments","0","9.9999999999999998e-201","0","NaN","NaN","NaN","StandardDeviation","1.2825498301618641e-200","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gumbel","analytic-scale-separated-moments","0","9.9999999999999998e-201","0","NaN","NaN","NaN","Skewness","1.1395470994046486","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gumbel","analytic-scale-separated-moments","0","9.9999999999999998e-201","0","NaN","NaN","NaN","Kurtosis","5.4000000000000004","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gumbel","analytic-scale-separated-moments","0","9.9999999999999997e+199","0","NaN","NaN","NaN","Mean","5.7721566490153228e+199","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gumbel","analytic-scale-separated-moments","0","9.9999999999999997e+199","0","NaN","NaN","NaN","StandardDeviation","1.282549830161864e+200","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gumbel","analytic-scale-separated-moments","0","9.9999999999999997e+199","0","NaN","NaN","NaN","Skewness","1.1395470994046486","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Gumbel","analytic-scale-separated-moments","0","9.9999999999999997e+199","0","NaN","NaN","NaN","Kurtosis","5.4000000000000004","0","1.9999999999999999e-11","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","1","0.5","NaN","NaN","NaN","Mean","2","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","1","0.5","NaN","NaN","NaN","StandardDeviation","4.4721359549995796","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","1","0.5","NaN","NaN","NaN","Skewness","6.6187612133993774","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","1","0.5","NaN","NaN","NaN","Kurtosis","87.719999999999999","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","1","1","NaN","NaN","NaN","Mean","1","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","1","1","NaN","NaN","NaN","StandardDeviation","1","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","1","1","NaN","NaN","NaN","Skewness","2","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","1","1","NaN","NaN","NaN","Kurtosis","9","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","1","2","NaN","NaN","NaN","Mean","0.88622692545275805","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","1","2","NaN","NaN","NaN","StandardDeviation","0.46325137517610415","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","1","2","NaN","NaN","NaN","Skewness","0.63111065781893982","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","1","2","NaN","NaN","NaN","Kurtosis","3.2450893006876576","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","1","10","NaN","NaN","NaN","Mean","0.95135076986687317","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","1","10","NaN","NaN","NaN","StandardDeviation","0.11445721940300818","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","1","10","NaN","NaN","NaN","Skewness","-0.63763713390328691","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","1","10","NaN","NaN","NaN","Kurtosis","3.5701664835660853","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999998e-201","0.5","NaN","NaN","NaN","Mean","2e-200","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999998e-201","0.5","NaN","NaN","NaN","StandardDeviation","4.4721359549995793e-200","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999998e-201","0.5","NaN","NaN","NaN","Skewness","6.6187612133993774","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999998e-201","0.5","NaN","NaN","NaN","Kurtosis","87.719999999999999","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999998e-201","1","NaN","NaN","NaN","Mean","9.9999999999999998e-201","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999998e-201","1","NaN","NaN","NaN","StandardDeviation","9.9999999999999998e-201","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999998e-201","1","NaN","NaN","NaN","Skewness","2","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999998e-201","1","NaN","NaN","NaN","Kurtosis","9","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999998e-201","2","NaN","NaN","NaN","Mean","8.8622692545275799e-201","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999998e-201","2","NaN","NaN","NaN","StandardDeviation","4.6325137517610418e-201","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999998e-201","2","NaN","NaN","NaN","Skewness","0.63111065781893982","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999998e-201","2","NaN","NaN","NaN","Kurtosis","3.2450893006876576","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999998e-201","10","NaN","NaN","NaN","Mean","9.5135076986687315e-201","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999998e-201","10","NaN","NaN","NaN","StandardDeviation","1.1445721940300819e-201","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999998e-201","10","NaN","NaN","NaN","Skewness","-0.63763713390328691","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999998e-201","10","NaN","NaN","NaN","Kurtosis","3.5701664835660853","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999997e+199","0.5","NaN","NaN","NaN","Mean","1.9999999999999999e+200","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999997e+199","0.5","NaN","NaN","NaN","StandardDeviation","4.4721359549995795e+200","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999997e+199","0.5","NaN","NaN","NaN","Skewness","6.6187612133993774","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999997e+199","0.5","NaN","NaN","NaN","Kurtosis","87.719999999999999","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999997e+199","1","NaN","NaN","NaN","Mean","9.9999999999999997e+199","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999997e+199","1","NaN","NaN","NaN","StandardDeviation","9.9999999999999997e+199","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999997e+199","1","NaN","NaN","NaN","Skewness","2","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999997e+199","1","NaN","NaN","NaN","Kurtosis","9","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999997e+199","2","NaN","NaN","NaN","Mean","8.8622692545275797e+199","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999997e+199","2","NaN","NaN","NaN","StandardDeviation","4.6325137517610414e+199","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999997e+199","2","NaN","NaN","NaN","Skewness","0.63111065781893982","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999997e+199","2","NaN","NaN","NaN","Kurtosis","3.2450893006876576","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999997e+199","10","NaN","NaN","NaN","Mean","9.513507698668732e+199","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999997e+199","10","NaN","NaN","NaN","StandardDeviation","1.1445721940300817e+199","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999997e+199","10","NaN","NaN","NaN","Skewness","-0.63763713390328691","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Weibull","analytic-scale-separated-moments","0","9.9999999999999997e+199","10","NaN","NaN","NaN","Kurtosis","3.5701664835660853","0","2e-08","0","R-gamma-and-analytical-moments","finite" +"Exponential","actual-MLE-order-statistics-covariance","0","1","0","NaN","NaN","2","Covariance11","0.25","9.9999999999999998e-13","2.0000000000000001e-09","0","actual-MLE-order-statistics","finite" +"Exponential","actual-MLE-order-statistics-covariance","0","1","0","NaN","NaN","2","Covariance12","0","9.9999999999999998e-13","2.0000000000000001e-09","0","actual-MLE-order-statistics","finite" +"Exponential","actual-MLE-order-statistics-covariance","0","1","0","NaN","NaN","2","Covariance22","0.25","9.9999999999999998e-13","2.0000000000000001e-09","0","actual-MLE-order-statistics","finite" +"Exponential","actual-MLE-order-statistics-covariance","0","1","0","NaN","NaN","10","Covariance11","0.01","9.9999999999999998e-13","2.0000000000000001e-09","0","actual-MLE-order-statistics","finite" +"Exponential","actual-MLE-order-statistics-covariance","0","1","0","NaN","NaN","10","Covariance12","0","9.9999999999999998e-13","2.0000000000000001e-09","0","actual-MLE-order-statistics","finite" +"Exponential","actual-MLE-order-statistics-covariance","0","1","0","NaN","NaN","10","Covariance22","0.089999999999999997","9.9999999999999998e-13","2.0000000000000001e-09","0","actual-MLE-order-statistics","finite" +"Exponential","actual-MLE-order-statistics-covariance","0","1","0","NaN","NaN","100","Covariance11","0.0001","9.9999999999999998e-13","2.0000000000000001e-09","0","actual-MLE-order-statistics","finite" +"Exponential","actual-MLE-order-statistics-covariance","0","1","0","NaN","NaN","100","Covariance12","0","9.9999999999999998e-13","2.0000000000000001e-09","0","actual-MLE-order-statistics","finite" +"Exponential","actual-MLE-order-statistics-covariance","0","1","0","NaN","NaN","100","Covariance22","0.0099000000000000008","9.9999999999999998e-13","2.0000000000000001e-09","0","actual-MLE-order-statistics","finite" +"Exponential","actual-MLE-order-statistics-covariance","0","1","0","NaN","NaN","50000","Covariance11","4.0000000000000001e-10","9.9999999999999998e-13","2.0000000000000001e-09","0","actual-MLE-order-statistics","finite" +"Exponential","actual-MLE-order-statistics-covariance","0","1","0","NaN","NaN","50000","Covariance12","0","9.9999999999999998e-13","2.0000000000000001e-09","0","actual-MLE-order-statistics","finite" +"Exponential","actual-MLE-order-statistics-covariance","0","1","0","NaN","NaN","50000","Covariance22","1.9999600000000001e-05","9.9999999999999998e-13","2.0000000000000001e-09","0","actual-MLE-order-statistics","finite" +"Gamma","MLE-R-trigamma-covariance","0","1","0.001","NaN","NaN","100","Covariance11","10.010009993551806","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-R-trigamma","finite" +"Gamma","MLE-R-trigamma-covariance","0","1","0.001","NaN","NaN","100","Covariance12","-1.0009993551805106e-05","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-R-trigamma","finite" +"Gamma","MLE-R-trigamma-covariance","0","1","0.001","NaN","NaN","100","Covariance22","1.0009993551805107e-08","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-R-trigamma","finite" +"Gamma","MoM-independent-moment-Jacobian-covariance","0","1","0.001","NaN","NaN","100","Covariance11","30.02","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-independent-moment-Jacobian","finite" +"Gamma","MoM-independent-moment-Jacobian-covariance","0","1","0.001","NaN","NaN","100","Covariance12","-0.020019999999999996","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-independent-moment-Jacobian","finite" +"Gamma","MoM-independent-moment-Jacobian-covariance","0","1","0.001","NaN","NaN","100","Covariance22","2.0019999999999995e-05","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-independent-moment-Jacobian","finite" +"Gamma","MLE-R-trigamma-covariance","0","1","0.10000000000000001","NaN","NaN","100","Covariance11","0.11093693445700553","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-R-trigamma","finite" +"Gamma","MLE-R-trigamma-covariance","0","1","0.10000000000000001","NaN","NaN","100","Covariance12","-0.0010936934457005533","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-R-trigamma","finite" +"Gamma","MLE-R-trigamma-covariance","0","1","0.10000000000000001","NaN","NaN","100","Covariance22","0.00010936934457005534","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-R-trigamma","finite" +"Gamma","MoM-independent-moment-Jacobian-covariance","0","1","0.10000000000000001","NaN","NaN","100","Covariance11","0.32000000000000012","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-independent-moment-Jacobian","finite" +"Gamma","MoM-independent-moment-Jacobian-covariance","0","1","0.10000000000000001","NaN","NaN","100","Covariance12","-0.022000000000000009","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-independent-moment-Jacobian","finite" +"Gamma","MoM-independent-moment-Jacobian-covariance","0","1","0.10000000000000001","NaN","NaN","100","Covariance22","0.002200000000000001","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-independent-moment-Jacobian","finite" +"Gamma","MLE-R-trigamma-covariance","0","1","1","NaN","NaN","100","Covariance11","0.025505460967304303","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-R-trigamma","finite" +"Gamma","MLE-R-trigamma-covariance","0","1","1","NaN","NaN","100","Covariance12","-0.015505460967304305","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-R-trigamma","finite" +"Gamma","MLE-R-trigamma-covariance","0","1","1","NaN","NaN","100","Covariance22","0.015505460967304305","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-R-trigamma","finite" +"Gamma","MoM-independent-moment-Jacobian-covariance","0","1","1","NaN","NaN","100","Covariance11","0.050000000000000003","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-independent-moment-Jacobian","finite" +"Gamma","MoM-independent-moment-Jacobian-covariance","0","1","1","NaN","NaN","100","Covariance12","-0.040000000000000001","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-independent-moment-Jacobian","finite" +"Gamma","MoM-independent-moment-Jacobian-covariance","0","1","1","NaN","NaN","100","Covariance22","0.040000000000000001","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-independent-moment-Jacobian","finite" +"Gamma","MLE-R-trigamma-covariance","0","1","2","NaN","NaN","100","Covariance11","0.022249222728414682","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-R-trigamma","finite" +"Gamma","MLE-R-trigamma-covariance","0","1","2","NaN","NaN","100","Covariance12","-0.034498445456829362","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-R-trigamma","finite" +"Gamma","MLE-R-trigamma-covariance","0","1","2","NaN","NaN","100","Covariance22","0.068996890913658723","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-R-trigamma","finite" +"Gamma","MoM-independent-moment-Jacobian-covariance","0","1","2","NaN","NaN","100","Covariance11","0.035000000000000003","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-independent-moment-Jacobian","finite" +"Gamma","MoM-independent-moment-Jacobian-covariance","0","1","2","NaN","NaN","100","Covariance12","-0.059999999999999998","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-independent-moment-Jacobian","finite" +"Gamma","MoM-independent-moment-Jacobian-covariance","0","1","2","NaN","NaN","100","Covariance22","0.12","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-independent-moment-Jacobian","finite" +"Gamma","MLE-R-trigamma-covariance","0","1","100","NaN","NaN","100","Covariance11","0.020033556141671988","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-R-trigamma","finite" +"Gamma","MLE-R-trigamma-covariance","0","1","100","NaN","NaN","100","Covariance12","-1.9933556141671986","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-R-trigamma","finite" +"Gamma","MLE-R-trigamma-covariance","0","1","100","NaN","NaN","100","Covariance22","199.33556141671986","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-R-trigamma","finite" +"Gamma","MoM-independent-moment-Jacobian-covariance","0","1","100","NaN","NaN","100","Covariance11","0.020300000000000002","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-independent-moment-Jacobian","finite" +"Gamma","MoM-independent-moment-Jacobian-covariance","0","1","100","NaN","NaN","100","Covariance12","-2.02","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-independent-moment-Jacobian","finite" +"Gamma","MoM-independent-moment-Jacobian-covariance","0","1","100","NaN","NaN","100","Covariance22","202","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-independent-moment-Jacobian","finite" +"Gamma","MLE-R-trigamma-covariance","0","1","10000","NaN","NaN","100","Covariance11","0.020000333355901515","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-R-trigamma","finite" +"Gamma","MLE-R-trigamma-covariance","0","1","10000","NaN","NaN","100","Covariance12","-199.99333355901516","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-R-trigamma","finite" +"Gamma","MLE-R-trigamma-covariance","0","1","10000","NaN","NaN","100","Covariance22","1999933.3355901514","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-R-trigamma","finite" +"Gamma","MoM-independent-moment-Jacobian-covariance","0","1","10000","NaN","NaN","100","Covariance11","0.020002999999999996","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-independent-moment-Jacobian","finite" +"Gamma","MoM-independent-moment-Jacobian-covariance","0","1","10000","NaN","NaN","100","Covariance12","-200.02000000000001","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-independent-moment-Jacobian","finite" +"Gamma","MoM-independent-moment-Jacobian-covariance","0","1","10000","NaN","NaN","100","Covariance22","2000200","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-independent-moment-Jacobian","finite" +"Gumbel","MLE-exact-Euler-pi-covariance","0","1","0","NaN","NaN","100","Covariance11","0.011086648988595273","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-exact-Euler-pi","finite" +"Gumbel","MLE-exact-Euler-pi-covariance","0","1","0","NaN","NaN","100","Covariance12","0.0025702205554569311","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-exact-Euler-pi","finite" +"Gumbel","MLE-exact-Euler-pi-covariance","0","1","0","NaN","NaN","100","Covariance22","0.0060792710185402664","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-exact-Euler-pi","finite" +"Weibull","MLE-exact-Euler-pi-covariance","0","1","0.5","NaN","NaN","100","Covariance11","0.044346595954381091","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-exact-Euler-pi","finite" +"Weibull","MLE-exact-Euler-pi-covariance","0","1","0.5","NaN","NaN","100","Covariance12","0.0025702205554569311","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-exact-Euler-pi","finite" +"Weibull","MLE-exact-Euler-pi-covariance","0","1","0.5","NaN","NaN","100","Covariance22","0.0015198177546350666","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-exact-Euler-pi","finite" +"Weibull","MLE-exact-Euler-pi-covariance","0","1","1","NaN","NaN","100","Covariance11","0.011086648988595273","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-exact-Euler-pi","finite" +"Weibull","MLE-exact-Euler-pi-covariance","0","1","1","NaN","NaN","100","Covariance12","0.0025702205554569311","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-exact-Euler-pi","finite" +"Weibull","MLE-exact-Euler-pi-covariance","0","1","1","NaN","NaN","100","Covariance22","0.0060792710185402664","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-exact-Euler-pi","finite" +"Weibull","MLE-exact-Euler-pi-covariance","0","1","2","NaN","NaN","100","Covariance11","0.0027716622471488182","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-exact-Euler-pi","finite" +"Weibull","MLE-exact-Euler-pi-covariance","0","1","2","NaN","NaN","100","Covariance12","0.0025702205554569311","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-exact-Euler-pi","finite" +"Weibull","MLE-exact-Euler-pi-covariance","0","1","2","NaN","NaN","100","Covariance22","0.024317084074161065","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-exact-Euler-pi","finite" +"Weibull","MLE-exact-Euler-pi-covariance","0","1","10","NaN","NaN","100","Covariance11","0.00011086648988595273","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-exact-Euler-pi","finite" +"Weibull","MLE-exact-Euler-pi-covariance","0","1","10","NaN","NaN","100","Covariance12","0.0025702205554569311","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-exact-Euler-pi","finite" +"Weibull","MLE-exact-Euler-pi-covariance","0","1","10","NaN","NaN","100","Covariance22","0.60792710185402665","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-exact-Euler-pi","finite" +"GPA","MLE-Hosking-Wallis-covariance","0","1","-0.20000000000000001","NaN","NaN","100","Covariance11","0.0001008044208915856","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-Hosking-Wallis","finite" +"GPA","MLE-Hosking-Wallis-covariance","0","1","-0.20000000000000001","NaN","NaN","100","Covariance12","0","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-Hosking-Wallis","finite" +"GPA","MLE-Hosking-Wallis-covariance","0","1","-0.20000000000000001","NaN","NaN","100","Covariance13","0","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-Hosking-Wallis","finite" +"GPA","MLE-Hosking-Wallis-covariance","0","1","-0.20000000000000001","NaN","NaN","100","Covariance22","0.024","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-Hosking-Wallis","finite" +"GPA","MLE-Hosking-Wallis-covariance","0","1","-0.20000000000000001","NaN","NaN","100","Covariance23","0.012","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-Hosking-Wallis","finite" +"GPA","MLE-Hosking-Wallis-covariance","0","1","-0.20000000000000001","NaN","NaN","100","Covariance33","0.0144","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-Hosking-Wallis","finite" +"GPA","MLE-Hosking-Wallis-covariance","0","1","0","NaN","NaN","100","Covariance11","9.9999999999999991e-05","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-Hosking-Wallis","finite" +"GPA","MLE-Hosking-Wallis-covariance","0","1","0","NaN","NaN","100","Covariance12","0","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-Hosking-Wallis","finite" +"GPA","MLE-Hosking-Wallis-covariance","0","1","0","NaN","NaN","100","Covariance13","0","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-Hosking-Wallis","finite" +"GPA","MLE-Hosking-Wallis-covariance","0","1","0","NaN","NaN","100","Covariance22","0.02","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-Hosking-Wallis","finite" +"GPA","MLE-Hosking-Wallis-covariance","0","1","0","NaN","NaN","100","Covariance23","0.01","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-Hosking-Wallis","finite" +"GPA","MLE-Hosking-Wallis-covariance","0","1","0","NaN","NaN","100","Covariance33","0.01","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-Hosking-Wallis","finite" +"GPA","MLE-Hosking-Wallis-covariance","0","1","0.20000000000000001","NaN","NaN","100","Covariance11","9.9204379290817566e-05","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-Hosking-Wallis","finite" +"GPA","MLE-Hosking-Wallis-covariance","0","1","0.20000000000000001","NaN","NaN","100","Covariance12","0","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-Hosking-Wallis","finite" +"GPA","MLE-Hosking-Wallis-covariance","0","1","0.20000000000000001","NaN","NaN","100","Covariance13","0","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-Hosking-Wallis","finite" +"GPA","MLE-Hosking-Wallis-covariance","0","1","0.20000000000000001","NaN","NaN","100","Covariance22","0.016","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-Hosking-Wallis","finite" +"GPA","MLE-Hosking-Wallis-covariance","0","1","0.20000000000000001","NaN","NaN","100","Covariance23","0.0080000000000000002","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-Hosking-Wallis","finite" +"GPA","MLE-Hosking-Wallis-covariance","0","1","0.20000000000000001","NaN","NaN","100","Covariance33","0.0064000000000000012","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-Hosking-Wallis","finite" +"GPA","MLE-Hosking-Wallis-covariance","0","1","0.48999999999999999","NaN","NaN","100","Covariance11","9.8066108364978048e-05","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-Hosking-Wallis","finite" +"GPA","MLE-Hosking-Wallis-covariance","0","1","0.48999999999999999","NaN","NaN","100","Covariance12","0","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-Hosking-Wallis","finite" +"GPA","MLE-Hosking-Wallis-covariance","0","1","0.48999999999999999","NaN","NaN","100","Covariance13","0","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-Hosking-Wallis","finite" +"GPA","MLE-Hosking-Wallis-covariance","0","1","0.48999999999999999","NaN","NaN","100","Covariance22","0.010200000000000001","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-Hosking-Wallis","finite" +"GPA","MLE-Hosking-Wallis-covariance","0","1","0.48999999999999999","NaN","NaN","100","Covariance23","0.0051000000000000004","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-Hosking-Wallis","finite" +"GPA","MLE-Hosking-Wallis-covariance","0","1","0.48999999999999999","NaN","NaN","100","Covariance33","0.002601","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","-0.20000000000000001","NaN","NaN","100","Covariance11","0.0001008044208915856","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","-0.20000000000000001","NaN","NaN","100","Covariance12","0","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","-0.20000000000000001","NaN","NaN","100","Covariance13","0","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","-0.20000000000000001","NaN","NaN","100","Covariance22","0.074666666666666701","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","-0.20000000000000001","NaN","NaN","100","Covariance23","0.054400000000000046","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","-0.20000000000000001","NaN","NaN","100","Covariance33","0.049920000000000041","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","0","NaN","NaN","100","Covariance11","0.0001","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","0","NaN","NaN","100","Covariance12","0","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","0","NaN","NaN","100","Covariance13","0","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","0","NaN","NaN","100","Covariance22","0.02","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","0","NaN","NaN","100","Covariance23","0.01","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","0","NaN","NaN","100","Covariance33","0.01","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","0.20000000000000001","NaN","NaN","100","Covariance11","9.9204379290817552e-05","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","0.20000000000000001","NaN","NaN","100","Covariance12","0","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","0.20000000000000001","NaN","NaN","100","Covariance13","0","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","0.20000000000000001","NaN","NaN","100","Covariance22","0.019142857142857142","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","0.20000000000000001","NaN","NaN","100","Covariance23","0.011400000000000002","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","0.20000000000000001","NaN","NaN","100","Covariance33","0.010079999999999999","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","1","NaN","NaN","100","Covariance11","9.6107455824207935e-05","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","1","NaN","NaN","100","Covariance12","0","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","1","NaN","NaN","100","Covariance13","0","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","1","NaN","NaN","100","Covariance22","0.025333333333333333","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","1","NaN","NaN","100","Covariance23","0.034000000000000002","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","1","NaN","NaN","100","Covariance33","0.048000000000000001","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","2","NaN","NaN","100","Covariance11","9.2420075119037062e-05","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","2","NaN","NaN","100","Covariance12","0","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","2","NaN","NaN","100","Covariance13","0","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","2","NaN","NaN","100","Covariance22","0.034857142857142857","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","2","NaN","NaN","100","Covariance23","0.081428571428571433","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GPA","MoM-Hosking-Wallis-covariance","0","1","2","NaN","NaN","100","Covariance33","0.19285714285714287","9.9999999999999998e-13","2.0000000000000001e-09","0","MoM-Hosking-Wallis","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","-0.20000000000000001","NaN","NaN","100","Covariance11","0.012783531950225214","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","-0.20000000000000001","NaN","NaN","100","Covariance12","0.0057130408748802715","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","-0.20000000000000001","NaN","NaN","100","Covariance13","0.0027297786307977456","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","-0.20000000000000001","NaN","NaN","100","Covariance22","0.0080555934872568596","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","-0.20000000000000001","NaN","NaN","100","Covariance23","0.00045053807373202103","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","-0.20000000000000001","NaN","NaN","100","Covariance33","0.0064441795247405396","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","-9.9999999999999995e-07","NaN","NaN","100","Covariance11","0.012487133045870449","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","-9.9999999999999995e-07","NaN","NaN","100","Covariance12","0.0033659515001776396","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","-9.9999999999999995e-07","NaN","NaN","100","Covariance13","0.0025836837304344488","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","-9.9999999999999995e-07","NaN","NaN","100","Covariance22","0.0065313841920975326","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","-9.9999999999999995e-07","NaN","NaN","100","Covariance23","0.0014679795688165851","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","-9.9999999999999995e-07","NaN","NaN","100","Covariance33","0.0047665197368892348","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","0","NaN","NaN","100","Covariance11","0.012487131118084189","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","0","NaN","NaN","100","Covariance12","0.0033659395310777916","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","0","NaN","NaN","100","Covariance13","0.002583682395442025","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","0","NaN","NaN","100","Covariance22","0.0065313786712056978","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","0","NaN","NaN","100","Covariance23","0.0014679838219578351","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","0","NaN","NaN","100","Covariance33","0.0047665118889829135","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","9.9999999999999995e-07","NaN","NaN","100","Covariance11","0.012487129190293505","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","9.9999999999999995e-07","NaN","NaN","100","Covariance12","0.0033659275619754431","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","9.9999999999999995e-07","NaN","NaN","100","Covariance13","0.0025836810604436306","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","9.9999999999999995e-07","NaN","NaN","100","Covariance22","0.0065313731503346874","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","9.9999999999999995e-07","NaN","NaN","100","Covariance23","0.0014679880750904668","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","9.9999999999999995e-07","NaN","NaN","100","Covariance33","0.0047665040410819507","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","0.20000000000000001","NaN","NaN","100","Covariance11","0.012017342064038776","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","0.20000000000000001","NaN","NaN","100","Covariance12","0.00091902118521342794","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","0.20000000000000001","NaN","NaN","100","Covariance13","0.0022018153305384007","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","0.20000000000000001","NaN","NaN","100","Covariance22","0.0058390197084415207","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","0.20000000000000001","NaN","NaN","100","Covariance23","0.0021396981708541657","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","0.20000000000000001","NaN","NaN","100","Covariance33","0.0033038701887370754","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","0.48999999999999999","NaN","NaN","100","Covariance11","0.011119315214864596","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","0.48999999999999999","NaN","NaN","100","Covariance12","-0.0028388200507278799","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","0.48999999999999999","NaN","NaN","100","Covariance13","0.0013196962046728674","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","0.48999999999999999","NaN","NaN","100","Covariance22","0.0062391464983712151","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","0.48999999999999999","NaN","NaN","100","Covariance23","0.0024049515245483373","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-independent-score-outer-product-integral-covariance","0","1","0.48999999999999999","NaN","NaN","100","Covariance33","0.0015708995405816781","9.9999999999999998e-13","2.0000000000000001e-09","0","MLE-independent-score-outer-product-integral","finite" +"GEV","MLE-nonregular-domain","0","1","0.5","NaN","NaN","100","CovarianceDefined","0","0","1.9999999999999999e-11","0","regular-Fisher-domain-kappa-less-than-half","outside-uncertainty-domain" +"GEV","MLE-nonregular-domain","0","1","1","NaN","NaN","100","CovarianceDefined","0","0","1.9999999999999999e-11","0","regular-Fisher-domain-kappa-less-than-half","outside-uncertainty-domain" +"GEV","MLE-nonregular-domain","0","1","2","NaN","NaN","100","CovarianceDefined","0","0","1.9999999999999999e-11","0","regular-Fisher-domain-kappa-less-than-half","outside-uncertainty-domain" +"GPA","MLE-nonregular-domain","0","1","0.5","NaN","NaN","100","CovarianceDefined","0","0","1.9999999999999999e-11","0","regular-Fisher-domain-kappa-less-than-half","outside-uncertainty-domain" +"GPA","MLE-nonregular-domain","0","1","1","NaN","NaN","100","CovarianceDefined","0","0","1.9999999999999999e-11","0","regular-Fisher-domain-kappa-less-than-half","outside-uncertainty-domain" +"GPA","MLE-nonregular-domain","0","1","2","NaN","NaN","100","CovarianceDefined","0","0","1.9999999999999999e-11","0","regular-Fisher-domain-kappa-less-than-half","outside-uncertainty-domain" +"GPA","MoM-missing-fourth-moment","0","1","-1","NaN","NaN","100","CovarianceDefined","0","0","1.9999999999999999e-11","0","Hosking-Wallis-fourth-moment-domain","outside-uncertainty-domain" +"GPA","MoM-missing-fourth-moment","0","1","-0.29999999999999999","NaN","NaN","100","CovarianceDefined","0","0","1.9999999999999999e-11","0","Hosking-Wallis-fourth-moment-domain","outside-uncertainty-domain" +"GPA","MoM-missing-fourth-moment","0","1","-0.25","NaN","NaN","100","CovarianceDefined","0","0","1.9999999999999999e-11","0","Hosking-Wallis-fourth-moment-domain","outside-uncertainty-domain" diff --git a/docs/distributions/oracles/extreme-positive.md b/docs/distributions/oracles/extreme-positive.md new file mode 100644 index 00000000..734dc999 --- /dev/null +++ b/docs/distributions/oracles/extreme-positive.md @@ -0,0 +1,183 @@ +# Extreme and positive distribution oracle fixtures + +Generated with **R version 4.4.3 (2025-02-28 ucrt)**, using only base R and `stats`. +The generator does not load Numerics, invoke an optimizer, run a test suite, or +change production files. Its probability, moment, and derivative calculations +are independent of the implementation being repaired. + +Run from the repository root: + +```powershell +& 'C:/Program Files/R/R-4.4.3/bin/Rscript.exe' --vanilla docs/distributions/oracles/extreme-positive.R +``` + +An optional first argument changes the CSV output path. The generator requires +R 4.4.3 so a reference change cannot silently substitute another R version. +Regeneration is deterministic, with LF line endings and 17-digit numeric text. + +## Contents and parameter mapping + +| CSV family | Numerics parameters | Independent mapping | Rows | +| --- | --- | --- | ---: | +| Exponential | xi, scale=Alpha | R `dexp/pexp/qexp` in unit-scale coordinates, then affine scale | 97 | +| Gamma | scale=Theta, shape=Kappa; xi=0 | R `dgamma/pgamma/qgamma(shape=Kappa, scale=Theta)` | 701 | +| GEV | xi, scale=Alpha, shape=Kappa | Hosking GEV defining formulas; SciPy `genextreme(c=Kappa)` | 560 | +| GPA | xi, scale=Alpha, shape=Kappa | Hosking GPA defining formulas; SciPy `genpareto(c=-Kappa)` | 513 | +| Gumbel | xi, scale=Alpha | Maximum Gumbel defining formulas; SciPy `gumbel_r` | 47 | +| Weibull | scale=Lambda, shape=Kappa; xi=0 | R `dweibull/pweibull/qweibull(shape=Kappa, scale=Lambda)` | 579 | + +The CSV has **2,497 rows**. All kurtoses are ordinary kurtosis, not excess. +Xi is zero except for selected tail-overflow cases; affine location/scale +metamorphic checks supplement the external goldens. Shapes include both signs, +exact zero, and nonzero values as small as +/-1e-12. Scales include 1e-200 and +1e200, plus selected 1e-320 log-density reproductions. + +The added GEV shape 50, 100, and 200 moment rows use R `lgamma` and normalized +central-moment ratios. Scale is combined with the moment logarithm before +exponentiation, so finite scaled moments remain referenceable when a raw Gamma +function or the corresponding unit-scale moment overflows. These 36 rows have +relative tolerance 2e-10 and exact finite/infinite classification. + +Eighty-four additional Weibull moment rows use the defining transform +`X = scale * T^(1/kappa)`, where T has the unit exponential distribution. +Shapes .005, .01, and .02 use normalized R `lgamma` central moments at scales +1 and 1e±200, with relative tolerance 2e-10. Shapes 1e3, 1e6, 1e12, and 1e200 +use full-probability integration of the centered, divided transform +`expm1(c*log(T))/c`, with `c=1/kappa`, at the same scales. Integrating this +transform preserves its central moments as c tends to zero; scale and c are +restored through logarithms. These large-shape rows use relative tolerance +2e-8. No production GEV moment routine participates in either reference. + +The final review added 56 rows without changing any of the earlier 2,441 rows: + +- Thirty-six GEV/GPA density and tail logarithms evaluate the defining Hosking + support expression from physical-coordinate logarithms when the difference, + standardized observation, or shape product exceeds the floating-point range. +- Twelve Gamma covariance entries use R integration of the scaled Fisher + residual kernel for shapes 1e16 and 1e155, with the same Fisher inverse and + physical scale/count factors. In the coordinate `u=kappa*t`, the integrated + kernel is `exp(-u)*(u/2+u^2/(12*kappa))`; the first omitted integrated + Bernoulli term has magnitude `1/(30*kappa^3)`. +- Two Gamma median variance rows use the defining concentrated-shape limit + `kappa/n` for MLE and MoM at kappa=1e16. The median expansion + `q=kappa-1/3+O(1/kappa)` gives a relative variance correction of order + `1/kappa`, below the rows' 2e-10 relative acceptance. These rows protect + the positive mean-direction term from cancellation in a rounded matrix. +- One Weibull variance reference analytically contracts its retained rounded + covariance coefficients at lambda=kappa=1e100. One median reference combines + the defining quantile logarithm with lambda=1e200 before exponentiation. +- Four GPA skewness/kurtosis entries evaluate the same rational moments in + reciprocal-shape coordinates at shapes 1e200 and 1e308. + +The GEV/GPA definitions are evaluated with `expm1` divided differences, without +zeroing small nonzero shapes. Mathematical support and one-sided endpoint +densities are explicit. Infinite density at an integrable endpoint is retained. +These endpoints must not be replaced blindly with a package's endpoint policy. + +R 4.4.3 `dweibull(...,log=TRUE)` and `pweibull(...,log.p=TRUE)` can return +negative infinity after an internal positive power underflows, even when the +mathematical logarithm is finite. The selected `k=100,z=1e-20` rows therefore +use the defining log-density and log-CDF limit, identified by the oracle label +`Weibull-defining-log-formula-R-underflow`. The density uses +`log(k)+(k-1)*log(z)-exp(k*log(z))-log(scale)`; the CDF logarithm equals +`k*log(z)` to binary64 precision after this underflow. Other native R rows +remain unchanged. + +## Schema and tolerance interpretation + +Coordinates: `family,case,xi,scale,shape,x,p,n`. `NaN` in an unused coordinate +means not applicable, not an invalid-parameter test. `quantity` names the result; +`value` is its independent expected value. Readers must map `Inf`, `-Inf`, and +`NaN` to their corresponding floating-point classifications. + +For finite rows, use `absolute_tolerance + relative_tolerance * abs(value)`; +classification must be checked separately for infinite/undefined rows. A +relative-only tolerance intentionally protects tiny nonzero values from passing +as zero. `estimated_absolute_error` records an oracle convergence indicator, +not a rigorous proof or an additional allowed test tolerance. + +`Gradient1/2/3` follow the distribution's public parameter order. Gamma instead +uses `GradientScale` and `GradientShape` for clarity. `Covarianceij` is the +upper triangle of the public-order covariance matrix, already divided by `n`. +`CovarianceDefined=0` rows denote unsupported uncertainty domains; their +`case` distinguishes MLE from MoM. They are not invalid distribution parameters. +The standalone `QuantileVariance` and `Median` rows are exercised by focused +regressions in addition to the family-wide probability/moment/covariance readers. + +Most native R/analytic rows use relative tolerance 2e-11. Centered moment +integration uses 2e-8 relative plus 2e-9 absolute, protecting zero centered +moments from meaningless relative checks. Covariance uses 2e-9 relative plus +1e-12 absolute. Weibull gamma-function moment cancellation receives 2e-8 +relative tolerance. Exact classification still applies at moment-existence +boundaries. + +## Gamma actual-quantile derivatives + +The **49 finite shape derivatives** differentiate actual R `qgamma` results; +they do not differentiate a Wilson-Hilferty or Cornish-Fisher approximation. +The shape grid is `.001,.01,.1,.5,1,2,10,100,10000`, and probabilities are +`1e-12,1e-6,.01,.5,.99,1-1e-12`. + +The generator differentiates `log(qgamma(p, shape=k*exp(u)))` with respect to +`u`, then multiplies by `q/k`. A five-point central derivative is extrapolated +to sixth order, and refinement selects the most stable pair of estimates. +Using log-quantiles retains the shape=.001 median near 5.24e-302. Upper-tail +quantiles are requested with `lower.tail=FALSE, log.p=TRUE` to avoid derivative +noise from an unnecessary lower-tail inversion. + +Every finite derivative is independently cross-checked by differentiating +`pgamma` at the fixed quantile and applying implicit differentiation. Lower or +upper log-probability is selected according to the requested tail. The maximum +observed relative disagreement in the frozen grid is below **5e-10**; fixture +acceptance is a conservative **5e-8 relative**. The generator refuses to write +the CSV if this cross-check exceeds that acceptance threshold. + +These are convergence-checked **binary64** references, not arbitrary-precision +calculations. Five lower-tail quantiles underflow in R; their shape derivatives +have status `unresolved-quantile-underflow` and value NaN. They must not become +zero-derivative expected values. Quantiles/scale derivatives that underflow are +separately marked `underflowed`. A broader extreme-tail contract would require +an independently validated arbitrary-precision oracle. + +There are no legacy named quantile-approximation API rows in this file. Those +APIs need their own separately named fixtures if their existing contracts are +changed; they must not replace these actual-quantile references. The two Gamma +large-shape variance-limit rows explicitly identify their asymptotic bound. + +## Moment and covariance provenance + +GEV/GPA centered moments are integrated over the **full probability interval** +with paired tails `p=u^8/2`, relative tolerance 1e-10, absolute tolerance 1e-12, +and a 1,000-subdivision ceiling. Existence is checked first: moment order r +requires `kappa > -1/r`. No probability-tail truncation is used. Undefined +moments follow the planned Numerics NaN convention. Other moments use analytic +identities and R's gamma function with scale applied after standardization. + +GEV expected Fisher information is independently integrated from analytic +scores in the latent Gumbel coordinate; its positive-definite inverse is frozen +for kappa `-.2,-1e-6,0,1e-6,.2,.49`. No Numerics information implementation or +matrix routine participates. GPA MLE/MoM covariance uses Hosking-Wallis formulas, +with separate rejected-domain rows. Gamma covariance uses R `trigamma` for MLE +and an independent sample-mean/sample-variance Jacobian for MoM. Gumbel/Weibull +covariance uses Euler/pi constants, rather than the production rounded decimals. + +Exponential MLE covariance deliberately corresponds to the **actual MLE** +`location=min(sample), scale=mean(sample)-min(sample)`: diagonal entries are +`scale^2/n^2` and `scale^2*(n-1)/n^2`, with zero covariance. This differs from +bias-corrected estimators and is explicitly named `actual-MLE-order-statistics`. +The n=50000 case also protects against integer multiplication overflow. + +## Sources + +- [R pgamma implementation](https://raw.githubusercontent.com/wch/r-source/trunk/src/nmath/pgamma.c) +- [R qgamma implementation](https://raw.githubusercontent.com/wch/r-source/trunk/src/nmath/qgamma.c) +- [SciPy 1.16.2 distribution source, for parameterization cross-checks](https://raw.githubusercontent.com/scipy/scipy/v1.16.2/scipy/stats/_continuous_distns.py) +- [Hosking and Wallis, Parameter and Quantile Estimation for the Generalized Pareto Distribution, 1987](https://stat.cmu.edu/technometrics/80-89/VOL-29-03/v2903339.pdf) + +The current CSV SHA-256 is +`E5824F8D5E09B2D582F42DB962F5F49A24D87641F8B5E3DFC1913BCE0BC72BCA`. +An independent regeneration on 2026-09-08 produced the same hash and all +2,497 coordinate/quantity combinations are unique. The task's +`extreme-positive-report.md` records current implementation and validation +evidence; `extreme-positive-oracles-report.md` retains the earlier 2,321-row +reference-generation checkpoint. diff --git a/docs/distributions/oracles/generalized-adjacent-boundaries.csv b/docs/distributions/oracles/generalized-adjacent-boundaries.csv new file mode 100644 index 00000000..c19e6f9d --- /dev/null +++ b/docs/distributions/oracles/generalized-adjacent-boundaries.csv @@ -0,0 +1,181 @@ +kappa,hondo,endpoint,step,x,logcdf,logpdf +-2.0,0.2,lower,1,-0.4799999999999999,-169.8210592371866,-131.02853365244698 +-2.0,0.2,lower,2,-0.47999999999999987,-166.9344824120125,-128.7192721923077 +-2.0,0.2,lower,3,-0.4799999999999998,-165.11614251486398,-127.2646002745889 +-2.0,0.2,lower,4,-0.47999999999999976,-163.78559457887573,-126.20016192579831 +-2.0,0.5,lower,1,-0.37499999999999994,-72.0873067782343,-33.96421184743732 +-2.0,0.5,lower,2,-0.3749999999999999,-70.70101241711443,-33.27106466687738 +-2.0,0.5,lower,3,-0.37499999999999983,-69.89008220089809,-32.86559955876921 +-2.0,0.5,lower,4,-0.3749999999999998,-69.31471805599453,-32.57791748631743 +-2.0,1.0,lower,1,5e-324,-744.4400719213812,-1.5e-323 +-2.0,1.0,lower,2,1e-323,-743.7469247408213,-3e-323 +-2.0,1.0,lower,3,1.5e-323,-743.3414596327132,-4.4e-323 +-2.0,1.0,lower,4,2e-323,-743.0537775602613,-6e-323 +-2.0,2.0,lower,1,1.5000000000000002,-18.714973875118524,16.635532333438686 +-2.0,2.0,lower,2,1.5000000000000004,-18.36840028483855,16.288958743158716 +-2.0,2.0,lower,3,1.5000000000000007,-18.16566773078447,16.086226189104632 +-2.0,2.0,lower,4,1.5000000000000009,-18.021826694558577,15.942385152878742 +-2.0,10.0,lower,1,49.50000000000001,-3.718308767230552,26.557023626092832 +-2.0,10.0,lower,2,49.500000000000014,-3.6489940491745574,25.93319116358888 +-2.0,10.0,lower,3,49.50000000000002,-3.608447538363741,25.568272566291533 +-2.0,10.0,lower,4,49.50000000000003,-3.5796793311185633,25.30935870108493 +-0.5,0.2,lower,1,-1.1055728090000838,-174.9451248220798,-137.5419429890127 +-0.5,0.2,lower,2,-1.1055728090000836,-172.06522953351705,-135.2380267581625 +-0.5,0.2,lower,3,-1.1055728090000834,-170.24949948924422,-133.78544272274422 +-0.5,0.2,lower,4,-1.1055728090000831,-168.92034240505183,-132.7221170553903 +-0.5,0.5,lower,1,-0.5857864376269049,-73.05724925174383,-35.48890385503199 +-0.5,0.5,lower,2,-0.5857864376269047,-71.52777258372446,-34.72416552102231 +-0.5,0.5,lower,3,-0.5857864376269046,-70.67130566741635,-34.29593206286825 +-0.5,0.5,lower,4,-0.5857864376269045,-70.07355616013324,-33.9970573092267 +-0.5,1.0,lower,1,5e-324,-744.4400719213812,-5e-324 +-0.5,1.0,lower,2,1e-323,-743.7469247408213,-1.5e-323 +-0.5,1.0,lower,3,1.5e-323,-743.3414596327132,-2e-323 +-0.5,1.0,lower,4,2e-323,-743.0537775602613,-3e-323 +-0.5,2.0,lower,1,0.8284271247461902,-18.69122394073675,17.651503169896834 +-0.5,2.0,lower,2,0.8284271247461903,-18.264312312935957,17.22459154209604 +-0.5,2.0,lower,3,0.8284271247461904,-18.037433607090083,16.997712836250166 +-0.5,2.0,lower,4,0.8284271247461905,-17.881943145931114,16.842222375091197 +-0.5,10.0,lower,1,4.32455532033676,-3.5451245162643485,28.452243006888068 +-0.5,10.0,lower,2,4.324555320336761,-3.492091803945591,27.974948596019246 +-0.5,10.0,lower,3,4.324555320336762,-3.4576203049879455,27.664705105400436 +-0.5,10.0,lower,4,4.324555320336763,-3.4320338503208725,27.434427013396782 +0.0,0.2,lower,1,-1.6094379124341,-179.44206912116195,-141.94421738449546 +0.0,0.2,lower,2,-1.6094379124340998,-176.34938515416331,-139.47007021089655 +0.0,0.2,lower,3,-1.6094379124340996,-174.45286286520727,-137.95285237973172 +0.0,0.2,lower,4,-1.6094379124340994,-173.0811601306311,-136.85549019207076 +0.0,0.5,lower,1,-0.6931471805599452,-73.09421058584333,-35.85395811236172 +0.0,0.5,lower,2,-0.6931471805599451,-71.88862888292962,-35.251167260904865 +0.0,0.5,lower,3,-0.693147180559945,-71.14175647858673,-34.877731058733424 +0.0,0.5,lower,4,-0.6931471805599448,-70.59920742655979,-34.60645653271995 +0.0,1.0,lower,1,5e-324,-744.4400719213812,-5e-324 +0.0,1.0,lower,2,1e-323,-743.7469247408213,-1e-323 +0.0,1.0,lower,3,1.5e-323,-743.3414596327132,-1.5e-323 +0.0,1.0,lower,4,2e-323,-743.0537775602613,-2e-323 +0.0,2.0,lower,1,0.6931471805599454,-18.485553830622237,17.792406650062293 +0.0,2.0,lower,2,0.6931471805599455,-18.07698004945112,17.383832868891176 +0.0,2.0,lower,3,0.6931471805599456,-17.855179019468224,17.162031838908277 +0.0,2.0,lower,4,0.6931471805599457,-17.70206969381971,17.008922513259762 +0.0,10.0,lower,1,2.3025850929940463,-3.4952528497218482,29.154690554502587 +0.0,10.0,lower,2,2.302585092994047,-3.44387011840673,28.692245972666527 +0.0,10.0,lower,3,2.3025850929940472,-3.4100945262228413,28.388265643011525 +0.0,10.0,lower,4,2.3025850929940477,-3.3848918386883615,28.161441455201206 +0.2,0.2,lower,1,-1.8986483073060738,-180.47982054799593,-143.09630610844945 +0.2,0.2,lower,2,-1.8986483073060736,-177.64274386529203,-140.82664476228635 +0.2,0.2,lower,3,-1.8986483073060734,-175.8438548178481,-139.3875335243312 +0.2,0.2,lower,4,-1.8986483073060731,-174.52369952316343,-138.33140928858347 +0.2,0.2,upper,1,4.999999999999999,-2.716530433528379e-80,-146.5659615594911 +0.2,0.2,upper,2,4.999999999999998,-2.420304285808122e-78,-142.97419518666726 +0.2,0.2,upper,3,4.999999999999997,-2.4796673686914197e-77,-141.11274218791033 +0.2,0.2,upper,4,4.9999999999999964,-1.2058997559173439e-76,-139.8473928750617 +0.2,0.5,lower,1,-0.7434917749851749,-73.9374278576764,-36.41419618439024 +0.2,0.5,lower,2,-0.7434917749851748,-72.4556748892554,-35.673319700179746 +0.2,0.5,lower,3,-0.7434917749851747,-71.61391145879261,-35.25243798494835 +0.2,0.5,lower,4,-0.7434917749851746,-71.02330714775353,-34.95713582942881 +0.2,0.5,upper,1,4.999999999999999,-2.716530433528379e-80,-146.5659615594911 +0.2,0.5,upper,2,4.999999999999998,-2.420304285808122e-78,-142.97419518666726 +0.2,0.5,upper,3,4.999999999999997,-2.4796673686914197e-77,-141.11274218791033 +0.2,0.5,upper,4,4.9999999999999964,-1.2058997559173439e-76,-139.8473928750617 +0.2,1.0,lower,1,5e-324,-744.4400719213812,-5e-324 +0.2,1.0,lower,2,1e-323,-743.7469247408213,-1e-323 +0.2,1.0,lower,3,1.5e-323,-743.3414596327132,-1e-323 +0.2,1.0,lower,4,2e-323,-743.0537775602613,-1.5e-323 +0.2,1.0,upper,1,4.999999999999999,-2.716530433528379e-80,-146.5659615594911 +0.2,1.0,upper,2,4.999999999999998,-2.420304285808122e-78,-142.97419518666726 +0.2,1.0,upper,3,4.999999999999997,-2.4796673686914197e-77,-141.11274218791033 +0.2,1.0,upper,4,4.9999999999999964,-1.2058997559173439e-76,-139.8473928750617 +0.2,2.0,lower,1,0.6472471835193794,-18.497461122181768,17.942943377733812 +0.2,2.0,lower,2,0.6472471835193795,-18.041925396778623,17.487407652330667 +0.2,2.0,lower,3,0.6472471835193796,-17.8075781260519,17.253060381603945 +0.2,2.0,lower,4,0.6472471835193797,-17.648649063939587,17.09413131949163 +0.2,2.0,upper,1,4.999999999999999,-2.716530433528379e-80,-146.5659615594911 +0.2,2.0,upper,2,4.999999999999998,-2.420304285808122e-78,-142.97419518666726 +0.2,2.0,upper,3,4.999999999999997,-2.4796673686914197e-77,-141.11274218791033 +0.2,2.0,upper,4,4.9999999999999964,-1.2058997559173439e-76,-139.8473928750617 +0.2,10.0,lower,1,1.845213277599034,-3.5492564439210996,30.10123992089466 +0.2,10.0,lower,2,1.8452132775990342,-3.4843678165258685,29.517242274337576 +0.2,10.0,lower,3,1.8452132775990344,-3.445341300435684,29.166003629525918 +0.2,10.0,lower,4,1.8452132775990346,-3.4173418432126295,28.91400851451843 +0.2,10.0,upper,1,4.999999999999999,-2.716530433528379e-80,-146.5659615594911 +0.2,10.0,upper,2,4.999999999999998,-2.420304285808122e-78,-142.97419518666726 +0.2,10.0,upper,3,4.999999999999997,-2.4796673686914197e-77,-141.11274218791033 +0.2,10.0,upper,4,4.9999999999999964,-1.2058997559173439e-76,-139.8473928750617 +2.0,0.2,lower,1,-11.999999999999996,-184.926309644878,-149.5504856283365 +2.0,0.2,lower,2,-11.999999999999995,-181.93071849031966,-147.15401270468982 +2.0,0.2,lower,3,-11.999999999999993,-180.07047293437805,-145.66581625993655 +2.0,0.2,lower,4,-11.999999999999991,-178.7177439833341,-144.58363309910135 +2.0,0.2,upper,1,0.49999999999999994,-1.0536712138825738e-08,18.36840027640918 +2.0,0.2,upper,2,0.4999999999999999,-1.4901161216052115e-08,18.02182668263765 +2.0,0.2,upper,3,0.49999999999999983,-1.8250120783250976e-08,17.8190941259044 +2.0,0.2,upper,4,0.4999999999999998,-2.1073424299855936e-08,17.675253087419865 +2.0,0.5,lower,1,-1.4999999999999998,-74.8598955004741,-38.123094930796995 +2.0,0.5,lower,2,-1.4999999999999996,-73.4736011393542,-37.42994775023705 +2.0,0.5,lower,3,-1.4999999999999993,-72.66267092313787,-37.02448264212888 +2.0,0.5,lower,4,-1.4999999999999991,-72.0873067782343,-36.7368005696771 +2.0,0.5,upper,1,0.49999999999999994,-1.0536712155479084e-08,18.368400279570196 +2.0,0.5,upper,2,0.4999999999999999,-1.4901161249358807e-08,18.021826687107996 +2.0,0.5,upper,3,0.49999999999999983,-1.8250120833211014e-08,17.819094131379437 +2.0,0.5,upper,4,0.4999999999999998,-2.107342436646932e-08,17.675253093741894 +2.0,1.0,lower,1,5e-324,-744.4400719213812,5e-324 +2.0,1.0,lower,2,1e-323,-743.7469247408213,1e-323 +2.0,1.0,lower,3,1.5e-323,-743.3414596327132,1.5e-323 +2.0,1.0,lower,4,2e-323,-743.0537775602613,2e-323 +2.0,1.0,upper,1,0.49999999999999994,-1.053671218323466e-08,18.36840028483855 +2.0,1.0,upper,2,0.4999999999999999,-1.490116130486996e-08,18.021826694558577 +2.0,1.0,upper,3,0.49999999999999983,-1.825012091647774e-08,17.819094140504497 +2.0,1.0,upper,4,0.4999999999999998,-2.1073424477491626e-08,17.675253104278607 +2.0,2.0,lower,1,0.37500000000000006,-18.021826694558577,18.714973875118524 +2.0,2.0,lower,2,0.3750000000000001,-17.675253104278607,18.36840028483855 +2.0,2.0,lower,3,0.37500000000000017,-17.472520550224523,18.16566773078447 +2.0,2.0,lower,4,0.3750000000000002,-17.328679513998633,18.02182669455858 +2.0,2.0,upper,1,0.49999999999999994,-1.0536712238745813e-08,18.368400295375263 +2.0,2.0,upper,2,0.4999999999999999,-1.4901161415892264e-08,18.021826709459738 +2.0,2.0,upper,3,0.49999999999999983,-1.82501210830112e-08,17.819094158754616 +2.0,2.0,upper,4,0.4999999999999998,-2.1073424699536237e-08,17.67525312535203 +2.0,10.0,lower,1,0.49500000000000005,-3.2908159173188003,31.919928348863255 +2.0,10.0,lower,2,0.4950000000000001,-3.217245237820926,31.25779223338239 +2.0,10.0,lower,3,0.49500000000000016,-3.1753193947968756,30.880459646165942 +2.0,10.0,lower,4,0.4950000000000002,-3.1458685910446573,30.615402412395984 +2.0,10.0,upper,1,0.49999999999999994,-1.0536712682835059e-08,18.368400379668966 +2.0,10.0,upper,2,0.4999999999999999,-1.490116230407079e-08,18.021826828669038 +2.0,10.0,upper,3,0.49999999999999983,-1.8250122415279026e-08,17.8190943047556 +2.0,10.0,upper,4,0.4999999999999998,-2.1073426475893378e-08,17.675253293939445 +10.0,0.2,lower,1,-976562.3999999993,-194.17120530814861,-169.8219054584258 +10.0,0.2,lower,2,-976562.3999999992,-191.02924367727545,-167.30833615372725 +10.0,0.2,lower,3,-976562.3999999991,-189.11467580719648,-165.77668185766407 +10.0,0.2,lower,4,-976562.399999999,-187.733612414409,-164.6718311434341 +10.0,0.2,upper,1,0.09999999999999999,-0.024724098253273142,33.30225509931338 +10.0,0.2,upper,2,0.09999999999999998,-0.027278984255823924,32.417464862800784 +10.0,0.2,upper,3,0.09999999999999996,-0.028639971636108583,31.979419038693024 +10.0,0.2,upper,4,0.09999999999999995,-0.02959011977956636,31.68577875978709 +10.0,0.5,lower,1,-102.29999999999998,-77.26784110912597,-44.87224517960249 +10.0,0.5,lower,2,-102.29999999999997,-76.05556950198533,-44.266109376032176 +10.0,0.5,lower,3,-102.29999999999995,-75.30618260310251,-43.89141592659077 +10.0,0.5,lower,4,-102.29999999999994,-74.76231517213523,-43.61948221110712 +10.0,0.5,upper,1,0.09999999999999999,-0.024816399368165203,33.30962617823191 +10.0,0.5,upper,2,0.09999999999999998,-0.02739142424435244,32.42559233808326 +10.0,0.5,upper,3,0.09999999999999996,-0.028763956484614835,31.987949037759602 +10.0,0.5,upper,4,0.09999999999999995,-0.02972250148505874,31.694589604868217 +10.0,1.0,lower,1,5e-324,-744.4400719213812,4.4e-323 +10.0,1.0,lower,2,1e-323,-743.7469247408213,9e-323 +10.0,1.0,lower,3,1.5e-323,-743.3414596327132,1.33e-322 +10.0,1.0,lower,4,2e-323,-743.0537775602613,1.8e-322 +10.0,1.0,upper,1,0.09999999999999999,-0.02497229924978276,33.322034377916 +10.0,1.0,upper,2,0.09999999999999998,-0.027581604442091408,32.43928805020544 +10.0,1.0,upper,3,0.09999999999999996,-0.028973819701101855,32.00233101600191 +10.0,1.0,upper,4,0.09999999999999995,-0.029946694231720975,31.709450855610744 +10.0,2.0,lower,1,0.09990234375000001,-16.053956928535822,22.29228155357543 +10.0,2.0,lower,2,0.09990234375000002,-15.648491820427624,21.886816445467364 +10.0,2.0,lower,3,0.09990234375000004,-15.427575444288074,21.66590006932794 +10.0,2.0,lower,4,0.09990234375000005,-15.27488461951245,21.513209244552446 +10.0,2.0,upper,1,0.09999999999999999,-0.02529211058413248,33.34732648850013 +10.0,2.0,upper,2,0.09999999999999998,-0.027972791975658863,32.4672608421811 +10.0,2.0,upper,3,0.09999999999999996,-0.029406117270237425,32.031737133272145 +10.0,2.0,upper,4,0.09999999999999995,-0.030408975900199503,31.739859831510945 +10.0,10.0,lower,1,0.09999999999000002,-1.5497437726136534,34.670961464605774 +10.0,10.0,lower,2,0.09999999999000003,-1.494007671841482,34.16933780666033 +10.0,10.0,lower,3,0.09999999999000005,-1.4584304738108018,33.84914427339003 +10.0,10.0,lower,4,0.09999999999000006,-1.4322426734989497,33.613455319590926 +10.0,10.0,upper,1,0.09999999999999999,-0.028319974353035498,33.57691414709331 +10.0,10.0,upper,2,0.09999999999999998,-0.03175188634098617,32.725055027274315 +10.0,10.0,upper,3,0.09999999999999996,-0.03362856991996543,32.3049881452816 +10.0,10.0,upper,4,0.09999999999999995,-0.034959626738983646,32.0240874962616 diff --git a/docs/distributions/oracles/generalized-adjacent-boundaries.py b/docs/distributions/oracles/generalized-adjacent-boundaries.py new file mode 100644 index 00000000..44f5db1d --- /dev/null +++ b/docs/distributions/oracles/generalized-adjacent-boundaries.py @@ -0,0 +1,37 @@ +"""Frozen adjacent Kappa support references; independent 400-digit Decimal formulas. + +This extends the existing kappa-four boundary fixture from the fourth interior +binary64 value to the first, second, third and fourth values. No Numerics helper +is loaded. Binary64 shapes and evaluation arguments are converted exactly. +""" +import csv +import math +from decimal import Decimal as D, localcontext +from pathlib import Path + +rows = [] +with localcontext() as context: + context.prec = 400 + for kf in [-2., -.5, 0., .2, 2., 10.]: + for hf in [.2, .5, 1., 2., 10.]: + k, h = D.from_float(kf), D.from_float(hf) + lower = h.ln() if not k else (1 - (-k * h.ln()).exp()) / k + endpoints = [(lower, math.inf, "lower")] + if k > 0: + endpoints.append((1 / k, -math.inf, "upper")) + for endpoint, direction, label in endpoints: + x_float = float(endpoint) + for step in range(1, 5): + x_float = math.nextafter(x_float, direction) + x = D.from_float(x_float) + logt = -x if not k else (1 - k * x).ln() / k + logcdf = (1 - h * logt.exp()).ln() / h + logpdf = (1 - k) * logt + (1 - h) * logcdf + rows.append([kf, hf, label, step, x_float, float(logcdf), float(logpdf)]) + +target = Path(__file__).with_suffix(".csv") +with target.open("w", newline="", encoding="ascii") as stream: + writer = csv.writer(stream, lineterminator="\n") + writer.writerow(["kappa", "hondo", "endpoint", "step", "x", "logcdf", "logpdf"]) + writer.writerows(rows) +print(f"Wrote {len(rows)} independent adjacent-boundary rows to {target.name}") diff --git a/docs/distributions/oracles/generalized-affine-overflow.R b/docs/distributions/oracles/generalized-affine-overflow.R new file mode 100644 index 00000000..deb30462 --- /dev/null +++ b/docs/distributions/oracles/generalized-affine-overflow.R @@ -0,0 +1,32 @@ +# Independent four-case Hosking GNO/GLO tail reference. +# Base R only; no Numerics assembly or production helper is loaded. +# Run: Rscript --vanilla generalized-affine-overflow.R +# Inputs are the binary64 constants used by the C# tests. The standardized +# ratio x/alpha exceeds binary64 range, so the defining support factor is +# assembled from its logarithm before evaluating the normal or logistic law. + +cat("# ", R.version.string, "\n", sep = "") +cat("# platform=", R.version$platform, "; locale=", Sys.getlocale(), "\n", sep = "") +cat("family,x,alpha,kappa,latent,log_tail,log_pdf,pdf\n") +alpha <- 1e-200 +for (family in c("GNO", "GLO")) { + for (direction in c(-1, 1)) { + x <- direction * 1e200 + kappa <- -direction * 20 + log_product <- log(abs(kappa)) + log(abs(x)) - log(alpha) + log_support <- log_product + log1p(exp(-log_product)) + latent <- -log_support / kappa + if (family == "GNO") { + log_tail <- pnorm(abs(latent), lower.tail = FALSE, log.p = TRUE) + log_pdf <- dnorm(latent, log = TRUE) + kappa * latent - log(alpha) + } else { + # Defining logistic tail/density, independently cross-checked by stats. + log_tail <- -abs(latent) - log1p(exp(-abs(latent))) + log_pdf <- -abs(latent) - 2 * log1p(exp(-abs(latent))) + kappa * latent - log(alpha) + stopifnot(abs(log_tail - plogis(abs(latent), lower.tail = FALSE, log.p = TRUE)) < 1e-13) + stopifnot(abs(log_pdf - (dlogis(latent, log = TRUE) + kappa * latent - log(alpha))) < 1e-12) + } + stopifnot(is.finite(latent), is.finite(log_tail), is.finite(log_pdf)) + cat(paste(c(family, sprintf("%.17g", c(x, alpha, kappa, latent, log_tail, log_pdf, exp(log_pdf)))), collapse = ","), "\n", sep = "") + } +} diff --git a/docs/distributions/oracles/generalized-affine-overflow.md b/docs/distributions/oracles/generalized-affine-overflow.md new file mode 100644 index 00000000..9dd1a153 --- /dev/null +++ b/docs/distributions/oracles/generalized-affine-overflow.md @@ -0,0 +1,38 @@ +# Independent generalized-family affine-overflow tail references + +Generated on 2026-09-08 with **R 4.4.3 (2025-02-28 ucrt)**, platform +`x86_64-w64-mingw32`, locale `C`, using `generalized-affine-overflow.R`. +The standalone script uses base R only and never loads Numerics. It emits the +runtime metadata and four reference rows; it does not alter other frozen fixtures. +R emitted four startup locale-setting warnings but exited successfully with all +numerical checks passing. + +All cases use location zero and scale `1E-200`. At observation `1E200` and shape +`-20`, the ratio `x/alpha` overflows binary64, but the defining Hosking latent value +is finite: + +``` +logSupport = log(20) + log(abs(x)) - log(alpha) + + log1p(exp(-(log(20) + log(abs(x)) - log(alpha)))) +z = -logSupport/kappa = 46.201488473558619 +``` + +Reflecting both x and shape negates z and gives the identical opposite-tail and +density limits. The first omitted ordinary ratio is never formed. Normal tails +and density use R `pnorm(..., log.p=TRUE)` and `dnorm(..., log=TRUE)`. Logistic +values use their defining log formulas, independently cross-checked against +R `plogis` and `dlogis`. The density Jacobian is `kappa*z-log(alpha)`. + +| Family | x | kappa | Tail | Log tail | Log PDF | PDF | +| --- | ---: | ---: | --- | ---: | ---: | ---: | +| GNO | -1E200 | 20 | lower | -1072.0411870643629 | -1531.7204579917529 | 0 | +| GNO | 1E200 | -20 | upper | -1072.0411870643629 | -1531.7204579917529 | 0 | +| GLO | -1E200 | 20 | lower | -46.201488473558619 | -509.71423934592178 | 4.3044582966584941E-222 | +| GLO | 1E200 | -20 | upper | -46.201488473558619 | -509.71423934592178 | 4.3044582966584941E-222 | + +The GNO ordinary PDF and tail genuinely underflow; their logarithms remain finite. +Both GLO ordinary tail and PDF remain representable. Tests retain a `5E-12` +absolute allowance for log values and `3E-12` relative allowance for ordinary +values, with exact zero required for underflow. References are R binary64 +arithmetic, not arbitrary-precision claims. Existing compensated near-support +transforms remain separately covered by the accepted adjacent-boundary fixtures. diff --git a/docs/distributions/oracles/generalized-fisher.R b/docs/distributions/oracles/generalized-fisher.R new file mode 100644 index 00000000..3ca813a8 --- /dev/null +++ b/docs/distributions/oracles/generalized-fisher.R @@ -0,0 +1,384 @@ +# Independent expected-information oracle for Hosking GNO/GLO, Kappa Four and GEV. +# No Numerics assembly, production numerical helper, fitted sample, or package is used. +# Run from the repository root with Rscript --vanilla docs/distributions/oracles/generalized-fisher.R. +# Public parameter order is xi, alpha, kappa[, hondo]. GEV fixes hondo=0; +# GLO fixes hondo=-1. Covariance in a restricted family is the inverse of its +# principal INFORMATION block, not a principal block of the larger covariance. +options(digits = 17, warn = 2) +args <- commandArgs(trailingOnly = FALSE) +script <- sub("^--file=", "", args[grep("^--file=", args)]) +directory <- dirname(normalizePath(script, winslash = "/", mustWork = TRUE)) +target <- file.path(directory, "generalized-fisher.csv") +notes <- file.path(directory, "generalized-fisher.md") +relative_tolerance <- 2e-12 +absolute_tolerance <- 2e-13 + +# expm1(x)-x evaluated from its convergent series near the removable singularity. +# This independently implements the derivative of the defining density, not a +# finite difference of a quantile with its observation moving with the parameter. +expm1_minus_x <- function(x) { + out <- expm1(x) - x + small <- abs(x) < 0.05 + if (any(small)) { + y <- x[small] + term <- y * y / 2 + total <- term + for (j in 3:16) { term <- term * y / j; total <- total + term } + out[small] <- total + } + out +} + +# Scores at a fixed observation, written in terms of its true latent variate. +normal_scores <- function(z, k) { + A <- k - z + r <- exp(k * z) + d1 <- if (k == 0) z else expm1(k * z) / k + d2 <- if (k == 0) z * z / 2 else expm1_minus_x(k * z) / k^2 + cbind(-A * r, -1 - A * d1, z + A * d2) +} + +# Each row below is score * sqrt(phi(z)); combining the Gaussian exponent first +# avoids overflow before multiplication by the integration weight in remote tails. +normal_weighted_scores <- function(z, k) { + A <- k - z + root <- exp(-z * z / 4) / (2 * pi)^0.25 + rroot <- exp(k * z - z * z / 4) / (2 * pi)^0.25 + if (k == 0) { + d1root <- z * root + d2root <- z * z * root / 2 + } else { + d1root <- (rroot - root) / k + d2root <- (rroot - root - k * z * root) / k^2 + small <- abs(k * z) < 0.05 + d1root[small] <- expm1(k * z[small]) / k * root[small] + d2root[small] <- expm1_minus_x(k * z[small]) / k^2 * root[small] + } + cbind(-A * rroot, -root - A * d1root, z * root + A * d2root) +} + +kappa_scores <- function(logp, logq, k, h) { + if (h == 0) { + B <- -logp + w <- log(-logp) + } else { + B <- expm1(-h * logp) / h + w <- numeric(length(logp)) + large <- h * logp > 36 + if (any(large)) w[large] <- h * logp[large] + log1p(-exp(-h * logp[large])) - log(-h) + w[!large] <- log(-expm1(h * logp[!large]) / h) + } + # If the survival probability is below rounding resolution, log(t)=log(q) + # to binary64 relative precision. Retain its logarithm even if q underflows. + upper <- logq < -36 + w[upper] <- logq[upper] + A <- 1 - k - (1 - h) * B + R <- exp(-k * w) + d1 <- if (k == 0) -w else expm1(-k * w) / k + d2 <- if (k == 0) -w * w / 2 else -expm1_minus_x(-k * w) / k^2 + if (h == 0) { + sh <- -logp - logp * logp / 2 + } else { + # Rearranged to preserve the exact nonzero shape when h is small. + sh <- B - expm1_minus_x(-h * logp) / h^2 + } + cbind(A * R, -1 + A * d1, -w + A * d2, sh) +} + +# Score times sqrt(Jacobian), combining exponents before exponentiation. This +# matters for large negative h: t itself can overflow while every weighted +# information integrand remains finite. No domain tail is omitted. +kappa_weighted_scores <- function(logp, logq, k, h, logroot) { + if (h == 0) { + w <- log(-logp) + logB <- w + } else { + log_abs_expm1 <- function(v) { + answer <- numeric(length(v)) + large <- v > 36 + answer[large] <- v[large] + log1p(-exp(-v[large])) + answer[!large] <- log(abs(expm1(v[!large]))) + answer + } + w <- log_abs_expm1(h * logp) - log(abs(h)) + logB <- log_abs_expm1(-h * logp) - log(abs(h)) + } + upper <- logq < -36 + w[upper] <- logq[upper] + logB[upper] <- logq[upper] + root <- exp(logroot) + Broot <- exp(logB + logroot) + Aroot <- (1 - k) * root - (1 - h) * Broot + ARroot <- (1 - k) * exp(-k * w + logroot) - (1 - h) * exp(logB - k * w + logroot) + if (k == 0) { + scale_score <- -root - w * Aroot + shape_score <- -w * root - w * w * Aroot / 2 + } else { + scale_score <- -root + (ARroot - Aroot) / k + shape_score <- -w * root + ((1 - k * w) * Aroot - ARroot) / k^2 + small <- abs(k * w) < .05 + scale_score[small] <- -root[small] + Aroot[small] * expm1(-k * w[small]) / k + shape_score[small] <- -w[small] * root[small] - Aroot[small] * expm1_minus_x(-k * w[small]) / k^2 + } + if (h == 0) { + hscore <- (-logp - logp * logp / 2) * root + } else { + hscore <- (-logp * root - (1 - h) * Broot) / h + small <- abs(h * logp) < .05 + hscore[small] <- Broot[small] - expm1_minus_x(-h * logp[small]) / h^2 * root[small] + } + cbind(ARroot, scale_score, shape_score, hscore) +} + +fixed_log_density <- function(x, parameters, family) { + xi <- parameters[1]; alpha <- parameters[2]; k <- parameters[3] + y <- (x - xi) / alpha + if (alpha <= 0 || (k != 0 && 1 - k * y <= 0)) return(-Inf) + w <- if (k == 0) -y else log1p(-k * y) / k + if (family == "GNO") { + z <- -w + return(-log(alpha) + k * z + dnorm(z, log = TRUE)) + } + h <- if (family == "GLO") -1 else if (family == "GEV") 0 else parameters[4] + t <- exp(w) + if (h != 0 && 1 - h * t <= 0) return(-Inf) + logF <- if (h == 0) -t else log1p(-h * t) / h + -log(alpha) + (1 - k) * w + (1 - h) * logF +} + +quantile <- function(p, parameters, family) { + xi <- parameters[1]; alpha <- parameters[2]; k <- parameters[3] + if (family == "GNO") z <- qnorm(p) else { + h <- if (family == "GLO") -1 else if (family == "GEV") 0 else parameters[4] + t <- if (h == 0) -log(p) else -expm1(h * log(p)) / h + z <- -log(t) + } + xi + alpha * if (k == 0) z else -expm1(-k * z) / k +} + +# Five-point fixed-x differences, at two step sizes; observations are never +# recomputed inside the differentiation stencil. +derivative_check <- function(family, k, h) { + parameters <- c(0.7, 1.3, k, if (family == "K4") h) + worst <- 0 + agreement <- 0 + for (p in c(.1, .5, .9)) { + x <- quantile(p, parameters, family) + expected <- if (family == "GNO") normal_scores(qnorm(p), k) else { + kappa_scores(log(p), log1p(-p), k, h)[, seq_along(parameters), drop = FALSE] + } + expected[1:2] <- expected[1:2] / parameters[2] + for (j in seq_along(parameters)) { + difference <- function(step) { + values <- vapply(c(-2, -1, 1, 2), function(multiple) { + candidate <- parameters + candidate[j] <- candidate[j] + multiple * step + fixed_log_density(x, candidate, family) + }, 0.0) + (values[1] - 8 * values[2] + 8 * values[3] - values[4]) / (12 * step) + } + step <- 1e-4 * (1 + abs(parameters[j])) + coarse <- difference(step) + fine <- difference(step / 2) + worst <- max(worst, abs(fine - expected[j]) / max(1, abs(expected[j]))) + agreement <- max(agreement, abs(fine - coarse) / max(1, abs(fine))) + } + } + stopifnot(is.finite(worst), worst < 2e-7) + c(derivative_relative_error = worst, derivative_step_agreement = agreement) +} + +integral <- function(f, lower, upper) { + answer <- integrate(f, lower, upper, subdivisions = 1500L, + rel.tol = relative_tolerance, abs.tol = absolute_tolerance, + stop.on.error = TRUE) + stopifnot(is.finite(answer$value), is.finite(answer$abs.error)) + c(value = answer$value, error = answer$abs.error) +} + +# Alternative full-domain parametrizations: p=u^power/2 and q=u^power/2. +# The production algorithm is neither loaded nor called. Different powers give +# independent error-sensitive coordinates; GNO additionally uses direct z-space. +probability_integral <- function(family, k, h, i, j, power) { + halves <- lapply(c(FALSE, TRUE), function(upper_tail) { + integral(function(u) { + logtail <- power * log(u) - log(2) + logother <- log1p(-exp(logtail)) + lp <- if (upper_tail) logother else logtail + lq <- if (upper_tail) logtail else logother + logjac <- log(power / 2) + (power - 1) * log(u) + weighted <- if (family == "GNO") { + z <- if (upper_tail) qnorm(logtail, lower.tail = FALSE, log.p = TRUE) else qnorm(logtail, log.p = TRUE) + normal_scores(z, k) * exp(logjac / 2) + } else kappa_weighted_scores(lp, lq, k, h, logjac / 2) + if (j == 0) weighted[, i] * exp(logjac / 2) else weighted[, i] * weighted[, j] + }, 0, 1) + }) + Reduce(`+`, halves) +} + +normal_integral <- function(k, i, j) { + integral(function(z) { + scores <- normal_weighted_scores(z, k) + if (j == 0) scores[, i] * exp(-z * z / 4) / (2 * pi)^.25 else scores[, i] * scores[, j] + }, -Inf, Inf) +} + +cases <- list() +add <- function(family, k, h) cases[[length(cases) + 1L]] <<- list(family = family, k = k, h = h) +for (k in c(0, -1e-6, 1e-6, -.2, .2, -1, 1, -2, 2)) add("GNO", k, NA_real_) +for (k in c(0, -1e-6, 1e-6, -.2, .2, -.4, .4, -.45, .45, -.49, .49)) add("GLO", k, -1) +for (k in c(0, -1e-6, 1e-6, -.2, .2, -1, .45, .49)) add("GEV", k, 0) +for (pair in list(c(0,0), c(1e-6,0), c(-1e-6,0), c(0,1e-6), c(0,-1e-6), c(0,-1), c(.2,-1), c(-.2,-1), + c(.2,.2), c(-.2,-.2), c(.4,-1), c(-.4,-1), c(-1,-.2), + c(-.2,-2), c(.49,.2), c(.2,.49), c(-.49,-1), c(-.2,-2.45))) add("K4", pair[1], pair[2]) + +rows <- list(); summaries <- list() +append_row <- function(family, k, h, quantity, i, j, value, error, agreement) { + rows[[length(rows) + 1L]] <<- data.frame(family = family, kappa = k, hondo = h, + location = 0, scale = 1, sample_size = 1, quantity = quantity, row = i, + column = j, value = value, absolute_error_estimate = error, + alternate_coordinate_difference = agreement) +} + +for (case in cases) { + family <- case$family; k <- case$k; h <- case$h + dimension <- if (family == "K4") 4L else 3L + check <- derivative_check(family, k, h) + information <- matrix(0, dimension, dimension) + errors <- information; alternatives <- information + score_mean <- score_errors <- numeric(dimension) + # The high-power map makes approach-to-boundary cases well conditioned at u=0. + near_boundary <- family != "GNO" && max(k, h, k*h) > .4 + first_power <- if (near_boundary) 128 else 16 + second_power <- if (near_boundary) 192 else 24 + for (i in seq_len(dimension)) { + mean <- if (family == "GNO") normal_integral(k, i, 0) else probability_integral(family, k, h, i, 0, first_power) + mean2 <- probability_integral(family, k, h, i, 0, second_power) + score_mean[i] <- mean["value"]; score_errors[i] <- mean["error"] + append_row(family, k, h, "score_mean", i, 0, mean["value"], mean["error"], abs(mean["value"] - mean2["value"])) + for (j in i:dimension) { + a <- if (family == "GNO") normal_integral(k, i, j) else probability_integral(family, k, h, i, j, first_power) + b <- probability_integral(family, k, h, i, j, second_power) + information[i,j] <- information[j,i] <- a["value"] + errors[i,j] <- errors[j,i] <- a["error"] + alternatives[i,j] <- alternatives[j,i] <- b["value"] + } + } + eigenvalues <- eigen(information, symmetric = TRUE, only.values = TRUE)$values + covariance <- solve(information) + alternate_covariance <- solve(alternatives) + delta <- norm(information - alternatives, "2") + error_norm <- norm(errors, "2") + covariance_error <- norm(covariance, "2")^2 * error_norm / (1 - norm(covariance, "2") * error_norm) + residual <- max(abs(information %*% covariance - diag(dimension))) + stopifnot(min(eigenvalues) > 0, is.finite(covariance_error), covariance_error > 0, + norm(covariance, "2") * error_norm < 1, max(abs(score_mean)) < 5e-8, + delta < 2e-7 * max(1, norm(information, "2")), residual < 1e-8) + for (i in seq_len(dimension)) for (j in seq_len(dimension)) { + append_row(family, k, h, "information", i, j, information[i,j], errors[i,j], abs(information[i,j] - alternatives[i,j])) + append_row(family, k, h, "covariance", i, j, covariance[i,j], covariance_error, abs(covariance[i,j] - alternate_covariance[i,j])) + } + if (family == "GNO" && k == 0) { + exact <- matrix(c(7/6,0,1/3, 0,1/2,0, 1/3,0,2/3), 3, 3) + stopifnot(max(abs(covariance - exact)) < 1e-10) + for (i in 1:3) for (j in 1:3) append_row(family, k, h, "exact_covariance", i, j, exact[i,j], 0, abs(covariance[i,j] - exact[i,j])) + } + summaries[[length(summaries) + 1L]] <- data.frame(family = family, k = k, h = h, + min_eigenvalue = min(eigenvalues), condition = max(eigenvalues) / min(eigenvalues), + max_mean = max(abs(score_mean)), error_norm = error_norm, coordinate_difference = delta, + covariance_error = covariance_error, inverse_residual = residual, + derivative_error = check[1], derivative_agreement = check[2]) + cat(family, "k=", k, "h=", h, "minimum eigenvalue=", min(eigenvalues), "coordinate delta=", delta, "\n") +} + +# Additional analytical moment references. GNO is a shifted/reflected lognormal. +# Near zero, GLO uses power-series algebra performed on coefficient arrays before +# evaluation, rather than subtracting nearly equal floating-point Gamma products. +normal_moments <- function(k) { + if (k == 0) return(c(0, 1, 0, 3)) + t <- k*k; v <- expm1(t); relative <- expm1(t)/t + c(-expm1(t/2)/k, exp(t/2)*sqrt(relative), + -k*(v+3)*sqrt(relative), 3+v*(16+v*(15+v*(6+v)))) +} + +logistic_moments <- function(k) { + if (k == 0) return(c(0, pi/sqrt(3), 0, 21/5)) + if (abs(k) > .01) { + r <- 1:4; B <- pi*r*k/sin(pi*r*k) + V <- B[2]-B[1]^2 + return(c((1-B[1])/k, sqrt(V)/abs(k), + sign(k)*(-B[3]+3*B[1]*B[2]-2*B[1]^3)/V^1.5, + (B[4]-4*B[1]*B[3]+6*B[1]^2*B[2]-3*B[1]^4)/V^2)) + } + degree <- 14L + reciprocal_sinc <- numeric(degree+1L); reciprocal_sinc[1] <- 1 + sinc <- (-1)^(0:degree)*pi^(2*(0:degree))/factorial(2*(0:degree)+1) + for (n in 1:degree) reciprocal_sinc[n+1] <- -sum(sinc[2:(n+1)]*rev(reciprocal_sinc[1:n])) + multiply <- function(a,b) { + answer <- numeric(degree+1L) + for (n in 0:degree) answer[n+1] <- sum(a[1:(n+1)]*rev(b[1:(n+1)])) + answer + } + B <- lapply(1:4, function(r) reciprocal_sinc*r^(2*(0:degree))) + B11 <- multiply(B[[1]],B[[1]]) + V <- B[[2]]-B11 + N3 <- -B[[3]]+3*multiply(B[[1]],B[[2]])-2*multiply(B11,B[[1]]) + N4 <- B[[4]]-4*multiply(B[[1]],B[[3]])+6*multiply(B11,B[[2]])-3*multiply(B11,B11) + horner <- function(a,t) Reduce(function(value,coefficient) value*t+coefficient, rev(a), init=0) + t <- k*k + # Exact orders of vanishing are removed symbolically, not inferred by tolerance. + variance <- horner(V[-1],t) + c(-k*horner(reciprocal_sinc[-1],t), sqrt(variance), + k*horner(N3[-c(1,2)],t)/variance^1.5, + horner(N4[-c(1,2)],t)/variance^2) +} + +stopifnot(abs(logistic_moments(.00010001)[4]-4.2000018682670925) < 3e-13, + abs(logistic_moments(.0002)[3]+.0017412478719479963) < 3e-15) +for (family in c("GNO","GLO")) for (k in c(0,-1e-6,1e-6,-.00010001,.00010001,-.0002,.0002,-.01,.01,-.2,.2)) { + values <- if (family == "GNO") normal_moments(k) else logistic_moments(k) + for (i in 1:4) append_row(family,k,if (family=="GLO") -1 else NA_real_,"moment",i,0, + values[i],5e-13*max(1,abs(values[i])),NA_real_) +} + +result <- do.call(rbind, rows) +formatted <- result +for (column in names(formatted)) if (is.numeric(formatted[[column]])) { + formatted[[column]] <- ifelse(is.na(formatted[[column]]),"NA",sprintf("%.17g",formatted[[column]])) +} +write.table(formatted, target, sep = ",", quote = FALSE, row.names = FALSE, na = "NA", eol = "\n") +summary <- do.call(rbind, summaries) +header <- c("# Independent generalized-family Fisher information oracle", "", + paste("Generated with", R.version.string, "on", R.version$platform, "."), "", + "The generator uses base R only. It does not load Numerics or any production numerical helper.", + "All CSV matrices use location=0, scale=1, sample_size=1 and parameter order xi, alpha, kappa[, hondo].", + "GNO denotes Hosking's transformed normal, not the symmetric exponential-power distribution.", + "GLO fixes Kappa Four hondo=-1; GEV fixes hondo=0. A fixed-shape family inverts its own information block.", "", + "## Method and acceptance checks", "", + sprintf("R integrate (QUADPACK): relative tolerance %.3g; absolute tolerance %.3g; at most 1500 subdivisions.", relative_tolerance, absolute_tolerance), + "GNO uses direct integration over the entire normal latent line, with an independent probability-coordinate cross-check.", + "GLO, GEV and K4 pair p=u^a/2 and 1-p=u^a/2 over the complete u interval [0,1]. Powers 16/24 are cross-checked; boundary-approach cases use 128/192.", + "Log probabilities and log survival probabilities are retained separately. Below exp(-36), log(t)=log(survival) is used to binary64 relative precision; no probability tail is discarded.", + "Analytical scores are checked against five-point fixed-observation derivatives of an independently expressed log density at probabilities .1, .5 and .9, using two step sizes.", + "Finite matrices, near-zero mean scores, positive eigenvalues, inverse residuals and agreement between integration coordinates are required before writing the fixture.", + "The covariance error column is a conservative matrix-norm perturbation estimate from the numerical integration error estimates; it is not a rigorous interval bound.", + "The alternate-coordinate difference is independent numerical corroboration, not a rigorous bound. Values are binary64 R references, not arbitrary-precision goldens.", + "The exact GNO kappa=0 covariance is [[7/6,0,1/3],[0,1/2,0],[1/3,0,2/3]] with all three parameters estimated.", "", + "The additional moment rows use row=1 mean, row=2 standard deviation, row=3 skewness, row=4 non-excess kurtosis. GNO uses analytical lognormal moments; GLO uses trigonometric moments, with factored coefficient-array series for abs(kappa)<=.01.", + "Moment absolute_error_estimate is an arithmetic comparison allowance of 5e-13*max(1,abs(value)), not a quadrature error estimate. The GLO near-zero series is cross-checked against independent 70-digit Decimal values at kappa=.00010001 and .0002.", "", + "## Mathematical domain", "", + "GNO information is finite for every finite kappa. GLO uses abs(kappa)<1/2. K4 uses kappa<1/2, hondo<1/2 and kappa*hondo<1/2. GEV uses kappa<1/2.", + "These are local asymptotic-information conditions, not guarantees of global-MLE existence, estimator convergence, or finite-sample coverage.", + "MLE covariance is not L-moment or product-moment covariance. The fixture does not authorize substitution between estimators.", "", + "## Primary-source mapping and references", "", + "- [Hosking lmom R defining quantiles](https://raw.githubusercontent.com/cran/lmom/master/R/lmom.r): quagno(p,c(xi,alpha,k)), quaglo(p,c(xi,alpha,k)), quakap(p,c(xi,alpha,k,h)), quagev(p,c(xi,alpha,k)).", + "- [Park and Kim (2007), Fisher information matrix for a four-parameter kappa distribution](https://doi.org/10.1016/j.spl.2007.03.002). The present scores/domain are independently derived; inaccessible full-paper formulae were not treated as verified values.", + "- [Wang and Flournoy (2015), local likelihood estimation for the three-parameter lognormal](https://doi.org/10.1016/j.spl.2015.05.021): finite Fisher information does not imply bounded global likelihood.", "", + "## Numerical summary", "", + "| Family | kappa | hondo | Minimum eigenvalue | Condition number | Max mean score | Information error norm | Coordinate delta norm | Covariance error estimate | Fixed-x derivative relative error |", + "| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |") +table <- apply(summary, 1, function(r) sprintf("| %s | %.6g | %.6g | %.6g | %.6g | %.3g | %.3g | %.3g | %.3g | %.3g |", + r[1], as.numeric(r[2]), as.numeric(r[3]), as.numeric(r[4]), as.numeric(r[5]), as.numeric(r[6]), as.numeric(r[7]), as.numeric(r[8]), as.numeric(r[9]), as.numeric(r[11]))) +writeLines(c(header, table, "", paste(nrow(result), "CSV rows from", nrow(summary), "parameter cases.")), notes, useBytes = TRUE) +cat("Wrote", target, "and", notes, "\n") diff --git a/docs/distributions/oracles/generalized-fisher.csv b/docs/distributions/oracles/generalized-fisher.csv new file mode 100644 index 00000000..6d8c6b8c --- /dev/null +++ b/docs/distributions/oracles/generalized-fisher.csv @@ -0,0 +1,1334 @@ +family,kappa,hondo,location,scale,sample_size,quantity,row,column,value,absolute_error_estimate,alternate_coordinate_difference +GNO,0,NA,0,1,1,score_mean,1,0,0,0,0 +GNO,0,NA,0,1,1,score_mean,2,0,2.8839777788114418e-17,1.4609176947993383e-14,2.7863995832877464e-16 +GNO,0,NA,0,1,1,score_mean,3,0,0,0,0 +GNO,0,NA,0,1,1,information,1,1,1.0000000000000002,1.9425164948689757e-12,4.4408920985006262e-16 +GNO,0,NA,0,1,1,covariance,1,1,1.1666666666666663,3.6462452288211073e-12,4.4408920985006262e-16 +GNO,0,NA,0,1,1,information,1,2,0,0,5.5511151231257827e-17 +GNO,0,NA,0,1,1,covariance,1,2,0,3.6462452288211073e-12,3.2381504884900413e-17 +GNO,0,NA,0,1,1,information,1,3,-0.5,3.4266786595014829e-13,3.8857805861880479e-16 +GNO,0,NA,0,1,1,covariance,1,3,0.3333333333333332,3.6462452288211073e-12,1.1102230246251565e-16 +GNO,0,NA,0,1,1,information,2,1,0,0,5.5511151231257827e-17 +GNO,0,NA,0,1,1,covariance,2,1,0,3.6462452288211073e-12,3.2381504884900413e-17 +GNO,0,NA,0,1,1,information,2,2,2,7.6559392893350515e-13,6.6613381477509392e-16 +GNO,0,NA,0,1,1,covariance,2,2,0.5,3.6462452288211073e-12,2.2204460492503131e-16 +GNO,0,NA,0,1,1,information,2,3,0,0,0 +GNO,0,NA,0,1,1,covariance,2,3,0,3.6462452288211073e-12,9.2518585385429753e-18 +GNO,0,NA,0,1,1,information,3,1,-0.5,3.4266786595014829e-13,3.8857805861880479e-16 +GNO,0,NA,0,1,1,covariance,3,1,0.3333333333333332,3.6462452288211073e-12,1.6653345369377348e-16 +GNO,0,NA,0,1,1,information,3,2,0,0,0 +GNO,0,NA,0,1,1,covariance,3,2,0,3.6462452288211073e-12,9.2518585385429753e-18 +GNO,0,NA,0,1,1,information,3,3,1.7500000000000002,9.6875384131102392e-13,1.3322676295501878e-15 +GNO,0,NA,0,1,1,covariance,3,3,0.66666666666666652,3.6462452288211073e-12,5.5511151231257827e-16 +GNO,0,NA,0,1,1,exact_covariance,1,1,1.1666666666666667,0,4.4408920985006262e-16 +GNO,0,NA,0,1,1,exact_covariance,1,2,0,0,0 +GNO,0,NA,0,1,1,exact_covariance,1,3,0.33333333333333331,0,1.1102230246251565e-16 +GNO,0,NA,0,1,1,exact_covariance,2,1,0,0,0 +GNO,0,NA,0,1,1,exact_covariance,2,2,0.5,0,0 +GNO,0,NA,0,1,1,exact_covariance,2,3,0,0,0 +GNO,0,NA,0,1,1,exact_covariance,3,1,0.33333333333333331,0,1.1102230246251565e-16 +GNO,0,NA,0,1,1,exact_covariance,3,2,0,0,0 +GNO,0,NA,0,1,1,exact_covariance,3,3,0.66666666666666663,0,1.1102230246251565e-16 +GNO,-9.9999999999999995e-07,NA,0,1,1,score_mean,1,0,-2.515885765942076e-17,1.3474487407666315e-13,2.515885765942076e-17 +GNO,-9.9999999999999995e-07,NA,0,1,1,score_mean,2,0,9.16692936836494e-17,1.4609307233232677e-14,3.6922504983993854e-16 +GNO,-9.9999999999999995e-07,NA,0,1,1,score_mean,3,0,-8.2787213554512201e-18,2.869129411614636e-14,4.2973190874987362e-17 +GNO,-9.9999999999999995e-07,NA,0,1,1,information,1,1,1.000000000003,1.9425705533691407e-12,2.2204460492503131e-16 +GNO,-9.9999999999999995e-07,NA,0,1,1,covariance,1,1,1.1666666666665091,3.6470165689171416e-12,0 +GNO,-9.9999999999999995e-07,NA,0,1,1,information,1,2,-2.4999999999601549e-06,1.7344659542721339e-14,5.6223082423425066e-17 +GNO,-9.9999999999999995e-07,NA,0,1,1,covariance,1,2,1.0000000000103786e-06,3.6470165689171416e-12,2.5875797747434807e-17 +GNO,-9.9999999999999995e-07,NA,0,1,1,information,1,3,-0.50000000000287492,3.425639368956698e-13,4.4408920985006262e-16 +GNO,-9.9999999999999995e-07,NA,0,1,1,covariance,1,3,0.33333333333310189,3.6470165689171416e-12,1.1102230246251565e-16 +GNO,-9.9999999999999995e-07,NA,0,1,1,information,2,1,-2.4999999999601549e-06,1.7344659542721339e-14,5.6223082423425066e-17 +GNO,-9.9999999999999995e-07,NA,0,1,1,covariance,2,1,1.0000000000103782e-06,3.6470165689171416e-12,2.5876221263908435e-17 +GNO,-9.9999999999999995e-07,NA,0,1,1,information,2,2,2.0000000000037503,7.6551313149756331e-13,8.8817841970012523e-16 +GNO,-9.9999999999999995e-07,NA,0,1,1,covariance,2,2,0.50000000000099998,3.6470165689171416e-12,2.2204460492503131e-16 +GNO,-9.9999999999999995e-07,NA,0,1,1,information,2,3,2.74999999978775e-06,2.8805135796265044e-14,4.152790782151021e-17 +GNO,-9.9999999999999995e-07,NA,0,1,1,covariance,2,3,-4.9999999993552288e-07,3.6470165689171416e-12,4.4728633570947397e-18 +GNO,-9.9999999999999995e-07,NA,0,1,1,information,3,1,-0.50000000000287492,3.425639368956698e-13,4.4408920985006262e-16 +GNO,-9.9999999999999995e-07,NA,0,1,1,covariance,3,1,0.33333333333310189,3.6470165689171416e-12,1.1102230246251565e-16 +GNO,-9.9999999999999995e-07,NA,0,1,1,information,3,2,2.74999999978775e-06,2.8805135796265044e-14,4.152790782151021e-17 +GNO,-9.9999999999999995e-07,NA,0,1,1,covariance,3,2,-4.9999999993552298e-07,3.6470165689171416e-12,4.4728633570947397e-18 +GNO,-9.9999999999999995e-07,NA,0,1,1,information,3,3,1.7500000000027913,9.6819962746727422e-13,1.1102230246251565e-15 +GNO,-9.9999999999999995e-07,NA,0,1,1,covariance,3,3,0.66666666666687058,3.6470165689171416e-12,2.2204460492503131e-16 +GNO,9.9999999999999995e-07,NA,0,1,1,score_mean,1,0,2.515885765942076e-17,1.3474487407666315e-13,2.515885765942076e-17 +GNO,9.9999999999999995e-07,NA,0,1,1,score_mean,2,0,9.16692936836494e-17,1.4609307233232677e-14,3.4146947422430962e-16 +GNO,9.9999999999999995e-07,NA,0,1,1,score_mean,3,0,8.2787213554512201e-18,2.869129411614636e-14,3.6034296971080134e-17 +GNO,9.9999999999999995e-07,NA,0,1,1,information,1,1,1.000000000003,1.9425705533691407e-12,2.2204460492503131e-16 +GNO,9.9999999999999995e-07,NA,0,1,1,covariance,1,1,1.1666666666665091,3.6470165689171416e-12,0 +GNO,9.9999999999999995e-07,NA,0,1,1,information,1,2,2.4999999999601549e-06,1.7344659542721339e-14,1.6724538488594072e-16 +GNO,9.9999999999999995e-07,NA,0,1,1,covariance,1,2,-1.0000000000103786e-06,3.6470165689171416e-12,1.0914252459432155e-16 +GNO,9.9999999999999995e-07,NA,0,1,1,information,1,3,-0.50000000000287492,3.425639368956698e-13,5.5511151231257827e-16 +GNO,9.9999999999999995e-07,NA,0,1,1,covariance,1,3,0.33333333333310189,3.6470165689171416e-12,1.6653345369377348e-16 +GNO,9.9999999999999995e-07,NA,0,1,1,information,2,1,2.4999999999601549e-06,1.7344659542721339e-14,1.6724538488594072e-16 +GNO,9.9999999999999995e-07,NA,0,1,1,covariance,2,1,-1.0000000000103782e-06,3.6470165689171416e-12,1.0914294811079518e-16 +GNO,9.9999999999999995e-07,NA,0,1,1,information,2,2,2.0000000000037503,7.6551313149756331e-13,8.8817841970012523e-16 +GNO,9.9999999999999995e-07,NA,0,1,1,covariance,2,2,0.50000000000099998,3.6470165689171416e-12,2.2204460492503131e-16 +GNO,9.9999999999999995e-07,NA,0,1,1,information,2,3,-2.74999999978775e-06,2.8805135796265044e-14,6.9494394641005444e-17 +GNO,9.9999999999999995e-07,NA,0,1,1,covariance,2,3,4.9999999993552288e-07,3.6470165689171416e-12,5.1038287874163087e-17 +GNO,9.9999999999999995e-07,NA,0,1,1,information,3,1,-0.50000000000287492,3.425639368956698e-13,5.5511151231257827e-16 +GNO,9.9999999999999995e-07,NA,0,1,1,covariance,3,1,0.33333333333310189,3.6470165689171416e-12,1.6653345369377348e-16 +GNO,9.9999999999999995e-07,NA,0,1,1,information,3,2,-2.74999999978775e-06,2.8805135796265044e-14,6.9494394641005444e-17 +GNO,9.9999999999999995e-07,NA,0,1,1,covariance,3,2,4.9999999993552298e-07,3.6470165689171416e-12,5.1038287874163087e-17 +GNO,9.9999999999999995e-07,NA,0,1,1,information,3,3,1.7500000000027913,9.6819962746727422e-13,1.1102230246251565e-15 +GNO,9.9999999999999995e-07,NA,0,1,1,covariance,3,3,0.66666666666687058,3.6470165689171416e-12,2.2204460492503131e-16 +GNO,-0.20000000000000001,NA,0,1,1,score_mean,1,0,9.9882125140227096e-18,6.9397781129274311e-14,1.0103408994849294e-16 +GNO,-0.20000000000000001,NA,0,1,1,score_mean,2,0,-5.6974824164113258e-17,1.5200829420078829e-14,1.3731420514528914e-16 +GNO,-0.20000000000000001,NA,0,1,1,score_mean,3,0,1.3357370765021415e-16,4.6088561407363565e-14,8.1532003370909933e-17 +GNO,-0.20000000000000001,NA,0,1,1,information,1,1,1.126618550381957,3.9328344433275145e-13,4.4408920985006262e-16 +GNO,-0.20000000000000001,NA,0,1,1,covariance,1,1,1.1604828547428099,6.4456339311483132e-12,0 +GNO,-0.20000000000000001,NA,0,1,1,information,1,2,-0.53208605177600543,4.2598332070351411e-14,3.3306690738754696e-16 +GNO,-0.20000000000000001,NA,0,1,1,covariance,1,2,0.20000000000000001,6.4456339311483132e-12,5.5511151231257827e-17 +GNO,-0.20000000000000001,NA,0,1,1,information,1,3,-0.62002757882651538,7.5042588626527028e-14,5.5511151231257827e-16 +GNO,-0.20000000000000001,NA,0,1,1,covariance,1,3,0.32418606536613409,6.4456339311483132e-12,3.8857805861880479e-16 +GNO,-0.20000000000000001,NA,0,1,1,information,2,1,-0.53208605177600543,4.2598332070351411e-14,3.3306690738754696e-16 +GNO,-0.20000000000000001,NA,0,1,1,covariance,2,1,0.20000000000000004,6.4456339311483132e-12,5.5511151231257827e-17 +GNO,-0.20000000000000001,NA,0,1,1,information,2,2,2.1553967582111317,3.4892354006417827e-12,8.8817841970012523e-16 +GNO,-0.20000000000000001,NA,0,1,1,covariance,2,2,0.54000000000000004,6.4456339311483132e-12,1.1102230246251565e-16 +GNO,-0.20000000000000001,NA,0,1,1,information,2,3,0.57497039078810097,3.8938198616231853e-13,1.1102230246251565e-16 +GNO,-0.20000000000000001,NA,0,1,1,covariance,2,3,-0.09999999999999998,6.4456339311483132e-12,1.5265566588595902e-16 +GNO,-0.20000000000000001,NA,0,1,1,information,3,1,-0.62002757882651538,7.5042588626527028e-14,5.5511151231257827e-16 +GNO,-0.20000000000000001,NA,0,1,1,covariance,3,1,0.32418606536613409,6.4456339311483132e-12,4.4408920985006262e-16 +GNO,-0.20000000000000001,NA,0,1,1,information,3,2,0.57497039078810097,3.8938198616231853e-13,1.1102230246251565e-16 +GNO,-0.20000000000000001,NA,0,1,1,covariance,3,2,-0.09999999999999998,6.4456339311483132e-12,1.3877787807814457e-16 +GNO,-0.20000000000000001,NA,0,1,1,information,3,3,1.8647849526027149,1.2881022703014334e-12,2.2204460492503131e-16 +GNO,-0.20000000000000001,NA,0,1,1,covariance,3,3,0.67487746429986739,6.4456339311483132e-12,2.2204460492503131e-16 +GNO,0.20000000000000001,NA,0,1,1,score_mean,1,0,-9.9882125140227096e-18,6.9397781129274311e-14,4.5522938717235117e-17 +GNO,0.20000000000000001,NA,0,1,1,score_mean,2,0,-5.6974824164113258e-17,1.5200829420078829e-14,1.7894756856873251e-16 +GNO,0.20000000000000001,NA,0,1,1,score_mean,3,0,-1.3357370765021415e-16,4.6088561407363565e-14,3.9898639947466563e-17 +GNO,0.20000000000000001,NA,0,1,1,information,1,1,1.126618550381957,3.9328344433275145e-13,4.4408920985006262e-16 +GNO,0.20000000000000001,NA,0,1,1,covariance,1,1,1.1604828547428099,6.4456339311483132e-12,0 +GNO,0.20000000000000001,NA,0,1,1,information,1,2,0.53208605177600543,4.2598332070351411e-14,3.3306690738754696e-16 +GNO,0.20000000000000001,NA,0,1,1,covariance,1,2,-0.20000000000000001,6.4456339311483132e-12,5.5511151231257827e-17 +GNO,0.20000000000000001,NA,0,1,1,information,1,3,-0.62002757882651538,7.5042588626527028e-14,5.5511151231257827e-16 +GNO,0.20000000000000001,NA,0,1,1,covariance,1,3,0.32418606536613409,6.4456339311483132e-12,3.8857805861880479e-16 +GNO,0.20000000000000001,NA,0,1,1,information,2,1,0.53208605177600543,4.2598332070351411e-14,3.3306690738754696e-16 +GNO,0.20000000000000001,NA,0,1,1,covariance,2,1,-0.20000000000000004,6.4456339311483132e-12,5.5511151231257827e-17 +GNO,0.20000000000000001,NA,0,1,1,information,2,2,2.1553967582111317,3.4892354006417827e-12,8.8817841970012523e-16 +GNO,0.20000000000000001,NA,0,1,1,covariance,2,2,0.54000000000000004,6.4456339311483132e-12,1.1102230246251565e-16 +GNO,0.20000000000000001,NA,0,1,1,information,2,3,-0.57497039078810097,3.8938198616231853e-13,1.1102230246251565e-16 +GNO,0.20000000000000001,NA,0,1,1,covariance,2,3,0.09999999999999998,6.4456339311483132e-12,1.5265566588595902e-16 +GNO,0.20000000000000001,NA,0,1,1,information,3,1,-0.62002757882651538,7.5042588626527028e-14,5.5511151231257827e-16 +GNO,0.20000000000000001,NA,0,1,1,covariance,3,1,0.32418606536613409,6.4456339311483132e-12,4.4408920985006262e-16 +GNO,0.20000000000000001,NA,0,1,1,information,3,2,-0.57497039078810097,3.8938198616231853e-13,1.1102230246251565e-16 +GNO,0.20000000000000001,NA,0,1,1,covariance,3,2,0.09999999999999998,6.4456339311483132e-12,1.3877787807814457e-16 +GNO,0.20000000000000001,NA,0,1,1,information,3,3,1.8647849526027149,1.2881022703014334e-12,2.2204460492503131e-16 +GNO,0.20000000000000001,NA,0,1,1,covariance,3,3,0.67487746429986739,6.4456339311483132e-12,2.2204460492503131e-16 +GNO,-1,NA,0,1,1,score_mean,1,0,-4.5102810375396984e-17,1.3458266784668782e-14,6.0021432268797525e-16 +GNO,-1,NA,0,1,1,score_mean,2,0,4.5102810375396984e-17,1.3572758091554548e-14,4.891920202254596e-16 +GNO,-1,NA,0,1,1,score_mean,3,0,5.4210108624275222e-17,1.1324159398515834e-13,2.0079424234431542e-16 +GNO,-1,NA,0,1,1,information,1,1,14.778112197861303,2.1004639009308756e-11,1.2434497875801753e-14 +GNO,-1,NA,0,1,1,covariance,1,1,1.0635395349949464,2.7064447157493955e-10,7.5495165674510645e-15 +GNO,-1,NA,0,1,1,information,1,2,-13.129390927161172,1.7179570726652612e-11,8.8817841970012523e-15 +GNO,-1,NA,0,1,1,covariance,1,2,0.99999999999999234,2.7064447157493955e-10,1.0547118733938987e-14 +GNO,-1,NA,0,1,1,information,1,3,-9.8319483857609171,1.5960551777435551e-11,1.0658141036401503e-14 +GNO,-1,NA,0,1,1,covariance,1,3,0.16148535219681237,2.7064447157493955e-10,2.6922908347160046e-15 +GNO,-1,NA,0,1,1,information,2,1,-13.129390927161172,1.7179570726652612e-11,8.8817841970012523e-15 +GNO,-1,NA,0,1,1,covariance,2,1,0.99999999999999234,2.7064447157493955e-10,1.1657341758564144e-14 +GNO,-1,NA,0,1,1,information,2,2,12.480669656461046,2.2421136482647423e-11,1.2434497875801753e-14 +GNO,-1,NA,0,1,1,covariance,2,2,1.4999999999999902,2.7064447157493955e-10,1.3766765505351941e-14 +GNO,-1,NA,0,1,1,information,2,3,9.183227115060788,1.2453265937401457e-11,8.8817841970012523e-15 +GNO,-1,NA,0,1,1,covariance,2,3,-0.499999999999998,2.7064447157493955e-10,1.3322676295501878e-15 +GNO,-1,NA,0,1,1,information,3,1,-9.8319483857609171,1.5960551777435551e-11,1.0658141036401503e-14 +GNO,-1,NA,0,1,1,covariance,3,1,0.16148535219681232,2.7064447157493955e-10,4.4408920985006262e-15 +GNO,-1,NA,0,1,1,information,3,2,9.183227115060788,1.2453265937401457e-11,8.8817841970012523e-15 +GNO,-1,NA,0,1,1,covariance,3,2,-0.49999999999999806,2.7064447157493955e-10,3.2751579226442118e-15 +GNO,-1,NA,0,1,1,information,3,3,7.8857845736605316,5.591886198380185e-12,5.3290705182007514e-15 +GNO,-1,NA,0,1,1,covariance,3,3,0.91041406702456473,2.7064447157493955e-10,1.9984014443252818e-15 +GNO,1,NA,0,1,1,score_mean,1,0,4.5102810375396984e-17,1.3458266784668782e-14,6.0021432268797525e-16 +GNO,1,NA,0,1,1,score_mean,2,0,4.5102810375396984e-17,1.3572758091554548e-14,4.891920202254596e-16 +GNO,1,NA,0,1,1,score_mean,3,0,-5.4210108624275222e-17,1.1324159398515834e-13,1.95590071916385e-16 +GNO,1,NA,0,1,1,information,1,1,14.778112197861303,2.1004639009308756e-11,1.2434497875801753e-14 +GNO,1,NA,0,1,1,covariance,1,1,1.0635395349949464,2.7064447157493955e-10,7.5495165674510645e-15 +GNO,1,NA,0,1,1,information,1,2,13.129390927161172,1.7179570726652612e-11,8.8817841970012523e-15 +GNO,1,NA,0,1,1,covariance,1,2,-0.99999999999999234,2.7064447157493955e-10,1.0547118733938987e-14 +GNO,1,NA,0,1,1,information,1,3,-9.8319483857609171,1.5960551777435551e-11,1.0658141036401503e-14 +GNO,1,NA,0,1,1,covariance,1,3,0.16148535219681237,2.7064447157493955e-10,2.6922908347160046e-15 +GNO,1,NA,0,1,1,information,2,1,13.129390927161172,1.7179570726652612e-11,8.8817841970012523e-15 +GNO,1,NA,0,1,1,covariance,2,1,-0.99999999999999234,2.7064447157493955e-10,1.1657341758564144e-14 +GNO,1,NA,0,1,1,information,2,2,12.480669656461046,2.2421136482647423e-11,1.2434497875801753e-14 +GNO,1,NA,0,1,1,covariance,2,2,1.4999999999999902,2.7064447157493955e-10,1.3766765505351941e-14 +GNO,1,NA,0,1,1,information,2,3,-9.183227115060788,1.2453265937401457e-11,8.8817841970012523e-15 +GNO,1,NA,0,1,1,covariance,2,3,0.499999999999998,2.7064447157493955e-10,1.3322676295501878e-15 +GNO,1,NA,0,1,1,information,3,1,-9.8319483857609171,1.5960551777435551e-11,1.0658141036401503e-14 +GNO,1,NA,0,1,1,covariance,3,1,0.16148535219681232,2.7064447157493955e-10,4.4408920985006262e-15 +GNO,1,NA,0,1,1,information,3,2,-9.183227115060788,1.2453265937401457e-11,8.8817841970012523e-15 +GNO,1,NA,0,1,1,covariance,3,2,0.49999999999999806,2.7064447157493955e-10,3.2751579226442118e-15 +GNO,1,NA,0,1,1,information,3,3,7.8857845736605316,5.591886198380185e-12,5.3290705182007514e-15 +GNO,1,NA,0,1,1,covariance,3,3,0.91041406702456473,2.7064447157493955e-10,1.9984014443252818e-15 +GNO,-2,NA,0,1,1,score_mean,1,0,1.1102230246251565e-16,1.753589474603726e-13,3.4416913763379853e-15 +GNO,-2,NA,0,1,1,score_mean,2,0,-4.163336342344337e-17,1.7276724438607776e-13,1.0824674490095276e-15 +GNO,-2,NA,0,1,1,score_mean,3,0,-6.9388939039072284e-18,7.7352099103511779e-14,4.8572257327350599e-16 +GNO,-2,NA,0,1,1,information,1,1,14904.789935208642,1.1757743938410821e-08,2.9103830456733704e-11 +GNO,-2,NA,0,1,1,covariance,1,1,1.0028320881413928,4.8941978127198323e-07,1.8305357230019581e-11 +GNO,-2,NA,0,1,1,information,1,2,-7448.7004395548565,5.8554275653436792e-09,6.3664629124104977e-12 +GNO,-2,NA,0,1,1,covariance,1,2,2.0000000000041092,4.8941978127198323e-07,3.6966429917129062e-11 +GNO,-2,NA,0,1,1,information,1,3,-3709.5721075795673,2.9023750901131011e-09,4.5474735088646412e-12 +GNO,-2,NA,0,1,1,covariance,1,3,0.013101439595815579,4.8941978127198323e-07,6.8230317229467374e-13 +GNO,-2,NA,0,1,1,information,2,1,-7448.7004395548565,5.8554275653436792e-09,6.3664629124104977e-12 +GNO,-2,NA,0,1,1,covariance,2,1,2.0000000000041092,4.8941978127198323e-07,3.6498359889947096e-11 +GNO,-2,NA,0,1,1,information,2,2,3722.7529557526955,2.9177008987441205e-09,3.637978807091713e-12 +GNO,-2,NA,0,1,1,covariance,2,2,4.5000000000079581,4.8941978127198323e-07,7.2986949817277491e-11 +GNO,-2,NA,0,1,1,information,2,3,1853.9874217774172,1.446962762977124e-09,2.2737367544323206e-12 +GNO,-2,NA,0,1,1,covariance,2,3,-0.99999999999946931,4.8941978127198323e-07,8.2600593032111647e-14 +GNO,-2,NA,0,1,1,information,3,1,-3709.5721075795673,2.9023750901131011e-09,4.5474735088646412e-12 +GNO,-2,NA,0,1,1,covariance,3,1,0.013101439595815527,4.8941978127198323e-07,2.5787878776828421e-13 +GNO,-2,NA,0,1,1,information,3,2,1853.9874217774172,1.446962762977124e-09,2.2737367544323206e-12 +GNO,-2,NA,0,1,1,covariance,3,2,-0.99999999999946942,4.8941978127198323e-07,1.9637624859569769e-12 +GNO,-2,NA,0,1,1,information,3,3,923.79918283924326,7.1960126895214905e-10,7.9580786405131221e-13 +GNO,-2,NA,0,1,1,covariance,3,3,2.0606081841506305,4.8941978127198323e-07,2.9065638784686598e-12 +GNO,2,NA,0,1,1,score_mean,1,0,-1.1102230246251565e-16,1.753589474603726e-13,3.4416913763379853e-15 +GNO,2,NA,0,1,1,score_mean,2,0,-4.163336342344337e-17,1.7276724438607776e-13,1.0824674490095276e-15 +GNO,2,NA,0,1,1,score_mean,3,0,6.9388939039072284e-18,7.7352099103511779e-14,5.4123372450476381e-16 +GNO,2,NA,0,1,1,information,1,1,14904.789935208642,1.1757743938410821e-08,2.9103830456733704e-11 +GNO,2,NA,0,1,1,covariance,1,1,1.0028320881413928,4.8941978127198323e-07,1.8305357230019581e-11 +GNO,2,NA,0,1,1,information,1,2,7448.7004395548565,5.8554275653436792e-09,6.3664629124104977e-12 +GNO,2,NA,0,1,1,covariance,1,2,-2.0000000000041092,4.8941978127198323e-07,3.6966429917129062e-11 +GNO,2,NA,0,1,1,information,1,3,-3709.5721075795673,2.9023750901131011e-09,4.5474735088646412e-12 +GNO,2,NA,0,1,1,covariance,1,3,0.013101439595815579,4.8941978127198323e-07,6.8230317229467374e-13 +GNO,2,NA,0,1,1,information,2,1,7448.7004395548565,5.8554275653436792e-09,6.3664629124104977e-12 +GNO,2,NA,0,1,1,covariance,2,1,-2.0000000000041092,4.8941978127198323e-07,3.6498359889947096e-11 +GNO,2,NA,0,1,1,information,2,2,3722.7529557526955,2.9177008987441205e-09,3.637978807091713e-12 +GNO,2,NA,0,1,1,covariance,2,2,4.5000000000079581,4.8941978127198323e-07,7.2986949817277491e-11 +GNO,2,NA,0,1,1,information,2,3,-1853.9874217774172,1.446962762977124e-09,2.2737367544323206e-12 +GNO,2,NA,0,1,1,covariance,2,3,0.99999999999946931,4.8941978127198323e-07,8.2600593032111647e-14 +GNO,2,NA,0,1,1,information,3,1,-3709.5721075795673,2.9023750901131011e-09,4.5474735088646412e-12 +GNO,2,NA,0,1,1,covariance,3,1,0.013101439595815527,4.8941978127198323e-07,2.5787878776828421e-13 +GNO,2,NA,0,1,1,information,3,2,-1853.9874217774172,1.446962762977124e-09,2.2737367544323206e-12 +GNO,2,NA,0,1,1,covariance,3,2,0.99999999999946942,4.8941978127198323e-07,1.9637624859569769e-12 +GNO,2,NA,0,1,1,information,3,3,923.79918283924326,7.1960126895214905e-10,7.9580786405131221e-13 +GNO,2,NA,0,1,1,covariance,3,3,2.0606081841506305,4.8941978127198323e-07,2.9065638784686598e-12 +GLO,0,-1,0,1,1,score_mean,1,0,0,5.5595497795500773e-15,2.7755575615628914e-17 +GLO,0,-1,0,1,1,score_mean,2,0,-1.1102230246251565e-16,9.9620509752748834e-15,0 +GLO,0,-1,0,1,1,score_mean,3,0,-2.0816681711721685e-17,9.7534272794415726e-15,9.0205620750793969e-17 +GLO,0,-1,0,1,1,information,1,1,0.33333333333333326,1.3373867036071658e-14,1.1102230246251565e-16 +GLO,0,-1,0,1,1,covariance,1,1,3.113972242232403,5.4496748817983322e-13,4.4408920985006262e-16 +GLO,0,-1,0,1,1,information,1,2,-2.7755575615628914e-17,5.7876650277494293e-15,5.5511151231257827e-17 +GLO,0,-1,0,1,1,covariance,1,2,4.6721938141357549e-17,5.4496748817983322e-13,1.0716441982653002e-16 +GLO,0,-1,0,1,1,information,1,3,-0.21497802228274204,7.0378899485062419e-15,1.1102230246251565e-16 +GLO,0,-1,0,1,1,covariance,1,3,0.17671921532906712,5.4496748817983322e-13,2.7755575615628914e-17 +GLO,0,-1,0,1,1,information,2,1,-2.7755575615628914e-17,5.7876650277494293e-15,5.5511151231257827e-17 +GLO,0,-1,0,1,1,covariance,2,1,4.6721938141357555e-17,5.4496748817983322e-13,1.0716441982653002e-16 +GLO,0,-1,0,1,1,information,2,2,1.429956044565484,1.5874432157400921e-14,4.4408920985006262e-16 +GLO,0,-1,0,1,1,covariance,2,2,0.69932219511255445,5.4496748817983322e-13,2.2204460492503131e-16 +GLO,0,-1,0,1,1,information,2,3,1.1102230246251565e-16,2.0912533482946806e-14,1.1102230246251565e-16 +GLO,0,-1,0,1,1,covariance,2,3,-1.7844199350928454e-17,5.4496748817983322e-13,1.4414063464974717e-17 +GLO,0,-1,0,1,1,information,3,1,-0.21497802228274204,7.0378899485062419e-15,1.1102230246251565e-16 +GLO,0,-1,0,1,1,covariance,3,1,0.17671921532906712,5.4496748817983322e-13,0 +GLO,0,-1,0,1,1,information,3,2,1.1102230246251565e-16,2.0912533482946806e-14,1.1102230246251565e-16 +GLO,0,-1,0,1,1,covariance,3,2,-1.7844199350928454e-17,5.4496748817983322e-13,1.4414063464974717e-17 +GLO,0,-1,0,1,1,information,3,3,3.7881313179889826,4.2418187056403696e-14,8.8817841970012523e-16 +GLO,0,-1,0,1,1,covariance,3,3,0.27401128954574955,5.4496748817983322e-13,1.1102230246251565e-16 +GLO,-9.9999999999999995e-07,-1,0,1,1,score_mean,1,0,-1.1102230246251565e-16,3.5869895496659745e-13,2.7755575615628914e-17 +GLO,-9.9999999999999995e-07,-1,0,1,1,score_mean,2,0,4.163336342344337e-17,9.9626612479790534e-15,1.3877787807814457e-17 +GLO,-9.9999999999999995e-07,-1,0,1,1,score_mean,3,0,2.0122792321330962e-16,9.7513306157174569e-15,6.9388939039072284e-18 +GLO,-9.9999999999999995e-07,-1,0,1,1,information,1,1,0.33333333333519316,1.3408326943639253e-14,1.6653345369377348e-16 +GLO,-9.9999999999999995e-07,-1,0,1,1,covariance,1,1,3.113972242231712,5.4493479775322602e-13,1.7763568394002505e-15 +GLO,-9.9999999999999995e-07,-1,0,1,1,information,1,2,-1.6449340669577595e-06,5.8069439883685778e-15,2.7755575615628914e-17 +GLO,-9.9999999999999995e-07,-1,0,1,1,covariance,1,2,2.8696337923756957e-06,5.4493479775322602e-13,1.0160160202315333e-16 +GLO,-9.9999999999999995e-07,-1,0,1,1,information,1,3,-0.21497802228859036,7.0390598142000892e-15,5.5511151231257827e-17 +GLO,-9.9999999999999995e-07,-1,0,1,1,covariance,1,3,0.17671921532817111,5.4493479775322602e-13,8.3266726846886741e-17 +GLO,-9.9999999999999995e-07,-1,0,1,1,information,2,1,-1.6449340669577595e-06,5.8069439883685778e-15,2.7755575615628914e-17 +GLO,-9.9999999999999995e-07,-1,0,1,1,covariance,2,1,2.8696337923756953e-06,5.4493479775322602e-13,1.0160160202315333e-16 +GLO,-9.9999999999999995e-07,-1,0,1,1,information,2,2,1.4299560445732262,1.5876522597352676e-14,6.6613381477509392e-16 +GLO,-9.9999999999999995e-07,-1,0,1,1,covariance,2,2,0.6993221951157037,5.4493479775322602e-13,3.3306690738754696e-16 +GLO,-9.9999999999999995e-07,-1,0,1,1,information,2,3,5.7652408415043155e-06,2.0900237088594147e-14,3.3306690738754696e-16 +GLO,-9.9999999999999995e-07,-1,0,1,1,covariance,2,3,-9.0146100982102753e-07,5.4493479775322602e-13,6.7253463099925786e-17 +GLO,-9.9999999999999995e-07,-1,0,1,1,information,3,1,-0.21497802228859036,7.0390598142000892e-15,5.5511151231257827e-17 +GLO,-9.9999999999999995e-07,-1,0,1,1,covariance,3,1,0.17671921532817109,5.4493479775322602e-13,8.3266726846886741e-17 +GLO,-9.9999999999999995e-07,-1,0,1,1,information,3,2,5.7652408415043155e-06,2.0900237088594147e-14,3.3306690738754696e-16 +GLO,-9.9999999999999995e-07,-1,0,1,1,covariance,3,2,-9.0146100982102764e-07,5.4493479775322602e-13,6.7253674858162599e-17 +GLO,-9.9999999999999995e-07,-1,0,1,1,information,3,3,3.7881313180167835,4.241887543520958e-14,8.8817841970012523e-16 +GLO,-9.9999999999999995e-07,-1,0,1,1,covariance,3,3,0.27401128954533249,5.4493479775322602e-13,5.5511151231257827e-17 +GLO,9.9999999999999995e-07,-1,0,1,1,score_mean,1,0,-2.7755575615628914e-17,3.5869805634580822e-13,2.7755575615628914e-17 +GLO,9.9999999999999995e-07,-1,0,1,1,score_mean,2,0,1.3877787807814457e-17,9.9648521810377463e-15,9.7144514654701197e-17 +GLO,9.9999999999999995e-07,-1,0,1,1,score_mean,3,0,2.7755575615628914e-17,9.7549155979408785e-15,4.163336342344337e-17 +GLO,9.9999999999999995e-07,-1,0,1,1,information,1,1,0.33333333333519322,1.3409357663622054e-14,0 +GLO,9.9999999999999995e-07,-1,0,1,1,covariance,1,1,3.1139722422317115,5.4465616181672512e-13,0 +GLO,9.9999999999999995e-07,-1,0,1,1,information,1,2,1.6449340668189816e-06,5.8110543482509412e-15,5.5511151231257827e-17 +GLO,9.9999999999999995e-07,-1,0,1,1,covariance,1,2,-2.8696337921695266e-06,5.4465616181672512e-13,1.0716322035482506e-16 +GLO,9.9999999999999995e-07,-1,0,1,1,information,1,3,-0.21497802228859036,7.0378403535258295e-15,0 +GLO,9.9999999999999995e-07,-1,0,1,1,covariance,1,3,0.17671921532817106,5.4465616181672512e-13,2.7755575615628914e-17 +GLO,9.9999999999999995e-07,-1,0,1,1,information,2,1,1.6449340668189816e-06,5.8110543482509412e-15,5.5511151231257827e-17 +GLO,9.9999999999999995e-07,-1,0,1,1,covariance,2,1,-2.8696337921695266e-06,5.4465616181672512e-13,1.0716279683835143e-16 +GLO,9.9999999999999995e-07,-1,0,1,1,information,2,2,1.4299560445732262,1.5876476613720034e-14,6.6613381477509392e-16 +GLO,9.9999999999999995e-07,-1,0,1,1,covariance,2,2,0.6993221951157037,5.4465616181672512e-13,3.3306690738754696e-16 +GLO,9.9999999999999995e-07,-1,0,1,1,information,2,3,-5.7652408407271594e-06,2.0864715121887759e-14,1.1102230246251565e-16 +GLO,9.9999999999999995e-07,-1,0,1,1,covariance,2,3,9.014610096892579e-07,5.4465616181672512e-13,1.4413430267834395e-17 +GLO,9.9999999999999995e-07,-1,0,1,1,information,3,1,-0.21497802228859036,7.0378403535258295e-15,0 +GLO,9.9999999999999995e-07,-1,0,1,1,covariance,3,1,0.17671921532817106,5.4465616181672512e-13,5.5511151231257827e-17 +GLO,9.9999999999999995e-07,-1,0,1,1,information,3,2,-5.7652408407271594e-06,2.0864715121887759e-14,1.1102230246251565e-16 +GLO,9.9999999999999995e-07,-1,0,1,1,covariance,3,2,9.014610096892579e-07,5.4465616181672512e-13,1.4413536146952802e-17 +GLO,9.9999999999999995e-07,-1,0,1,1,information,3,3,3.7881313180167835,4.2418871590295571e-14,8.8817841970012523e-16 +GLO,9.9999999999999995e-07,-1,0,1,1,covariance,3,3,0.27401128954533249,5.4465616181672512e-13,5.5511151231257827e-17 +GLO,-0.20000000000000001,-1,0,1,1,score_mean,1,0,-8.3266726846886741e-17,2.8528975409731289e-13,2.7755575615628914e-17 +GLO,-0.20000000000000001,-1,0,1,1,score_mean,2,0,2.7755575615628914e-17,1.0188877802985553e-14,2.4980018054066022e-16 +GLO,-0.20000000000000001,-1,0,1,1,score_mean,3,0,6.9388939039072284e-17,9.996703294690542e-15,1.5126788710517758e-15 +GLO,-0.20000000000000001,-1,0,1,1,information,1,1,0.42281804789684785,1.0417482564665784e-13,1.6653345369377348e-16 +GLO,-0.20000000000000001,-1,0,1,1,covariance,1,1,3.0880088925659779,6.9041230010259968e-12,4.4408920985006262e-16 +GLO,-0.20000000000000001,-1,0,1,1,information,1,2,-0.40375530809928734,4.2483583191631484e-14,2.2204460492503131e-16 +GLO,-0.20000000000000001,-1,0,1,1,covariance,1,2,0.5788445665366807,6.9041230010259968e-12,3.3306690738754696e-16 +GLO,-0.20000000000000001,-1,0,1,1,information,1,3,-0.50632393410685061,1.0223907949742628e-14,1.2212453270876722e-15 +GLO,-0.20000000000000001,-1,0,1,1,covariance,1,3,0.14211124675779777,6.9041230010259968e-12,1.3045120539345589e-15 +GLO,-0.20000000000000001,-1,0,1,1,information,2,1,-0.40375530809928734,4.2483583191631484e-14,2.2204460492503131e-16 +GLO,-0.20000000000000001,-1,0,1,1,covariance,2,1,0.57884456653668082,6.9041230010259968e-12,3.3306690738754696e-16 +GLO,-0.20000000000000001,-1,0,1,1,information,2,2,1.8004352169050084,2.0025931659980822e-14,4.4408920985006262e-16 +GLO,-0.20000000000000001,-1,0,1,1,covariance,2,2,0.82440750356232795,6.9041230010259968e-12,5.5511151231257827e-16 +GLO,-0.20000000000000001,-1,0,1,1,information,2,3,1.4399130525771162,2.8159271575060025e-14,0 +GLO,-0.20000000000000001,-1,0,1,1,covariance,2,3,-0.17402490778332608,6.9041230010259968e-12,2.4980018054066022e-16 +GLO,-0.20000000000000001,-1,0,1,1,information,3,1,-0.50632393410685061,1.0223907949742628e-14,1.2212453270876722e-15 +GLO,-0.20000000000000001,-1,0,1,1,covariance,3,1,0.14211124675779815,6.9041230010259968e-12,9.4368957093138306e-16 +GLO,-0.20000000000000001,-1,0,1,1,information,3,2,1.4399130525771162,2.8159271575060025e-14,0 +GLO,-0.20000000000000001,-1,0,1,1,covariance,3,2,-0.17402490778332605,6.9041230010259968e-12,1.6653345369377348e-16 +GLO,-0.20000000000000001,-1,0,1,1,information,3,3,5.1371512172830531,6.5999860437025368e-13,1.7763568394002505e-15 +GLO,-0.20000000000000001,-1,0,1,1,covariance,3,3,0.25744522709016981,6.9041230010259968e-12,1.6653345369377348e-16 +GLO,0.20000000000000001,-1,0,1,1,score_mean,1,0,5.5511151231257827e-17,2.8528487499521722e-13,0 +GLO,0.20000000000000001,-1,0,1,1,score_mean,2,0,-8.3266726846886741e-17,1.0184268922708439e-14,0 +GLO,0.20000000000000001,-1,0,1,1,score_mean,3,0,4.3715031594615539e-16,9.9936553895907295e-15,4.7184478546569153e-16 +GLO,0.20000000000000001,-1,0,1,1,information,1,1,0.42281804789684785,1.0417546871124578e-13,5.5511151231257827e-17 +GLO,0.20000000000000001,-1,0,1,1,covariance,1,1,3.0880088925659774,6.9033801796252367e-12,0 +GLO,0.20000000000000001,-1,0,1,1,information,1,2,0.40375530809928734,4.2483862050215942e-14,5.5511151231257827e-17 +GLO,0.20000000000000001,-1,0,1,1,covariance,1,2,-0.5788445665366807,6.9033801796252367e-12,2.2204460492503131e-16 +GLO,0.20000000000000001,-1,0,1,1,information,1,3,-0.50632393410685084,1.0213503163508041e-14,0 +GLO,0.20000000000000001,-1,0,1,1,covariance,1,3,0.14211124675779771,6.9033801796252367e-12,8.6042284408449632e-16 +GLO,0.20000000000000001,-1,0,1,1,information,2,1,0.40375530809928734,4.2483862050215942e-14,5.5511151231257827e-17 +GLO,0.20000000000000001,-1,0,1,1,covariance,2,1,-0.57884456653668059,6.9033801796252367e-12,1.1102230246251565e-16 +GLO,0.20000000000000001,-1,0,1,1,information,2,2,1.8004352169050089,2.0026902428011473e-14,0 +GLO,0.20000000000000001,-1,0,1,1,covariance,2,2,0.82440750356232784,6.9033801796252367e-12,4.4408920985006262e-16 +GLO,0.20000000000000001,-1,0,1,1,information,2,3,-1.4399130525771171,2.8130463515225329e-14,1.3322676295501878e-15 +GLO,0.20000000000000001,-1,0,1,1,covariance,2,3,0.17402490778332644,6.9033801796252367e-12,7.4940054162198066e-16 +GLO,0.20000000000000001,-1,0,1,1,information,3,1,-0.50632393410685084,1.0213503163508041e-14,0 +GLO,0.20000000000000001,-1,0,1,1,covariance,3,1,0.14211124675779824,6.9033801796252367e-12,5.5511151231257827e-17 +GLO,0.20000000000000001,-1,0,1,1,information,3,2,-1.4399130525771171,2.8130463515225329e-14,1.3322676295501878e-15 +GLO,0.20000000000000001,-1,0,1,1,covariance,3,2,0.17402490778332616,6.9033801796252367e-12,4.163336342344337e-16 +GLO,0.20000000000000001,-1,0,1,1,information,3,3,5.1371512172830531,6.5993031498592066e-13,1.7763568394002505e-15 +GLO,0.20000000000000001,-1,0,1,1,covariance,3,3,0.25744522709016993,6.9033801796252367e-12,2.7755575615628914e-16 +GLO,-0.40000000000000002,-1,0,1,1,score_mean,1,0,-5.5511151231257827e-17,5.190518001652141e-14,5.5511151231257827e-17 +GLO,-0.40000000000000002,-1,0,1,1,score_mean,2,0,2.7755575615628914e-17,1.2534778573300287e-13,9.7144514654701197e-17 +GLO,-0.40000000000000002,-1,0,1,1,score_mean,3,0,6.609296443471635e-16,1.0768726765151763e-14,5.8113236445223038e-16 +GLO,-0.40000000000000002,-1,0,1,1,information,1,1,1.1972344519694664,1.4088465383857739e-12,1.5765166949677223e-14 +GLO,-0.40000000000000002,-1,0,1,1,covariance,1,1,3.0287141639852071,4.3888007504562228e-10,1.3100631690576847e-13 +GLO,-0.40000000000000002,-1,0,1,1,information,1,2,-2.0681716501493113,3.6331552082422525e-12,3.8635761256955448e-14 +GLO,-0.40000000000000002,-1,0,1,1,covariance,1,2,1.1838112557185685,4.3888007504562228e-10,1.3500311979441904e-13 +GLO,-0.40000000000000002,-1,0,1,1,information,1,3,-3.3618280941867988,6.1357084519940403e-12,2.2204460492503131e-15 +GLO,-0.40000000000000002,-1,0,1,1,covariance,1,3,0.052874822598324579,4.3888007504562228e-10,3.5964287103951165e-14 +GLO,-0.40000000000000002,-1,0,1,1,information,2,1,-2.0681716501493113,3.6331552082422525e-12,3.8635761256955448e-14 +GLO,-0.40000000000000002,-1,0,1,1,covariance,2,1,1.1838112557185685,4.3888007504562228e-10,1.3455903058456897e-13 +GLO,-0.40000000000000002,-1,0,1,1,information,2,2,4.94147625927073,5.1285138172511935e-12,2.6645352591003757e-15 +GLO,-0.40000000000000002,-1,0,1,1,covariance,2,2,1.1884492392272321,4.3888007504562228e-10,8.5709217501062085e-14 +GLO,-0.40000000000000002,-1,0,1,1,information,2,3,7.8321880702106128,1.1100877425892034e-11,7.1054273576010019e-15 +GLO,-0.40000000000000002,-1,0,1,1,covariance,2,3,-0.30953914807101085,4.3888007504562228e-10,1.2878587085651816e-14 +GLO,-0.40000000000000002,-1,0,1,1,information,3,1,-3.3618280941867988,6.1357084519940403e-12,2.2204460492503131e-15 +GLO,-0.40000000000000002,-1,0,1,1,covariance,3,1,0.052874822598324607,4.3888007504562228e-10,3.582550922587302e-14 +GLO,-0.40000000000000002,-1,0,1,1,information,3,2,7.8321880702106128,1.1100877425892034e-11,7.1054273576010019e-15 +GLO,-0.40000000000000002,-1,0,1,1,covariance,3,2,-0.30953914807101085,4.3888007504562228e-10,1.2712053631958042e-14 +GLO,-0.40000000000000002,-1,0,1,1,information,3,3,17.213939009145275,2.7562912710420496e-11,1.7763568394002505e-14 +GLO,-0.40000000000000002,-1,0,1,1,covariance,3,3,0.20925628265311105,4.3888007504562228e-10,1.2490009027033011e-15 +GLO,0.40000000000000002,-1,0,1,1,score_mean,1,0,-2.7755575615628914e-17,5.1904553785282903e-14,8.3266726846886741e-17 +GLO,0.40000000000000002,-1,0,1,1,score_mean,2,0,1.3877787807814457e-17,1.2534684018911071e-13,1.3877787807814457e-17 +GLO,0.40000000000000002,-1,0,1,1,score_mean,3,0,-7.9797279894933126e-17,1.0767481159232199e-14,2.0816681711721685e-17 +GLO,0.40000000000000002,-1,0,1,1,information,1,1,1.197234451969466,1.408843861815332e-12,1.5543122344752192e-14 +GLO,0.40000000000000002,-1,0,1,1,covariance,1,1,3.0287141639852089,4.3885090611965931e-10,1.2789769243681803e-13 +GLO,0.40000000000000002,-1,0,1,1,information,1,2,2.0681716501493104,3.6331579107544694e-12,3.8191672047105385e-14 +GLO,0.40000000000000002,-1,0,1,1,covariance,1,2,-1.1838112557185709,4.3885090611965931e-10,1.3145040611561853e-13 +GLO,0.40000000000000002,-1,0,1,1,information,1,3,-3.3618280941867953,6.1370424512254564e-12,2.6645352591003757e-15 +GLO,0.40000000000000002,-1,0,1,1,covariance,1,3,0.052874822598323712,4.3885090611965931e-10,3.4652836156112699e-14 +GLO,0.40000000000000002,-1,0,1,1,information,2,1,2.0681716501493104,3.6331579107544694e-12,3.8191672047105385e-14 +GLO,0.40000000000000002,-1,0,1,1,covariance,2,1,-1.1838112557185703,4.3885090611965931e-10,1.3167245072054357e-13 +GLO,0.40000000000000002,-1,0,1,1,information,2,2,4.9414762592707273,5.1293604436086615e-12,1.7763568394002505e-15 +GLO,0.40000000000000002,-1,0,1,1,covariance,2,2,1.1884492392272337,4.3885090611965931e-10,8.1934459217336553e-14 +GLO,0.40000000000000002,-1,0,1,1,information,2,3,-7.8321880702106075,1.1100878076473396e-11,4.4408920985006262e-15 +GLO,0.40000000000000002,-1,0,1,1,covariance,2,3,0.30953914807101124,4.3885090611965931e-10,1.1324274851176597e-14 +GLO,0.40000000000000002,-1,0,1,1,information,3,1,-3.3618280941867953,6.1370424512254564e-12,2.6645352591003757e-15 +GLO,0.40000000000000002,-1,0,1,1,covariance,3,1,0.052874822598323871,4.3885090611965931e-10,3.4867941867133823e-14 +GLO,0.40000000000000002,-1,0,1,1,information,3,2,-7.8321880702106075,1.1100878076473396e-11,4.4408920985006262e-15 +GLO,0.40000000000000002,-1,0,1,1,covariance,3,2,0.30953914807101118,4.3885090611965931e-10,1.1435297153639112e-14 +GLO,0.40000000000000002,-1,0,1,1,information,3,3,17.213939009145264,2.7559372411762979e-11,2.1316282072803006e-14 +GLO,0.40000000000000002,-1,0,1,1,covariance,3,3,0.20925628265311108,4.3885090611965931e-10,1.7763568394002505e-15 +GLO,-0.45000000000000001,-1,0,1,1,score_mean,1,0,8.3266726846886741e-17,6.7546264618825475e-14,1.1102230246251565e-16 +GLO,-0.45000000000000001,-1,0,1,1,score_mean,2,0,9.7144514654701197e-17,1.4517802433158482e-13,7.3552275381416621e-16 +GLO,-0.45000000000000001,-1,0,1,1,score_mean,3,0,8.6736173798840355e-17,1.4438709221664473e-14,8.0317696937726168e-16 +GLO,-0.45000000000000001,-1,0,1,1,information,1,1,2.432312966772852,3.4211379899377135e-14,0 +GLO,-0.45000000000000001,-1,0,1,1,covariance,1,1,3.0136420389427045,1.3504057232988909e-10,7.1054273576010019e-15 +GLO,-0.45000000000000001,-1,0,1,1,information,1,2,-4.5595897533451648,3.1841311060694368e-13,8.8817841970012523e-16 +GLO,-0.45000000000000001,-1,0,1,1,covariance,1,2,1.3408035183197813,1.3504057232988909e-10,6.6613381477509392e-16 +GLO,-0.45000000000000001,-1,0,1,1,information,1,3,-8.197036682611996,3.6800242077851579e-13,5.3290705182007514e-15 +GLO,-0.45000000000000001,-1,0,1,1,covariance,1,3,0.02642499155964155,1.3504057232988909e-10,1.1761425167122752e-15 +GLO,-0.45000000000000001,-1,0,1,1,information,2,1,-4.5595897533451648,3.1841311060694368e-13,8.8817841970012523e-16 +GLO,-0.45000000000000001,-1,0,1,1,covariance,2,1,1.3408035183197822,1.3504057232988909e-10,1.7763568394002505e-15 +GLO,-0.45000000000000001,-1,0,1,1,information,2,2,9.8995118250426124,1.7926042521125695e-12,1.7763568394002505e-15 +GLO,-0.45000000000000001,-1,0,1,1,covariance,2,2,1.3130218017585902,1.3504057232988909e-10,0 +GLO,-0.45000000000000001,-1,0,1,1,information,2,3,17.69805963012054,7.8595456239286019e-12,3.5527136788005009e-15 +GLO,-0.45000000000000001,-1,0,1,1,covariance,2,3,-0.33250881693564599,1.3504057232988909e-10,3.8857805861880479e-16 +GLO,-0.45000000000000001,-1,0,1,1,information,3,1,-8.197036682611996,3.6800242077851579e-13,5.3290705182007514e-15 +GLO,-0.45000000000000001,-1,0,1,1,covariance,3,1,0.026424991559641096,1.3504057232988909e-10,9.2287288921966137e-16 +GLO,-0.45000000000000001,-1,0,1,1,information,3,2,17.69805963012054,7.8595456239286019e-12,3.5527136788005009e-15 +GLO,-0.45000000000000001,-1,0,1,1,covariance,3,2,-0.33250881693564616,1.3504057232988909e-10,3.8857805861880479e-16 +GLO,-0.45000000000000001,-1,0,1,1,information,3,3,36.833075983364722,1.5784289365471824e-12,1.4210854715202004e-14 +GLO,-0.45000000000000001,-1,0,1,1,covariance,3,3,0.19279865461215484,1.3504057232988909e-10,0 +GLO,0.45000000000000001,-1,0,1,1,score_mean,1,0,-1.3877787807814457e-16,6.7404582275598456e-14,1.1102230246251565e-16 +GLO,0.45000000000000001,-1,0,1,1,score_mean,2,0,9.7144514654701197e-17,1.4532819874609145e-13,4.0245584642661925e-16 +GLO,0.45000000000000001,-1,0,1,1,score_mean,3,0,-4.649058915617843e-16,1.444657859403579e-14,3.4954678040932663e-16 +GLO,0.45000000000000001,-1,0,1,1,information,1,1,2.432312966772852,3.4594475833024914e-14,4.4408920985006262e-16 +GLO,0.45000000000000001,-1,0,1,1,covariance,1,1,3.0136420389427037,1.3513046486136234e-10,3.1086244689504383e-15 +GLO,0.45000000000000001,-1,0,1,1,information,1,2,4.5595897533451648,3.18579600066127e-13,0 +GLO,0.45000000000000001,-1,0,1,1,covariance,1,2,-1.3408035183197868,1.3513046486136234e-10,2.886579864025407e-15 +GLO,0.45000000000000001,-1,0,1,1,information,1,3,-8.1970366826119943,3.6987052242803902e-13,1.7763568394002505e-15 +GLO,0.45000000000000001,-1,0,1,1,covariance,1,3,0.026424991559638414,1.3513046486136234e-10,9.8185348740287282e-16 +GLO,0.45000000000000001,-1,0,1,1,information,2,1,4.5595897533451648,3.18579600066127e-13,0 +GLO,0.45000000000000001,-1,0,1,1,covariance,2,1,-1.3408035183197851,1.3513046486136234e-10,1.5543122344752192e-15 +GLO,0.45000000000000001,-1,0,1,1,information,2,2,9.8995118250426142,1.7949786465325099e-12,0 +GLO,0.45000000000000001,-1,0,1,1,covariance,2,2,1.3130218017585955,1.3513046486136234e-10,1.5543122344752192e-15 +GLO,0.45000000000000001,-1,0,1,1,information,2,3,-17.698059630120543,7.8610964399154733e-12,3.5527136788005009e-15 +GLO,0.45000000000000001,-1,0,1,1,covariance,2,3,0.33250881693564827,1.3513046486136234e-10,5.5511151231257827e-16 +GLO,0.45000000000000001,-1,0,1,1,information,3,1,-8.1970366826119943,3.6987052242803902e-13,1.7763568394002505e-15 +GLO,0.45000000000000001,-1,0,1,1,covariance,3,1,0.026424991559639444,1.3513046486136234e-10,3.1225022567582528e-17 +GLO,0.45000000000000001,-1,0,1,1,information,3,2,-17.698059630120543,7.8610964399154733e-12,3.5527136788005009e-15 +GLO,0.45000000000000001,-1,0,1,1,covariance,3,2,0.33250881693564771,1.3513046486136234e-10,0 +GLO,0.45000000000000001,-1,0,1,1,information,3,3,36.833075983364722,1.5854963901736008e-12,1.4210854715202004e-14 +GLO,0.45000000000000001,-1,0,1,1,covariance,3,3,0.19279865461215528,1.3513046486136234e-10,0 +GLO,-0.48999999999999999,-1,0,1,1,score_mean,1,0,0,5.9543543259883161e-14,0 +GLO,-0.48999999999999999,-1,0,1,1,score_mean,2,0,2.6367796834847468e-16,1.1765205209262839e-13,3.7470027081099033e-16 +GLO,-0.48999999999999999,-1,0,1,1,score_mean,3,0,3.0704605524789486e-16,1.3747792430025079e-14,8.1532003370909933e-17 +GLO,-0.48999999999999999,-1,0,1,1,information,1,1,12.419870334201333,5.4034015463242996e-12,1.7763568394002505e-14 +GLO,-0.48999999999999999,-1,0,1,1,covariance,1,1,3.0025793179752331,3.1348524272569298e-09,1.5987211554602254e-14 +GLO,-0.48999999999999999,-1,0,1,1,information,1,2,-24.550515876228854,1.7850564267558604e-11,3.1974423109204508e-14 +GLO,-0.48999999999999999,-1,0,1,1,covariance,1,2,1.4679971070062754,3.1348524272569298e-09,2.2870594307278225e-14 +GLO,-0.48999999999999999,-1,0,1,1,information,1,3,-48.043504229631871,3.5323403975430527e-11,7.815970093361102e-14 +GLO,-0.48999999999999999,-1,0,1,1,covariance,1,3,0.0052360775847887916,3.1348524272569298e-09,7.5347714179052616e-15 +GLO,-0.48999999999999999,-1,0,1,1,information,2,1,-24.550515876228854,1.7850564267558604e-11,3.1974423109204508e-14 +GLO,-0.48999999999999999,-1,0,1,1,covariance,2,1,1.4679971070062785,3.1348524272569298e-09,1.4876988529977098e-14 +GLO,-0.48999999999999999,-1,0,1,1,information,2,2,49.866591244632588,4.3957965139910082e-11,7.1054273576010019e-14 +GLO,-0.48999999999999999,-1,0,1,1,covariance,2,2,1.4211774823560701,3.1348524272569298e-09,1.4432899320127035e-14 +GLO,-0.48999999999999999,-1,0,1,1,information,2,3,97.565309896546083,7.1029353480233606e-11,1.4210854715202004e-13 +GLO,-0.48999999999999999,-1,0,1,1,covariance,2,3,-0.34673379660026171,3.1348524272569298e-09,3.5527136788005009e-15 +GLO,-0.48999999999999999,-1,0,1,1,information,3,1,-48.043504229631871,3.5323403975430527e-11,7.815970093361102e-14 +GLO,-0.48999999999999999,-1,0,1,1,covariance,3,1,0.0052360775847871558,3.1348524272569298e-09,3.6420519378133065e-15 +GLO,-0.48999999999999999,-1,0,1,1,information,3,2,97.565309896546083,7.1029353480233606e-11,1.4210854715202004e-13 +GLO,-0.48999999999999999,-1,0,1,1,covariance,3,2,-0.34673379660026254,3.1348524272569298e-09,1.7208456881689926e-15 +GLO,-0.48999999999999999,-1,0,1,1,information,3,3,196.49049770324064,1.6730342729324121e-10,3.1263880373444408e-13 +GLO,-0.48999999999999999,-1,0,1,1,covariance,3,3,0.17853662259782357,3.1348524272569298e-09,2.7755575615628914e-17 +GLO,0.48999999999999999,-1,0,1,1,score_mean,1,0,-1.3877787807814457e-16,5.965648938959653e-14,2.7755575615628914e-17 +GLO,0.48999999999999999,-1,0,1,1,score_mean,2,0,5.5511151231257827e-17,1.1752172248109395e-13,3.3306690738754696e-16 +GLO,0.48999999999999999,-1,0,1,1,score_mean,3,0,-7.2858385991025898e-17,1.3904134242279754e-14,2.6020852139652106e-16 +GLO,0.48999999999999999,-1,0,1,1,information,1,1,12.419870334201326,5.4034315245053878e-12,1.2434497875801753e-14 +GLO,0.48999999999999999,-1,0,1,1,covariance,1,1,3.0025793179753268,3.1348561450869112e-09,6.2616578588858829e-14 +GLO,0.48999999999999999,-1,0,1,1,information,1,2,24.55051587622885,1.7850587199533048e-11,2.8421709430404007e-14 +GLO,0.48999999999999999,-1,0,1,1,covariance,1,2,-1.4679971070063733,3.1348561450869112e-09,6.7501559897209518e-14 +GLO,0.48999999999999999,-1,0,1,1,information,1,3,-48.043504229631829,3.5323410692640026e-11,4.9737991503207013e-14 +GLO,0.48999999999999999,-1,0,1,1,covariance,1,3,0.0052360775847626181,3.1348561450869112e-09,1.8493019615650752e-14 +GLO,0.48999999999999999,-1,0,1,1,information,2,1,24.55051587622885,1.7850587199533048e-11,2.8421709430404007e-14 +GLO,0.48999999999999999,-1,0,1,1,covariance,2,1,-1.4679971070063678,3.1348561450869112e-09,5.2180482157382357e-14 +GLO,0.48999999999999999,-1,0,1,1,information,2,2,49.866591244632559,4.3958187108551871e-11,4.2632564145606011e-14 +GLO,0.48999999999999999,-1,0,1,1,covariance,2,2,1.4211774823561463,3.1348561450869112e-09,5.773159728050814e-14 +GLO,0.48999999999999999,-1,0,1,1,information,2,3,-97.565309896546054,7.1029383024202696e-11,1.2789769243681803e-13 +GLO,0.48999999999999999,-1,0,1,1,covariance,2,3,0.34673379660027792,3.1348561450869112e-09,1.5931700403370996e-14 +GLO,0.48999999999999999,-1,0,1,1,information,3,1,-48.043504229631829,3.5323410692640026e-11,4.9737991503207013e-14 +GLO,0.48999999999999999,-1,0,1,1,covariance,3,1,0.005236077584765352,3.1348561450869112e-09,1.0796051552741659e-14 +GLO,0.48999999999999999,-1,0,1,1,information,3,2,-97.565309896546054,7.1029383024202696e-11,1.2789769243681803e-13 +GLO,0.48999999999999999,-1,0,1,1,covariance,3,2,0.34673379660027664,3.1348561450869112e-09,1.2212453270876722e-14 +GLO,0.48999999999999999,-1,0,1,1,information,3,3,196.49049770324055,1.6730365389119047e-10,2.2737367544323206e-13 +GLO,0.48999999999999999,-1,0,1,1,covariance,3,3,0.17853662259782524,3.1348561450869112e-09,3.4139358007223564e-15 +GEV,0,0,0,1,1,score_mean,1,0,-1.1102230246251565e-16,8.1992980705031805e-15,2.2204460492503131e-16 +GEV,0,0,0,1,1,score_mean,2,0,-2.7755575615628914e-17,1.0272011586195206e-14,5.5511151231257827e-17 +GEV,0,0,0,1,1,score_mean,3,0,-6.9388939039072284e-17,7.3410829239256472e-15,8.3266726846886741e-17 +GEV,0,0,0,1,1,information,1,1,0.99999999999999989,1.1107376730841375e-14,1.1102230246251565e-16 +GEV,0,0,0,1,1,covariance,1,1,1.2487131118084192,2.4286653449002156e-12,6.6613381477509392e-16 +GEV,0,0,0,1,1,information,1,2,-0.4227843350984673,1.0822720263083792e-12,1.1102230246251565e-16 +GEV,0,0,0,1,1,covariance,1,2,0.33659395310777923,2.4286653449002156e-12,2.2204460492503131e-16 +GEV,0,0,0,1,1,information,1,3,-0.41184033042643975,9.22299688317805e-15,0 +GEV,0,0,0,1,1,covariance,1,3,0.25836823954420185,2.4286653449002156e-12,0 +GEV,0,0,0,1,1,information,2,1,-0.4227843350984673,1.0822720263083792e-12,1.1102230246251565e-16 +GEV,0,0,0,1,1,covariance,2,1,0.33659395310777929,2.4286653449002156e-12,2.2204460492503131e-16 +GEV,0,0,0,1,1,information,2,2,1.8236806608528791,2.0242195852506291e-14,4.4408920985006262e-16 +GEV,0,0,0,1,1,covariance,2,2,0.65313786712056976,2.4286653449002156e-12,2.2204460492503131e-16 +GEV,0,0,0,1,1,information,2,3,-0.33248490716027401,1.7947278991890011e-14,0 +GEV,0,0,0,1,1,covariance,2,3,0.14679838219578298,2.4286653449002156e-12,0 +GEV,0,0,0,1,1,information,3,1,-0.41184033042643975,9.22299688317805e-15,0 +GEV,0,0,0,1,1,covariance,3,1,0.25836823954420185,2.4286653449002156e-12,5.5511151231257827e-17 +GEV,0,0,0,1,1,information,3,2,-0.33248490716027401,1.7947278991890011e-14,0 +GEV,0,0,0,1,1,covariance,3,2,0.14679838219578295,2.4286653449002156e-12,0 +GEV,0,0,0,1,1,information,3,3,2.4236060551770287,2.6912457224640438e-14,4.4408920985006262e-16 +GEV,0,0,0,1,1,covariance,3,3,0.47665118889828978,2.4286653449002156e-12,1.1102230246251565e-16 +GEV,-9.9999999999999995e-07,0,0,1,1,score_mean,1,0,-1.6653345369377348e-16,4.6601516220076469e-14,5.5511151231257827e-17 +GEV,-9.9999999999999995e-07,0,0,1,1,score_mean,2,0,-2.7755575615628914e-17,1.0269748638530053e-14,2.7755575615628914e-17 +GEV,-9.9999999999999995e-07,0,0,1,1,score_mean,3,0,1.6653345369377348e-16,7.3414593428170816e-15,1.0408340855860843e-16 +GEV,-9.9999999999999995e-07,0,0,1,1,information,1,1,1.0000008455713174,1.2807263853438268e-13,4.4408920985006262e-16 +GEV,-9.9999999999999995e-07,0,0,1,1,covariance,1,1,1.2487133045870449,2.5608633941894185e-12,4.4408920985006262e-16 +GEV,-9.9999999999999995e-07,0,0,1,1,information,1,2,-0.42278657061887526,1.0822615806298708e-12,1.1102230246251565e-16 +GEV,-9.9999999999999995e-07,0,0,1,1,covariance,1,2,0.33659515001776391,2.5608633941894185e-12,1.1102230246251565e-16 +GEV,-9.9999999999999995e-07,0,0,1,1,information,1,3,-0.41184007952223556,9.2225145000778598e-15,5.5511151231257827e-17 +GEV,-9.9999999999999995e-07,0,0,1,1,covariance,1,3,0.25836837304344429,2.5608633941894185e-12,1.1102230246251565e-16 +GEV,-9.9999999999999995e-07,0,0,1,1,information,2,1,-0.42278657061887526,1.0822615806298708e-12,1.1102230246251565e-16 +GEV,-9.9999999999999995e-07,0,0,1,1,covariance,2,1,0.33659515001776397,2.5608633941894185e-12,2.2204460492503131e-16 +GEV,-9.9999999999999995e-07,0,0,1,1,information,2,2,1.8236799958880612,2.0244210948719223e-14,0 +GEV,-9.9999999999999995e-07,0,0,1,1,covariance,2,2,0.6531384192097528,2.5608633941894185e-12,0 +GEV,-9.9999999999999995e-07,0,0,1,1,information,2,3,-0.33248119750853922,1.7943060827347878e-14,3.3306690738754696e-16 +GEV,-9.9999999999999995e-07,0,0,1,1,covariance,2,3,0.14679795688165789,2.5608633941894185e-12,2.7755575615628914e-17 +GEV,-9.9999999999999995e-07,0,0,1,1,information,3,1,-0.41184007952223556,9.2225145000778598e-15,5.5511151231257827e-17 +GEV,-9.9999999999999995e-07,0,0,1,1,covariance,3,1,0.25836837304344434,2.5608633941894185e-12,1.6653345369377348e-16 +GEV,-9.9999999999999995e-07,0,0,1,1,information,3,2,-0.33248119750853922,1.7943060827347878e-14,3.3306690738754696e-16 +GEV,-9.9999999999999995e-07,0,0,1,1,covariance,3,2,0.14679795688165787,2.5608633941894185e-12,5.5511151231257827e-17 +GEV,-9.9999999999999995e-07,0,0,1,1,information,3,3,2.4236006049772314,2.6910403566762171e-14,8.8817841970012523e-16 +GEV,-9.9999999999999995e-07,0,0,1,1,covariance,3,3,0.47665197368892215,2.5608633941894185e-12,1.1102230246251565e-16 +GEV,9.9999999999999995e-07,0,0,1,1,score_mean,1,0,0,4.6600150079112787e-14,5.5511151231257827e-17 +GEV,9.9999999999999995e-07,0,0,1,1,score_mean,2,0,2.7755575615628914e-17,1.0270712468057341e-14,2.7755575615628914e-17 +GEV,9.9999999999999995e-07,0,0,1,1,score_mean,3,0,7.2858385991025898e-17,7.3419729901003273e-15,3.1225022567582528e-17 +GEV,9.9999999999999995e-07,0,0,1,1,information,1,1,0.99999915443397724,1.2807655571808149e-13,1.1102230246251565e-16 +GEV,9.9999999999999995e-07,0,0,1,1,covariance,1,1,1.2487129190293502,2.5609061113448702e-12,2.2204460492503131e-16 +GEV,9.9999999999999995e-07,0,0,1,1,information,1,2,-0.42278209957689267,1.0822786358935621e-12,5.5511151231257827e-17 +GEV,9.9999999999999995e-07,0,0,1,1,covariance,1,2,0.33659275619754397,2.5609061113448702e-12,5.5511151231257827e-17 +GEV,9.9999999999999995e-07,0,0,1,1,information,1,3,-0.41184058133821144,9.2225167982485383e-15,5.5511151231257827e-17 +GEV,9.9999999999999995e-07,0,0,1,1,covariance,1,3,0.25836810604436239,2.5609061113448702e-12,5.5511151231257827e-17 +GEV,9.9999999999999995e-07,0,0,1,1,information,2,1,-0.42278209957689267,1.0822786358935621e-12,5.5511151231257827e-17 +GEV,9.9999999999999995e-07,0,0,1,1,covariance,2,1,0.33659275619754392,2.5609061113448702e-12,1.6653345369377348e-16 +GEV,9.9999999999999995e-07,0,0,1,1,information,2,2,1.8236813258276898,2.0251744323642118e-14,2.2204460492503131e-16 +GEV,9.9999999999999995e-07,0,0,1,1,covariance,2,2,0.65313731503346817,2.5609061113448702e-12,0 +GEV,9.9999999999999995e-07,0,0,1,1,information,2,3,-0.33248861682472641,1.7947150070624897e-14,1.1102230246251565e-16 +GEV,9.9999999999999995e-07,0,0,1,1,covariance,2,3,0.14679880750904611,2.5609061113448702e-12,5.5511151231257827e-17 +GEV,9.9999999999999995e-07,0,0,1,1,information,3,1,-0.41184058133821144,9.2225167982485383e-15,5.5511151231257827e-17 +GEV,9.9999999999999995e-07,0,0,1,1,covariance,3,1,0.25836810604436239,2.5609061113448702e-12,1.1102230246251565e-16 +GEV,9.9999999999999995e-07,0,0,1,1,information,3,2,-0.33248861682472641,1.7947150070624897e-14,1.1102230246251565e-16 +GEV,9.9999999999999995e-07,0,0,1,1,covariance,3,2,0.14679880750904611,2.5609061113448702e-12,5.5511151231257827e-17 +GEV,9.9999999999999995e-07,0,0,1,1,information,3,3,2.4236115054054275,2.6909787345218485e-14,4.4408920985006262e-16 +GEV,9.9999999999999995e-07,0,0,1,1,covariance,3,3,0.47665040410819376,2.5609061113448702e-12,1.1102230246251565e-16 +GEV,-0.20000000000000001,0,0,1,1,score_mean,1,0,-1.1102230246251565e-16,3.9506599426877489e-14,1.6653345369377348e-16 +GEV,-0.20000000000000001,0,0,1,1,score_mean,2,0,0,9.9284251751004523e-15,5.5511151231257827e-17 +GEV,-0.20000000000000001,0,0,1,1,score_mean,3,0,-5.5511151231257827e-17,6.7929399126963119e-15,1.1449174941446927e-16 +GEV,-0.20000000000000001,0,0,1,1,information,1,1,1.2776598972044284,6.1852981623062584e-14,2.2204460492503131e-16 +GEV,-0.20000000000000001,0,0,1,1,covariance,1,1,1.2783531950225215,2.2805301806513338e-12,1.1102230246251565e-15 +GEV,-0.20000000000000001,0,0,1,1,information,1,2,-0.87928703162357846,3.7111469322083312e-14,3.3306690738754696e-16 +GEV,-0.20000000000000001,0,0,1,1,covariance,1,2,0.57130408748802741,2.2805301806513338e-12,4.4408920985006262e-16 +GEV,-0.20000000000000001,0,0,1,1,information,1,3,-0.47974709386376579,4.6124748457772765e-14,7.7715611723760958e-16 +GEV,-0.20000000000000001,0,0,1,1,covariance,1,3,0.27297786307977395,2.2805301806513338e-12,3.3306690738754696e-16 +GEV,-0.20000000000000001,0,0,1,1,information,2,1,-0.87928703162357846,3.7111469322083312e-14,3.3306690738754696e-16 +GEV,-0.20000000000000001,0,0,1,1,covariance,2,1,0.57130408748802752,2.2805301806513338e-12,5.5511151231257827e-16 +GEV,-0.20000000000000001,0,0,1,1,information,2,2,1.8513728861250733,5.6846040267354388e-13,0 +GEV,-0.20000000000000001,0,0,1,1,covariance,2,2,0.80555934872568602,2.2805301806513338e-12,3.3306690738754696e-16 +GEV,-0.20000000000000001,0,0,1,1,information,2,3,0.24303248681641954,1.5499275013352091e-14,9.9920072216264089e-16 +GEV,-0.20000000000000001,0,0,1,1,covariance,2,3,0.045053807373201742,2.2805301806513338e-12,1.2490009027033011e-16 +GEV,-0.20000000000000001,0,0,1,1,information,3,1,-0.47974709386376579,4.6124748457772765e-14,7.7715611723760958e-16 +GEV,-0.20000000000000001,0,0,1,1,covariance,3,1,0.2729778630797739,2.2805301806513338e-12,2.7755575615628914e-16 +GEV,-0.20000000000000001,0,0,1,1,information,3,2,0.24303248681641954,1.5499275013352091e-14,9.9920072216264089e-16 +GEV,-0.20000000000000001,0,0,1,1,covariance,3,2,0.045053807373201742,2.2805301806513338e-12,1.3183898417423734e-16 +GEV,-0.20000000000000001,0,0,1,1,information,3,3,1.7380192363593427,7.6641184761493055e-13,1.1102230246251565e-15 +GEV,-0.20000000000000001,0,0,1,1,covariance,3,3,0.64441795247405331,2.2805301806513338e-12,1.1102230246251565e-16 +GEV,0.20000000000000001,0,0,1,1,score_mean,1,0,0,8.8949739207761014e-13,5.5511151231257827e-17 +GEV,0.20000000000000001,0,0,1,1,score_mean,2,0,-1.1102230246251565e-16,1.0880391461646311e-14,1.9428902930940239e-16 +GEV,0.20000000000000001,0,0,1,1,score_mean,3,0,6.3837823915946501e-16,8.1343804234415818e-15,9.783840404509192e-16 +GEV,0.20000000000000001,0,0,1,1,information,1,1,0.95308303924020288,7.5014627842652083e-13,1.1102230246251565e-16 +GEV,0.20000000000000001,0,0,1,1,covariance,1,1,1.2017342064038075,5.4711688852156218e-12,2.2204460492503131e-16 +GEV,0.20000000000000001,0,0,1,1,information,1,2,0.10849634129980151,1.2926845080028899e-14,1.6653345369377348e-16 +GEV,0.20000000000000001,0,0,1,1,covariance,1,2,0.091902118521309875,5.4711688852156218e-12,1.2490009027033011e-16 +GEV,0.20000000000000001,0,0,1,1,information,1,3,-0.70543397196520785,1.2709753290491671e-14,1.1102230246251565e-16 +GEV,0.20000000000000001,0,0,1,1,covariance,1,3,0.22018153305378035,5.4711688852156218e-12,8.3266726846886741e-17 +GEV,0.20000000000000001,0,0,1,1,information,2,1,0.10849634129980151,1.2926845080028899e-14,1.6653345369377348e-16 +GEV,0.20000000000000001,0,0,1,1,covariance,2,1,0.091902118521309847,5.4711688852156218e-12,1.5265566588595902e-16 +GEV,0.20000000000000001,0,0,1,1,information,2,2,2.257887431992939,2.5068849872758534e-14,0 +GEV,0.20000000000000001,0,0,1,1,covariance,2,2,0.5839019708442017,5.4711688852156218e-12,1.1102230246251565e-16 +GEV,0.20000000000000001,0,0,1,1,information,2,3,-1.5345901098340244,1.1902282550015246e-12,0 +GEV,0.20000000000000001,0,0,1,1,covariance,2,3,0.2139698170854282,5.4711688852156218e-12,8.3266726846886741e-17 +GEV,0.20000000000000001,0,0,1,1,information,3,1,-0.70543397196520785,1.2709753290491671e-14,1.1102230246251565e-16 +GEV,0.20000000000000001,0,0,1,1,covariance,3,1,0.22018153305378033,5.4711688852156218e-12,8.3266726846886741e-17 +GEV,0.20000000000000001,0,0,1,1,information,3,2,-1.5345901098340244,1.1902282550015246e-12,0 +GEV,0.20000000000000001,0,0,1,1,covariance,3,2,0.2139698170854282,5.4711688852156218e-12,5.5511151231257827e-17 +GEV,0.20000000000000001,0,0,1,1,information,3,3,4.4907318198387607,2.8925059140306939e-12,0 +GEV,0.20000000000000001,0,0,1,1,covariance,3,3,0.3303870188737037,5.4711688852156218e-12,5.5511151231257827e-17 +GEV,-1,0,0,1,1,score_mean,1,0,3.3306690738754696e-16,1.3555590424041487e-14,3.3306690738754696e-16 +GEV,-1,0,0,1,1,score_mean,2,0,-3.4694469519536142e-16,1.0319679935790172e-14,4.0245584642661925e-16 +GEV,-1,0,0,1,1,score_mean,3,0,-4.8572257327350599e-17,6.0108009833815836e-15,2.7755575615628914e-17 +GEV,-1,0,0,1,1,information,1,1,7.9999999999999991,8.8856996335618939e-14,8.8817841970012523e-16 +GEV,-1,0,0,1,1,covariance,1,1,1.259244757274687,1.7140160203412078e-11,4.4408920985006262e-15 +GEV,-1,0,0,1,1,information,1,2,-6,6.913739260033866e-14,8.8817841970012523e-16 +GEV,-1,0,0,1,1,covariance,1,2,1.44938153336493,1.7140160203412078e-11,5.773159728050814e-15 +GEV,-1,0,0,1,1,information,1,3,-3.1544313298030651,1.1433650255023266e-13,8.8817841970012523e-16 +GEV,-1,0,0,1,1,covariance,1,3,0.11972644781951571,1.7140160203412078e-11,6.8001160258290838e-16 +GEV,-1,0,0,1,1,information,2,1,-6,6.913739260033866e-14,8.8817841970012523e-16 +GEV,-1,0,0,1,1,covariance,2,1,1.4493815333649303,1.7140160203412078e-11,5.773159728050814e-15 +GEV,-1,0,0,1,1,information,2,2,5,5.5534631964557947e-14,8.8817841970012523e-16 +GEV,-1,0,0,1,1,covariance,2,2,2.2771548336377814,1.7140160203412078e-11,7.5495165674510645e-15 +GEV,-1,0,0,1,1,information,2,3,2.5772156649015328,1.4104075934904837e-12,8.8817841970012523e-16 +GEV,-1,0,0,1,1,covariance,2,3,-0.65554660054569969,1.7140160203412078e-11,8.8817841970012523e-16 +GEV,-1,0,0,1,1,information,3,1,-3.1544313298030651,1.1433650255023266e-13,8.8817841970012523e-16 +GEV,-1,0,0,1,1,covariance,3,1,0.11972644781951575,1.7140160203412078e-11,7.0776717819853729e-16 +GEV,-1,0,0,1,1,information,3,2,2.5772156649015328,1.4104075934904837e-12,8.8817841970012523e-16 +GEV,-1,0,0,1,1,covariance,3,2,-0.65554660054569969,1.7140160203412078e-11,7.7715611723760958e-16 +GEV,-1,0,0,1,1,information,3,3,1.9781119906559448,2.1970070604326075e-14,6.6613381477509392e-16 +GEV,-1,0,0,1,1,covariance,3,3,1.5505460967304305,1.7140160203412078e-11,2.2204460492503131e-16 +GEV,0.45000000000000001,0,0,1,1,score_mean,1,0,-5.5511151231257827e-17,3.128364882497438e-13,1.1102230246251565e-16 +GEV,0.45000000000000001,0,0,1,1,score_mean,2,0,-1.3877787807814457e-16,1.3597179743047622e-14,8.3266726846886741e-17 +GEV,0.45000000000000001,0,0,1,1,score_mean,3,0,-1.0408340855860843e-16,1.3191337385632013e-14,1.3530843112619095e-16 +GEV,0.45000000000000001,0,0,1,1,information,1,1,2.8778360788472912,1.6849291271394385e-12,4.4408920985006262e-16 +GEV,0.45000000000000001,0,0,1,1,covariance,1,1,1.125015304617716,1.3326304215898122e-11,4.4408920985006262e-16 +GEV,0.45000000000000001,0,0,1,1,information,1,2,4.4199282912084987,5.5001792460461854e-12,2.6645352591003757e-15 +GEV,0.45000000000000001,0,0,1,1,covariance,1,2,-0.23048355027862258,1.3326304215898122e-11,1.1657341758564144e-15 +GEV,0.45000000000000001,0,0,1,1,information,1,3,-8.3683497956433612,2.3723402038782016e-12,3.5527136788005009e-15 +GEV,0.45000000000000001,0,0,1,1,covariance,1,3,0.14565462703102378,1.3326304215898122e-11,3.8857805861880479e-16 +GEV,0.45000000000000001,0,0,1,1,information,2,1,4.4199282912084987,5.5001792460461854e-12,2.6645352591003757e-15 +GEV,0.45000000000000001,0,0,1,1,covariance,2,1,-0.23048355027862347,1.3326304215898122e-11,1.3877787807814457e-16 +GEV,0.45000000000000001,0,0,1,1,information,2,2,10.37086115180424,3.6565686737239326e-12,3.5527136788005009e-15 +GEV,0.45000000000000001,0,0,1,1,covariance,2,2,0.60905139657083696,1.3326304215898122e-11,2.9976021664879227e-15 +GEV,0.45000000000000001,0,0,1,1,information,2,3,-17.728060740960263,7.3200935681782239e-13,1.4210854715202004e-14 +GEV,0.45000000000000001,0,0,1,1,covariance,2,3,0.24242170457394094,1.3326304215898122e-11,1.3877787807814457e-15 +GEV,0.45000000000000001,0,0,1,1,information,3,1,-8.3683497956433612,2.3723402038782016e-12,3.5527136788005009e-15 +GEV,0.45000000000000001,0,0,1,1,covariance,3,1,0.14565462703102339,1.3326304215898122e-11,0 +GEV,0.45000000000000001,0,0,1,1,information,3,2,-17.728060740960263,7.3200935681782239e-13,1.4210854715202004e-14 +GEV,0.45000000000000001,0,0,1,1,covariance,3,2,0.24242170457394108,1.3326304215898122e-11,1.2490009027033011e-15 +GEV,0.45000000000000001,0,0,1,1,information,3,3,36.58308235018707,3.1460165460258491e-12,1.4210854715202004e-14 +GEV,0.45000000000000001,0,0,1,1,covariance,3,3,0.17813030377223543,1.3326304215898122e-11,6.106226635438361e-16 +GEV,0.48999999999999999,0,0,1,1,score_mean,1,0,-2.2204460492503131e-16,2.4397179154253146e-13,2.7755575615628914e-16 +GEV,0.48999999999999999,0,0,1,1,score_mean,2,0,-2.2204460492503131e-16,1.3675102076643387e-14,5.5511151231257827e-17 +GEV,0.48999999999999999,0,0,1,1,score_mean,3,0,8.3266726846886741e-17,1.2513674933284547e-14,1.1796119636642288e-16 +GEV,0.48999999999999999,0,0,1,1,information,1,1,12.859918863447181,1.1364735601510808e-11,1.0658141036401503e-14 +GEV,0.48999999999999999,0,0,1,1,covariance,1,1,1.1119315214865486,2.8211438315203013e-10,4.4408920985006262e-16 +GEV,0.48999999999999999,0,0,1,1,information,1,2,24.435361588837971,1.7637219117836273e-11,1.4210854715202004e-14 +GEV,0.48999999999999999,0,0,1,1,covariance,1,2,-0.28388200507263223,2.8211438315203013e-10,5.8286708792820718e-15 +GEV,0.48999999999999999,0,0,1,1,information,1,3,-48.212533179820738,4.0454953485587916e-11,6.3948846218409017e-14 +GEV,0.48999999999999999,0,0,1,1,covariance,1,3,0.13196962046738797,2.8211438315203013e-10,3.1641356201816961e-15 +GEV,0.48999999999999999,0,0,1,1,information,2,1,24.435361588837971,1.7637219117836273e-11,1.4210854715202004e-14 +GEV,0.48999999999999999,0,0,1,1,covariance,2,1,-0.28388200507263328,2.8211438315203013e-10,3.8302694349567901e-15 +GEV,0.48999999999999999,0,0,1,1,information,2,2,50.340422713927673,3.4593499818908952e-11,2.1316282072803006e-14 +GEV,0.48999999999999999,0,0,1,1,covariance,2,2,0.62391464983791078,2.8211438315203013e-10,1.4654943925052066e-14 +GEV,0.48999999999999999,0,0,1,1,information,2,3,-97.596011928364376,7.173288641432764e-11,1.2789769243681803e-13 +GEV,0.48999999999999999,0,0,1,1,covariance,2,3,0.24049515245527123,2.8211438315203013e-10,8.2434059578417873e-15 +GEV,0.48999999999999999,0,0,1,1,information,3,1,-48.212533179820738,4.0454953485587916e-11,6.3948846218409017e-14 +GEV,0.48999999999999999,0,0,1,1,covariance,3,1,0.13196962046738739,2.8211438315203013e-10,2.1094237467877974e-15 +GEV,0.48999999999999999,0,0,1,1,information,3,2,-97.596011928364376,7.173288641432764e-11,1.2789769243681803e-13 +GEV,0.48999999999999999,0,0,1,1,covariance,3,2,0.24049515245527153,2.8211438315203013e-10,8.7707618945387367e-15 +GEV,0.48999999999999999,0,0,1,1,information,3,3,196.28217258110058,1.3376504933912893e-10,1.9895196601282805e-13 +GEV,0.48999999999999999,0,0,1,1,covariance,3,3,0.15708995405841386,2.8211438315203013e-10,4.9127368839663177e-15 +K4,0,0,0,1,1,score_mean,1,0,-1.1102230246251565e-16,8.1992980705031805e-15,2.2204460492503131e-16 +K4,0,0,0,1,1,score_mean,2,0,-2.7755575615628914e-17,1.0272011586195206e-14,5.5511151231257827e-17 +K4,0,0,0,1,1,score_mean,3,0,-6.9388939039072284e-17,7.3410829239256472e-15,8.3266726846886741e-17 +K4,0,0,0,1,1,score_mean,4,0,1.2490009027033011e-16,6.776507721941321e-15,1.3877787807814457e-16 +K4,0,0,0,1,1,information,1,1,0.99999999999999989,1.1107376730841375e-14,1.1102230246251565e-16 +K4,0,0,0,1,1,covariance,1,1,3.4816596168698939,2.2672214054662958e-10,4.4408920985006262e-16 +K4,0,0,0,1,1,information,1,2,-0.4227843350984673,1.0822720263083792e-12,1.1102230246251565e-16 +K4,0,0,0,1,1,covariance,1,2,-2.5451965889656565,2.2672214054662958e-10,8.8817841970012523e-16 +K4,0,0,0,1,1,information,1,3,-0.41184033042643975,9.22299688317805e-15,0 +K4,0,0,0,1,1,covariance,1,3,-1.1451270207333315,2.2672214054662958e-10,4.4408920985006262e-16 +K4,0,0,0,1,1,information,1,4,0.99999999999999978,1.2407214692483362e-14,1.1102230246251565e-16 +K4,0,0,0,1,1,covariance,1,4,-4.0293383550296857,2.2672214054662958e-10,0 +K4,0,0,0,1,1,information,2,1,-0.4227843350984673,1.0822720263083792e-12,1.1102230246251565e-16 +K4,0,0,0,1,1,covariance,2,1,-2.5451965889656569,2.2672214054662958e-10,4.4408920985006262e-16 +K4,0,0,0,1,1,information,2,2,1.8236806608528791,2.0242195852506291e-14,4.4408920985006262e-16 +K4,0,0,0,1,1,covariance,2,2,4.3723119313264966,2.2672214054662958e-10,8.8817841970012523e-16 +K4,0,0,0,1,1,information,2,3,-0.33248490716027401,1.7947278991890011e-14,0 +K4,0,0,0,1,1,covariance,2,3,1.9581178015122518,2.2672214054662958e-10,1.7763568394002505e-15 +K4,0,0,0,1,1,information,2,4,-1.4227843350984668,4.0168601256209287e-14,2.2204460492503131e-16 +K4,0,0,0,1,1,covariance,2,4,5.200173464083325,2.2672214054662958e-10,8.8817841970012523e-16 +K4,0,0,0,1,1,information,3,1,-0.41184033042643975,9.22299688317805e-15,0 +K4,0,0,0,1,1,covariance,3,1,-1.1451270207333313,2.2672214054662958e-10,1.1102230246251565e-15 +K4,0,0,0,1,1,information,3,2,-0.33248490716027401,1.7947278991890011e-14,0 +K4,0,0,0,1,1,covariance,3,2,1.9581178015122516,2.2672214054662958e-10,1.9984014443252818e-15 +K4,0,0,0,1,1,information,3,3,2.4236060551770287,2.6912457224640438e-14,4.4408920985006262e-16 +K4,0,0,0,1,1,covariance,3,3,1.358803511471405,2.2672214054662958e-10,1.1102230246251565e-15 +K4,0,0,0,1,1,information,3,4,-0.83462466552490655,6.4074256166995558e-14,3.3306690738754696e-16 +K4,0,0,0,1,1,covariance,3,4,2.5325986406391516,2.2672214054662958e-10,1.7763568394002505e-15 +K4,0,0,0,1,1,information,4,1,0.99999999999999978,1.2407214692483362e-14,1.1102230246251565e-16 +K4,0,0,0,1,1,covariance,4,1,-4.0293383550296866,2.2672214054662958e-10,0 +K4,0,0,0,1,1,information,4,2,-1.4227843350984668,4.0168601256209287e-14,2.2204460492503131e-16 +K4,0,0,0,1,1,covariance,4,2,5.200173464083325,2.2672214054662958e-10,8.8817841970012523e-16 +K4,0,0,0,1,1,information,4,3,-0.83462466552490655,6.4074256166995558e-14,3.3306690738754696e-16 +K4,0,0,0,1,1,covariance,4,3,2.5325986406391516,2.2672214054662958e-10,1.7763568394002505e-15 +K4,0,0,0,1,1,information,4,4,1.9999999999999996,2.2214811222614338e-14,4.4408920985006262e-16 +K4,0,0,0,1,1,covariance,4,4,7.2709164964372297,2.2672214054662958e-10,0 +K4,9.9999999999999995e-07,0,0,1,1,score_mean,1,0,0,4.6600150079112787e-14,5.5511151231257827e-17 +K4,9.9999999999999995e-07,0,0,1,1,score_mean,2,0,2.7755575615628914e-17,1.0270712468057341e-14,2.7755575615628914e-17 +K4,9.9999999999999995e-07,0,0,1,1,score_mean,3,0,7.2858385991025898e-17,7.3419729901003273e-15,3.1225022567582528e-17 +K4,9.9999999999999995e-07,0,0,1,1,score_mean,4,0,1.2490009027033011e-16,6.776507721941321e-15,1.3877787807814457e-16 +K4,9.9999999999999995e-07,0,0,1,1,information,1,1,0.99999915443397724,1.2807655571808149e-13,1.1102230246251565e-16 +K4,9.9999999999999995e-07,0,0,1,1,covariance,1,1,3.4816560636371459,2.3905169698684535e-10,1.4033219031261979e-13 +K4,9.9999999999999995e-07,0,0,1,1,information,1,2,-0.42278209957689267,1.0822786358935621e-12,5.5511151231257827e-17 +K4,9.9999999999999995e-07,0,0,1,1,covariance,1,2,-2.5451946608011395,2.3905169698684535e-10,1.425526363618701e-13 +K4,9.9999999999999995e-07,0,0,1,1,information,1,3,-0.41184058133821144,9.2225167982485383e-15,5.5511151231257827e-17 +K4,9.9999999999999995e-07,0,0,1,1,covariance,1,3,-1.1451247772257798,2.3905169698684535e-10,6.7279515292284486e-14 +K4,9.9999999999999995e-07,0,0,1,1,information,1,4,0.99999857721649443,1.4741748605785035e-14,5.2180482157382357e-15 +K4,9.9999999999999995e-07,0,0,1,1,covariance,1,4,-4.0293304490148341,2.3905169698684535e-10,2.0961010704922955e-13 +K4,9.9999999999999995e-07,0,0,1,1,information,2,1,-0.42278209957689267,1.0822786358935621e-12,5.5511151231257827e-17 +K4,9.9999999999999995e-07,0,0,1,1,covariance,2,1,-2.5451946608011391,2.3905169698684535e-10,1.4388490399142029e-13 +K4,9.9999999999999995e-07,0,0,1,1,information,2,2,1.8236813258276898,2.0251744323642118e-14,2.2204460492503131e-16 +K4,9.9999999999999995e-07,0,0,1,1,covariance,2,2,4.3723089100767538,2.3905169698684535e-10,1.3233858453531866e-13 +K4,9.9999999999999995e-07,0,0,1,1,information,2,3,-0.33248861682472641,1.7947150070624897e-14,1.1102230246251565e-16 +K4,9.9999999999999995e-07,0,0,1,1,covariance,2,3,1.9581159208095305,2.3905169698684535e-10,6.1728400169158704e-14 +K4,9.9999999999999995e-07,0,0,1,1,information,2,4,-1.4227835004742948,4.017592039532222e-14,4.4408920985006262e-16 +K4,9.9999999999999995e-07,0,0,1,1,covariance,2,4,5.2001654475354462,2.3905169698684535e-10,1.9806378759312793e-13 +K4,9.9999999999999995e-07,0,0,1,1,information,3,1,-0.41184058133821144,9.2225167982485383e-15,5.5511151231257827e-17 +K4,9.9999999999999995e-07,0,0,1,1,covariance,3,1,-1.1451247772257798,2.3905169698684535e-10,6.7723604502134549e-14 +K4,9.9999999999999995e-07,0,0,1,1,information,3,2,-0.33248861682472641,1.7947150070624897e-14,1.1102230246251565e-16 +K4,9.9999999999999995e-07,0,0,1,1,covariance,3,2,1.9581159208095305,2.3905169698684535e-10,6.1728400169158704e-14 +K4,9.9999999999999995e-07,0,0,1,1,information,3,3,2.4236115054054275,2.6909787345218485e-14,4.4408920985006262e-16 +K4,9.9999999999999995e-07,0,0,1,1,covariance,3,3,1.3588010661824494,2.3905169698684535e-10,2.8643754035329039e-14 +K4,9.9999999999999995e-07,0,0,1,1,information,3,4,-0.83462417210781292,6.4074131721116831e-14,0 +K4,9.9999999999999995e-07,0,0,1,1,covariance,3,4,2.5325931935133537,2.3905169698684535e-10,9.2370555648813024e-14 +K4,9.9999999999999995e-07,0,0,1,1,information,4,1,0.99999857721649443,1.4741748605785035e-14,5.2180482157382357e-15 +K4,9.9999999999999995e-07,0,0,1,1,covariance,4,1,-4.0293304490148341,2.3905169698684535e-10,2.0961010704922955e-13 +K4,9.9999999999999995e-07,0,0,1,1,information,4,2,-1.4227835004742948,4.017592039532222e-14,4.4408920985006262e-16 +K4,9.9999999999999995e-07,0,0,1,1,covariance,4,2,5.2001654475354471,2.3905169698684535e-10,1.971756091734278e-13 +K4,9.9999999999999995e-07,0,0,1,1,information,4,3,-0.83462417210781292,6.4074131721116831e-14,0 +K4,9.9999999999999995e-07,0,0,1,1,covariance,4,3,2.5325931935133537,2.3905169698684535e-10,9.2370555648813024e-14 +K4,9.9999999999999995e-07,0,0,1,1,information,4,4,1.9999999999999996,2.2214811222614338e-14,4.4408920985006262e-16 +K4,9.9999999999999995e-07,0,0,1,1,covariance,4,4,7.2708989060309301,2.3905169698684535e-10,2.9398705692074145e-13 +K4,-9.9999999999999995e-07,0,0,1,1,score_mean,1,0,-1.6653345369377348e-16,4.6601516220076469e-14,5.5511151231257827e-17 +K4,-9.9999999999999995e-07,0,0,1,1,score_mean,2,0,-2.7755575615628914e-17,1.0269748638530053e-14,2.7755575615628914e-17 +K4,-9.9999999999999995e-07,0,0,1,1,score_mean,3,0,1.6653345369377348e-16,7.3414593428170816e-15,1.0408340855860843e-16 +K4,-9.9999999999999995e-07,0,0,1,1,score_mean,4,0,1.2490009027033011e-16,6.776507721941321e-15,1.3877787807814457e-16 +K4,-9.9999999999999995e-07,0,0,1,1,information,1,1,1.0000008455713174,1.2807263853438268e-13,4.4408920985006262e-16 +K4,-9.9999999999999995e-07,0,0,1,1,covariance,1,1,3.4816631701143752,2.3902494117763445e-10,5.773159728050814e-15 +K4,-9.9999999999999995e-07,0,0,1,1,information,1,2,-0.42278657061887526,1.0822615806298708e-12,1.1102230246251565e-16 +K4,-9.9999999999999995e-07,0,0,1,1,covariance,1,2,-2.5451985171376728,2.3902494117763445e-10,8.8817841970012523e-15 +K4,-9.9999999999999995e-07,0,0,1,1,information,1,3,-0.41184007952223556,9.2225145000778598e-15,5.5511151231257827e-17 +K4,-9.9999999999999995e-07,0,0,1,1,covariance,1,3,-1.1451292642463633,2.3902494117763445e-10,4.2188474935755949e-15 +K4,-9.9999999999999995e-07,0,0,1,1,information,1,4,1.0000014227851695,1.2407257521707167e-14,2.2204460492503131e-16 +K4,-9.9999999999999995e-07,0,0,1,1,covariance,1,4,-4.0293462610701303,2.3902494117763445e-10,9.7699626167013776e-15 +K4,-9.9999999999999995e-07,0,0,1,1,information,2,1,-0.42278657061887526,1.0822615806298708e-12,1.1102230246251565e-16 +K4,-9.9999999999999995e-07,0,0,1,1,covariance,2,1,-2.5451985171376728,2.3902494117763445e-10,7.5495165674510645e-15 +K4,-9.9999999999999995e-07,0,0,1,1,information,2,2,1.8236799958880612,2.0244210948719223e-14,0 +K4,-9.9999999999999995e-07,0,0,1,1,covariance,2,2,4.3723149525780673,2.3902494117763445e-10,1.0658141036401503e-14 +K4,-9.9999999999999995e-07,0,0,1,1,information,2,3,-0.33248119750853922,1.7943060827347878e-14,3.3306690738754696e-16 +K4,-9.9999999999999995e-07,0,0,1,1,covariance,2,3,1.9581196822148064,2.3902494117763445e-10,5.1070259132757201e-15 +K4,-9.9999999999999995e-07,0,0,1,1,information,2,4,-1.4227851697236265,4.0177333832911257e-14,0 +K4,-9.9999999999999995e-07,0,0,1,1,covariance,2,4,5.2001814806469584,2.3902494117763445e-10,1.5099033134902129e-14 +K4,-9.9999999999999995e-07,0,0,1,1,information,3,1,-0.41184007952223556,9.2225145000778598e-15,5.5511151231257827e-17 +K4,-9.9999999999999995e-07,0,0,1,1,covariance,3,1,-1.1451292642463633,2.3902494117763445e-10,3.7747582837255322e-15 +K4,-9.9999999999999995e-07,0,0,1,1,information,3,2,-0.33248119750853922,1.7943060827347878e-14,3.3306690738754696e-16 +K4,-9.9999999999999995e-07,0,0,1,1,covariance,3,2,1.9581196822148061,2.3902494117763445e-10,5.1070259132757201e-15 +K4,-9.9999999999999995e-07,0,0,1,1,information,3,3,2.4236006049772314,2.6910403566762171e-14,8.8817841970012523e-16 +K4,-9.9999999999999995e-07,0,0,1,1,covariance,3,3,1.3588059567622888,2.3902494117763445e-10,2.886579864025407e-15 +K4,-9.9999999999999995e-07,0,0,1,1,information,3,4,-0.83462515894231226,6.4074365755335618e-14,0 +K4,-9.9999999999999995e-07,0,0,1,1,covariance,3,4,2.5326040877764759,2.3902494117763445e-10,7.5495165674510645e-15 +K4,-9.9999999999999995e-07,0,0,1,1,information,4,1,1.0000014227851695,1.2407257521707167e-14,2.2204460492503131e-16 +K4,-9.9999999999999995e-07,0,0,1,1,covariance,4,1,-4.0293462610701303,2.3902494117763445e-10,1.0658141036401503e-14 +K4,-9.9999999999999995e-07,0,0,1,1,information,4,2,-1.4227851697236265,4.0177333832911257e-14,0 +K4,-9.9999999999999995e-07,0,0,1,1,covariance,4,2,5.2001814806469575,2.3902494117763445e-10,1.4210854715202004e-14 +K4,-9.9999999999999995e-07,0,0,1,1,information,4,3,-0.83462515894231226,6.4074365755335618e-14,0 +K4,-9.9999999999999995e-07,0,0,1,1,covariance,4,3,2.5326040877764755,2.3902494117763445e-10,7.1054273576010019e-15 +K4,-9.9999999999999995e-07,0,0,1,1,information,4,4,1.9999999999999996,2.2214811222614338e-14,4.4408920985006262e-16 +K4,-9.9999999999999995e-07,0,0,1,1,covariance,4,4,7.2709340868992847,2.3902494117763445e-10,2.042810365310288e-14 +K4,0,9.9999999999999995e-07,0,1,1,score_mean,1,0,0,8.2009625178179644e-15,4.4408920985006262e-16 +K4,0,9.9999999999999995e-07,0,1,1,score_mean,2,0,-3.0531133177191805e-16,1.0272859979820439e-14,8.3266726846886741e-17 +K4,0,9.9999999999999995e-07,0,1,1,score_mean,3,0,-2.0469737016526324e-16,7.3419314200639225e-15,1.0408340855860843e-16 +K4,0,9.9999999999999995e-07,0,1,1,score_mean,4,0,1.8041124150158794e-16,6.7771971977641381e-15,5.6898930012039273e-16 +K4,0,9.9999999999999995e-07,0,1,1,information,1,1,1.0000020000039997,1.1107179254533529e-14,4.4408920985006262e-16 +K4,0,9.9999999999999995e-07,0,1,1,covariance,1,1,3.4816527353009668,2.2672112758109345e-10,1.2878587085651816e-14 +K4,0,9.9999999999999995e-07,0,1,1,information,1,2,-0.42278568066991046,1.0822708169197872e-12,5.5511151231257827e-17 +K4,0,9.9999999999999995e-07,0,1,1,covariance,1,2,-2.5451908058400905,2.2672112758109345e-10,9.7699626167013776e-15 +K4,0,9.9999999999999995e-07,0,1,1,information,1,3,-0.41184086550024745,9.2230457944099784e-15,3.3306690738754696e-16 +K4,0,9.9999999999999995e-07,0,1,1,covariance,1,3,-1.1451243898394223,2.2672112758109345e-10,5.9952043329758453e-15 +K4,0,9.9999999999999995e-07,0,1,1,information,1,4,1.0000030000069997,1.2409415766020068e-14,2.2204460492503131e-16 +K4,0,9.9999999999999995e-07,0,1,1,covariance,1,4,-4.0293268577103962,2.2672112758109345e-10,1.7763568394002505e-14 +K4,0,9.9999999999999995e-07,0,1,1,information,2,1,-0.42278568066991046,1.0822708169197872e-12,5.5511151231257827e-17 +K4,0,9.9999999999999995e-07,0,1,1,covariance,2,1,-2.5451908058400869,2.2672112758109345e-10,1.1546319456101628e-14 +K4,0,9.9999999999999995e-07,0,1,1,information,2,2,1.8236817310004945,2.0245991569368889e-14,8.8817841970012523e-16 +K4,0,9.9999999999999995e-07,0,1,1,covariance,2,2,4.37230646132946,2.2672112758109345e-10,9.7699626167013776e-15 +K4,0,9.9999999999999995e-07,0,1,1,information,2,3,-0.3324849341140439,1.7945562833862093e-14,0 +K4,0,9.9999999999999995e-07,0,1,1,covariance,2,3,1.9581154555638731,2.2672112758109345e-10,4.4408920985006262e-15 +K4,0,9.9999999999999995e-07,0,1,1,information,2,4,-1.4227871034570136,4.0169490444441405e-14,4.4408920985006262e-16 +K4,0,9.9999999999999995e-07,0,1,1,covariance,2,4,5.2001608230324701,2.2672112758109345e-10,1.7763568394002505e-14 +K4,0,9.9999999999999995e-07,0,1,1,information,3,1,-0.41184086550024745,9.2230457944099784e-15,3.3306690738754696e-16 +K4,0,9.9999999999999995e-07,0,1,1,covariance,3,1,-1.1451243898394219,2.2672112758109345e-10,5.3290705182007514e-15 +K4,0,9.9999999999999995e-07,0,1,1,information,3,2,-0.3324849341140439,1.7945562833862093e-14,0 +K4,0,9.9999999999999995e-07,0,1,1,covariance,3,2,1.9581154555638729,2.2672112758109345e-10,4.8849813083506888e-15 +K4,0,9.9999999999999995e-07,0,1,1,information,3,3,2.4236055325913131,2.6911176697343724e-14,8.8817841970012523e-16 +K4,0,9.9999999999999995e-07,0,1,1,covariance,3,3,1.3588025540627315,2.2672112758109345e-10,1.9984014443252818e-15 +K4,0,9.9999999999999995e-07,0,1,1,information,3,4,-0.83462553522433269,6.4073702595330329e-14,3.3306690738754696e-16 +K4,0,9.9999999999999995e-07,0,1,1,covariance,3,4,2.5325926779170067,2.2672112758109345e-10,7.9936057773011271e-15 +K4,0,9.9999999999999995e-07,0,1,1,information,4,1,1.0000030000069997,1.2409415766020068e-14,2.2204460492503131e-16 +K4,0,9.9999999999999995e-07,0,1,1,covariance,4,1,-4.0293268577103953,2.2672112758109345e-10,1.7763568394002505e-14 +K4,0,9.9999999999999995e-07,0,1,1,information,4,2,-1.4227871034570136,4.0169490444441405e-14,4.4408920985006262e-16 +K4,0,9.9999999999999995e-07,0,1,1,covariance,4,2,5.2001608230324718,2.2672112758109345e-10,1.5987211554602254e-14 +K4,0,9.9999999999999995e-07,0,1,1,information,4,3,-0.83462553522433269,6.4073702595330329e-14,3.3306690738754696e-16 +K4,0,9.9999999999999995e-07,0,1,1,covariance,4,3,2.5325926779170072,2.2672112758109345e-10,7.5495165674510645e-15 +K4,0,9.9999999999999995e-07,0,1,1,information,4,4,2.0000060000140003,2.2211836414002342e-14,0 +K4,0,9.9999999999999995e-07,0,1,1,covariance,4,4,7.2708917972456559,2.2672112758109345e-10,2.6645352591003757e-14 +K4,0,-9.9999999999999995e-07,0,1,1,score_mean,1,0,2.2204460492503131e-16,8.1993166341007363e-15,6.106226635438361e-16 +K4,0,-9.9999999999999995e-07,0,1,1,score_mean,2,0,-3.3306690738754696e-16,1.0270030087812889e-14,0 +K4,0,-9.9999999999999995e-07,0,1,1,score_mean,3,0,-2.9143354396410359e-16,7.3409958486899094e-15,2.7755575615628914e-16 +K4,0,-9.9999999999999995e-07,0,1,1,score_mean,4,0,-2.7755575615628914e-17,6.7767857269795062e-15,5.8286708792820718e-16 +K4,0,-9.9999999999999995e-07,0,1,1,information,1,1,0.99999800000399897,1.1105392283509823e-14,4.4408920985006262e-16 +K4,0,-9.9999999999999995e-07,0,1,1,covariance,1,1,3.4816664984534484,2.2672255963061596e-10,8.8817841970012523e-16 +K4,0,-9.9999999999999995e-07,0,1,1,information,1,2,-0.42278298953257004,1.0822680719086492e-12,2.7755575615628914e-16 +K4,0,-9.9999999999999995e-07,0,1,1,covariance,1,2,-2.545202372104542,2.2672255963061596e-10,8.4376949871511897e-15 +K4,0,-9.9999999999999995e-07,0,1,1,information,1,3,-0.41183979535459081,9.2186618657530761e-15,2.2204460492503131e-16 +K4,0,-9.9999999999999995e-07,0,1,1,covariance,1,3,-1.1451296516330634,2.2672255963061596e-10,4.4408920985006262e-15 +K4,0,-9.9999999999999995e-07,0,1,1,information,1,4,0.99999700000699943,1.2408131807835051e-14,6.6613381477509392e-16 +K4,0,-9.9999999999999995e-07,0,1,1,covariance,1,4,-4.0293498523825102,2.2672255963061596e-10,8.8817841970012523e-15 +K4,0,-9.9999999999999995e-07,0,1,1,information,2,1,-0.42278298953257004,1.0822680719086492e-12,2.7755575615628914e-16 +K4,0,-9.9999999999999995e-07,0,1,1,covariance,2,1,-2.5452023721045411,2.2672255963061596e-10,8.8817841970012523e-15 +K4,0,-9.9999999999999995e-07,0,1,1,information,2,2,1.8236795907091814,2.0244929363791663e-14,6.6613381477509392e-16 +K4,0,-9.9999999999999995e-07,0,1,1,covariance,2,2,4.3723174013353789,2.2672255963061596e-10,1.9539925233402755e-14 +K4,0,-9.9999999999999995e-07,0,1,1,information,2,3,-0.33248488020506095,1.794886435986521e-14,8.8817841970012523e-16 +K4,0,-9.9999999999999995e-07,0,1,1,covariance,2,3,1.9581201474656549,2.2672255963061596e-10,1.021405182655144e-14 +K4,0,-9.9999999999999995e-07,0,1,1,information,2,4,-1.4227815667510029,4.0168989464809864e-14,6.6613381477509392e-16 +K4,0,-9.9999999999999995e-07,0,1,1,covariance,2,4,5.200186105165697,2.2672255963061596e-10,2.3980817331903381e-14 +K4,0,-9.9999999999999995e-07,0,1,1,information,3,1,-0.41183979535459081,9.2186618657530761e-15,2.2204460492503131e-16 +K4,0,-9.9999999999999995e-07,0,1,1,covariance,3,1,-1.1451296516330631,2.2672255963061596e-10,4.2188474935755949e-15 +K4,0,-9.9999999999999995e-07,0,1,1,information,3,2,-0.33248488020506095,1.794886435986521e-14,8.8817841970012523e-16 +K4,0,-9.9999999999999995e-07,0,1,1,covariance,3,2,1.9581201474656549,2.2672255963061596e-10,9.5479180117763462e-15 +K4,0,-9.9999999999999995e-07,0,1,1,information,3,3,2.4236065777641933,2.6900795600488727e-14,8.8817841970012523e-16 +K4,0,-9.9999999999999995e-07,0,1,1,covariance,3,3,1.358804468882127,2.2672255963061596e-10,4.8849813083506888e-15 +K4,0,-9.9999999999999995e-07,0,1,1,information,3,4,-0.83462379582934454,6.4073406097452927e-14,3.3306690738754696e-16 +K4,0,-9.9999999999999995e-07,0,1,1,covariance,3,4,2.5326046033752276,2.2672255963061596e-10,1.1990408665951691e-14 +K4,0,-9.9999999999999995e-07,0,1,1,information,4,1,0.99999700000699943,1.2408131807835051e-14,6.6613381477509392e-16 +K4,0,-9.9999999999999995e-07,0,1,1,covariance,4,1,-4.0293498523825093,2.2672255963061596e-10,8.8817841970012523e-15 +K4,0,-9.9999999999999995e-07,0,1,1,information,4,2,-1.4227815667510029,4.0168989464809864e-14,6.6613381477509392e-16 +K4,0,-9.9999999999999995e-07,0,1,1,covariance,4,2,5.2001861051656988,2.2672255963061596e-10,2.3980817331903381e-14 +K4,0,-9.9999999999999995e-07,0,1,1,information,4,3,-0.83462379582934454,6.4073406097452927e-14,3.3306690738754696e-16 +K4,0,-9.9999999999999995e-07,0,1,1,covariance,4,3,2.5326046033752281,2.2672255963061596e-10,1.2434497875801753e-14 +K4,0,-9.9999999999999995e-07,0,1,1,information,4,4,1.9999940000139993,2.22125277494123e-14,1.5543122344752192e-15 +K4,0,-9.9999999999999995e-07,0,1,1,covariance,4,4,7.2709411957074463,2.2672255963061596e-10,2.9309887850104133e-14 +K4,0,-1,0,1,1,score_mean,1,0,0,5.5595497795500773e-15,2.7755575615628914e-17 +K4,0,-1,0,1,1,score_mean,2,0,-1.1102230246251565e-16,9.9620509752748834e-15,0 +K4,0,-1,0,1,1,score_mean,3,0,-2.0816681711721685e-17,9.7534272794415726e-15,9.0205620750793969e-17 +K4,0,-1,0,1,1,score_mean,4,0,1.3877787807814457e-17,4.3186164064484242e-15,5.5511151231257827e-17 +K4,0,-1,0,1,1,information,1,1,0.33333333333333326,1.3373867036071658e-14,1.1102230246251565e-16 +K4,0,-1,0,1,1,covariance,1,1,28.531288367844851,5.6296128748179335e-09,4.4408920985006262e-13 +K4,0,-1,0,1,1,information,1,2,-2.7755575615628914e-17,5.7876650277494293e-15,5.5511151231257827e-17 +K4,0,-1,0,1,1,covariance,1,2,-21.590317225656708,5.6296128748179335e-09,3.5882408155885059e-13 +K4,0,-1,0,1,1,information,1,3,-0.21497802228274204,7.0378899485062419e-15,1.1102230246251565e-16 +K4,0,-1,0,1,1,covariance,1,3,-8.2828873952574007,5.6296128748179335e-09,1.4566126083082054e-13 +K4,0,-1,0,1,1,information,1,4,0.16666666666666666,7.9653211246202002e-15,0 +K4,0,-1,0,1,1,covariance,1,4,-61.746409241828225,5.6296128748179335e-09,1.0516032489249483e-12 +K4,0,-1,0,1,1,information,2,1,-2.7755575615628914e-17,5.7876650277494293e-15,5.5511151231257827e-17 +K4,0,-1,0,1,1,covariance,2,1,-21.590317225656715,5.6296128748179335e-09,3.730349362740526e-13 +K4,0,-1,0,1,1,information,2,2,1.429956044565484,1.5874432157400921e-14,4.4408920985006262e-16 +K4,0,-1,0,1,1,covariance,2,2,19.038858737870054,5.6296128748179335e-09,3.0908609005564358e-13 +K4,0,-1,0,1,1,information,2,3,1.1102230246251565e-16,2.0912533482946806e-14,1.1102230246251565e-16 +K4,0,-1,0,1,1,covariance,2,3,7.1858723959756174,5.6296128748179335e-09,1.2256862191861728e-13 +K4,0,-1,0,1,1,information,2,4,-0.49999999999999989,5.7186857192667309e-14,1.1102230246251565e-16 +K4,0,-1,0,1,1,covariance,2,4,52.449462267691345,5.6296128748179335e-09,8.8107299234252423e-13 +K4,0,-1,0,1,1,information,3,1,-0.21497802228274204,7.0378899485062419e-15,1.1102230246251565e-16 +K4,0,-1,0,1,1,covariance,3,1,-8.2828873952574007,5.6296128748179335e-09,1.4566126083082054e-13 +K4,0,-1,0,1,1,information,3,2,1.1102230246251565e-16,2.0912533482946806e-14,1.1102230246251565e-16 +K4,0,-1,0,1,1,covariance,3,2,7.1858723959756174,5.6296128748179335e-09,1.2168044349891716e-13 +K4,0,-1,0,1,1,information,3,3,3.7881313179889826,4.2418187056403696e-14,8.8817841970012523e-16 +K4,0,-1,0,1,1,covariance,3,3,3.0896092721259758,5.6296128748179335e-09,4.8405723873656825e-14 +K4,0,-1,0,1,1,information,3,4,-0.60748901114137099,9.5203240047186868e-14,0 +K4,0,-1,0,1,1,covariance,3,4,20.550963336203186,5.6296128748179335e-09,3.4816594052244909e-13 +K4,0,-1,0,1,1,information,4,1,0.16666666666666666,7.9653211246202002e-15,0 +K4,0,-1,0,1,1,covariance,4,1,-61.746409241828232,5.6296128748179335e-09,1.0587086762825493e-12 +K4,0,-1,0,1,1,information,4,2,-0.49999999999999989,5.7186857192667309e-14,1.1102230246251565e-16 +K4,0,-1,0,1,1,covariance,4,2,52.449462267691345,5.6296128748179335e-09,8.8107299234252423e-13 +K4,0,-1,0,1,1,information,4,3,-0.60748901114137099,9.5203240047186868e-14,0 +K4,0,-1,0,1,1,covariance,4,3,20.550963336203186,5.6296128748179335e-09,3.4816594052244909e-13 +K4,0,-1,0,1,1,information,4,4,0.33333333333333331,6.9369225327983265e-15,5.5511151231257827e-17 +K4,0,-1,0,1,1,covariance,4,4,150.00085120778905,5.6296128748179335e-09,2.5011104298755527e-12 +K4,0.20000000000000001,-1,0,1,1,score_mean,1,0,5.5511151231257827e-17,2.8528487499521722e-13,0 +K4,0.20000000000000001,-1,0,1,1,score_mean,2,0,-8.3266726846886741e-17,1.0184268922708439e-14,0 +K4,0.20000000000000001,-1,0,1,1,score_mean,3,0,4.3715031594615539e-16,9.9936553895907295e-15,4.7184478546569153e-16 +K4,0.20000000000000001,-1,0,1,1,score_mean,4,0,1.3877787807814457e-17,4.3186164064484242e-15,5.5511151231257827e-17 +K4,0.20000000000000001,-1,0,1,1,information,1,1,0.42281804789684785,1.0417546871124578e-13,5.5511151231257827e-17 +K4,0.20000000000000001,-1,0,1,1,covariance,1,1,11.34963081600711,2.7701362136325322e-09,2.1316282072803006e-14 +K4,0.20000000000000001,-1,0,1,1,information,1,2,0.40375530809928734,4.2483862050215942e-14,5.5511151231257827e-17 +K4,0.20000000000000001,-1,0,1,1,covariance,1,2,-9.4915182051452316,2.7701362136325322e-09,2.4868995751603507e-14 +K4,0.20000000000000001,-1,0,1,1,information,1,3,-0.50632393410685084,1.0213503163508041e-14,0 +K4,0.20000000000000001,-1,0,1,1,covariance,1,3,-3.2962301835545347,2.7701362136325322e-09,1.2434497875801753e-14 +K4,0.20000000000000001,-1,0,1,1,information,1,4,0.085516746569247584,7.9632786278445004e-15,4.163336342344337e-17 +K4,0.20000000000000001,-1,0,1,1,covariance,1,4,-19.12535484099967,2.7701362136325322e-09,5.6843418860808015e-14 +K4,0.20000000000000001,-1,0,1,1,information,2,1,0.40375530809928734,4.2483862050215942e-14,5.5511151231257827e-17 +K4,0.20000000000000001,-1,0,1,1,covariance,2,1,-9.4915182051452351,2.7701362136325322e-09,2.6645352591003757e-14 +K4,0.20000000000000001,-1,0,1,1,information,2,2,1.8004352169050089,2.0026902428011473e-14,0 +K4,0.20000000000000001,-1,0,1,1,covariance,2,2,10.439438562168469,2.7701362136325322e-09,3.3750779948604759e-14 +K4,0.20000000000000001,-1,0,1,1,information,2,3,-1.4399130525771171,2.8130463515225329e-14,1.3322676295501878e-15 +K4,0.20000000000000001,-1,0,1,1,covariance,2,3,3.8833225869146162,2.7701362136325322e-09,1.8207657603852567e-14 +K4,0.20000000000000001,-1,0,1,1,information,2,4,-0.4057496004870953,7.3781004667445251e-14,0 +K4,0.20000000000000001,-1,0,1,1,covariance,2,4,20.632515927261547,2.7701362136325322e-09,6.7501559897209518e-14 +K4,0.20000000000000001,-1,0,1,1,information,3,1,-0.50632393410685084,1.0213503163508041e-14,0 +K4,0.20000000000000001,-1,0,1,1,covariance,3,1,-3.2962301835545316,2.7701362136325322e-09,1.0658141036401503e-14 +K4,0.20000000000000001,-1,0,1,1,information,3,2,-1.4399130525771171,2.8130463515225329e-14,1.3322676295501878e-15 +K4,0.20000000000000001,-1,0,1,1,covariance,3,2,3.883322586914614,2.7701362136325322e-09,1.4210854715202004e-14 +K4,0.20000000000000001,-1,0,1,1,information,3,3,5.1371512172830531,6.5993031498592066e-13,1.7763568394002505e-15 +K4,0.20000000000000001,-1,0,1,1,covariance,3,3,1.6884223283126956,2.7701362136325322e-09,7.7715611723760958e-15 +K4,0.20000000000000001,-1,0,1,1,information,3,4,-0.47125199756452318,7.0976029169501514e-14,5.5511151231257827e-16 +K4,0.20000000000000001,-1,0,1,1,covariance,3,4,7.9596355931818676,2.7701362136325322e-09,2.5757174171303632e-14 +K4,0.20000000000000001,-1,0,1,1,information,4,1,0.085516746569247584,7.9632786278445004e-15,4.163336342344337e-17 +K4,0.20000000000000001,-1,0,1,1,covariance,4,1,-19.125354840999663,2.7701362136325322e-09,4.2632564145606011e-14 +K4,0.20000000000000001,-1,0,1,1,information,4,2,-0.4057496004870953,7.3781004667445251e-14,0 +K4,0.20000000000000001,-1,0,1,1,covariance,4,2,20.632515927261547,2.7701362136325322e-09,6.0396132539608516e-14 +K4,0.20000000000000001,-1,0,1,1,information,4,3,-0.47125199756452318,7.0976029169501514e-14,5.5511151231257827e-16 +K4,0.20000000000000001,-1,0,1,1,covariance,4,3,7.9596355931818747,2.7701362136325322e-09,3.4638958368304884e-14 +K4,0.20000000000000001,-1,0,1,1,information,4,4,0.33333333333333331,6.9369225327983265e-15,5.5511151231257827e-17 +K4,0.20000000000000001,-1,0,1,1,covariance,4,4,44.274502172062014,2.7701362136325322e-09,1.0658141036401503e-13 +K4,-0.20000000000000001,-1,0,1,1,score_mean,1,0,-8.3266726846886741e-17,2.8528975409731289e-13,2.7755575615628914e-17 +K4,-0.20000000000000001,-1,0,1,1,score_mean,2,0,2.7755575615628914e-17,1.0188877802985553e-14,2.4980018054066022e-16 +K4,-0.20000000000000001,-1,0,1,1,score_mean,3,0,6.9388939039072284e-17,9.996703294690542e-15,1.5126788710517758e-15 +K4,-0.20000000000000001,-1,0,1,1,score_mean,4,0,1.3877787807814457e-17,4.3186164064484242e-15,5.5511151231257827e-17 +K4,-0.20000000000000001,-1,0,1,1,information,1,1,0.42281804789684785,1.0417482564665784e-13,1.6653345369377348e-16 +K4,-0.20000000000000001,-1,0,1,1,covariance,1,1,20.689242350775078,9.9586013860920271e-09,1.1368683772161603e-13 +K4,-0.20000000000000001,-1,0,1,1,information,1,2,-0.40375530809928734,4.2483583191631484e-14,2.2204460492503131e-16 +K4,-0.20000000000000001,-1,0,1,1,covariance,1,2,-9.0216949341289112,9.9586013860920271e-09,4.9737991503207013e-14 +K4,-0.20000000000000001,-1,0,1,1,information,1,3,-0.50632393410685061,1.0223907949742628e-14,1.2212453270876722e-15 +K4,-0.20000000000000001,-1,0,1,1,covariance,1,3,-2.0201935693717292,9.9586013860920271e-09,1.1546319456101628e-14 +K4,-0.20000000000000001,-1,0,1,1,information,1,4,0.29930861299236661,6.8646317418092408e-15,5.5511151231257827e-17 +K4,-0.20000000000000001,-1,0,1,1,covariance,1,4,-41.472961678335487,9.9586013860920271e-09,2.5579538487363607e-13 +K4,-0.20000000000000001,-1,0,1,1,information,2,1,-0.40375530809928734,4.2483583191631484e-14,2.2204460492503131e-16 +K4,-0.20000000000000001,-1,0,1,1,covariance,2,1,-9.0216949341289183,9.9586013860920271e-09,6.3948846218409017e-14 +K4,-0.20000000000000001,-1,0,1,1,information,2,2,1.8004352169050084,2.0025931659980822e-14,4.4408920985006262e-16 +K4,-0.20000000000000001,-1,0,1,1,covariance,2,2,6.0609927078141812,9.9586013860920271e-09,3.2862601528904634e-14 +K4,-0.20000000000000001,-1,0,1,1,information,2,3,1.4399130525771162,2.8159271575060025e-14,0 +K4,-0.20000000000000001,-1,0,1,1,covariance,2,3,1.0053977076271283,9.9586013860920271e-09,4.6629367034256575e-15 +K4,-0.20000000000000001,-1,0,1,1,information,2,4,-0.66320973162849983,3.8845932198325267e-14,1.1102230246251565e-16 +K4,-0.20000000000000001,-1,0,1,1,covariance,2,4,22.621301384804362,9.9586013860920271e-09,1.2434497875801753e-13 +K4,-0.20000000000000001,-1,0,1,1,information,3,1,-0.50632393410685061,1.0223907949742628e-14,1.2212453270876722e-15 +K4,-0.20000000000000001,-1,0,1,1,covariance,3,1,-2.0201935693717274,9.9586013860920271e-09,1.1546319456101628e-14 +K4,-0.20000000000000001,-1,0,1,1,information,3,2,1.4399130525771162,2.8159271575060025e-14,0 +K4,-0.20000000000000001,-1,0,1,1,covariance,3,2,1.0053977076271283,9.9586013860920271e-09,4.8849813083506888e-15 +K4,-0.20000000000000001,-1,0,1,1,information,3,3,5.1371512172830531,6.5999860437025368e-13,1.7763568394002505e-15 +K4,-0.20000000000000001,-1,0,1,1,covariance,3,3,0.52308354891336539,9.9586013860920271e-09,7.7715611723760958e-16 +K4,-0.20000000000000001,-1,0,1,1,information,3,4,-0.81604865814249883,1.0245770603461905e-13,5.5511151231257827e-16 +K4,-0.20000000000000001,-1,0,1,1,covariance,3,4,5.0949375218016453,9.9586013860920271e-09,2.2204460492503131e-14 +K4,-0.20000000000000001,-1,0,1,1,information,4,1,0.29930861299236661,6.8646317418092408e-15,5.5511151231257827e-17 +K4,-0.20000000000000001,-1,0,1,1,covariance,4,1,-41.47296167833548,9.9586013860920271e-09,2.5579538487363607e-13 +K4,-0.20000000000000001,-1,0,1,1,information,4,2,-0.66320973162849983,3.8845932198325267e-14,1.1102230246251565e-16 +K4,-0.20000000000000001,-1,0,1,1,covariance,4,2,22.621301384804362,9.9586013860920271e-09,1.2434497875801753e-13 +K4,-0.20000000000000001,-1,0,1,1,information,4,3,-0.81604865814249883,1.0245770603461905e-13,5.5511151231257827e-16 +K4,-0.20000000000000001,-1,0,1,1,covariance,4,3,5.0949375218016479,9.9586013860920271e-09,2.2204460492503131e-14 +K4,-0.20000000000000001,-1,0,1,1,information,4,4,0.33333333333333331,6.9369225327983265e-15,5.5511151231257827e-17 +K4,-0.20000000000000001,-1,0,1,1,covariance,4,4,97.720796355353357,9.9586013860920271e-09,5.2580162446247414e-13 +K4,0.20000000000000001,0.20000000000000001,0,1,1,score_mean,1,0,5.5511151231257827e-17,1.6383944337259331e-13,0 +K4,0.20000000000000001,0.20000000000000001,0,1,1,score_mean,2,0,0,1.0986773300057703e-14,0 +K4,0.20000000000000001,0.20000000000000001,0,1,1,score_mean,3,0,2.9143354396410359e-16,7.7972415573222655e-15,2.4980018054066022e-16 +K4,0.20000000000000001,0.20000000000000001,0,1,1,score_mean,4,0,8.3266726846886741e-17,7.7432824570250645e-15,4.9960036108132044e-16 +K4,0.20000000000000001,0.20000000000000001,0,1,1,information,1,1,1.4640623212952995,6.4161459293543203e-13,4.4408920985006262e-16 +K4,0.20000000000000001,0.20000000000000001,0,1,1,covariance,1,1,2.1351445566107987,1.4223404387054416e-10,1.7763568394002505e-15 +K4,0.20000000000000001,0.20000000000000001,0,1,1,information,1,2,-0.24839833167821807,1.7409407631857603e-14,2.7755575615628914e-16 +K4,0.20000000000000001,0.20000000000000001,0,1,1,covariance,1,2,-1.5997159347447083,1.4223404387054416e-10,8.8817841970012523e-16 +K4,0.20000000000000001,0.20000000000000001,0,1,1,information,1,3,-0.85705081652806958,1.2352772520377106e-13,1.8873791418627661e-15 +K4,0.20000000000000001,0.20000000000000001,0,1,1,covariance,1,3,-0.54135636065459947,1.4223404387054416e-10,1.1102230246251565e-16 +K4,0.20000000000000001,0.20000000000000001,0,1,1,information,1,4,1.655655298971344,2.1773945240129797e-14,4.4408920985006262e-16 +K4,0.20000000000000001,0.20000000000000001,0,1,1,covariance,1,4,-1.8043136019858628,1.4223404387054416e-10,3.1086244689504383e-15 +K4,0.20000000000000001,0.20000000000000001,0,1,1,information,2,1,-0.24839833167821807,1.7409407631857603e-14,2.7755575615628914e-16 +K4,0.20000000000000001,0.20000000000000001,0,1,1,covariance,2,1,-1.5997159347447076,1.4223404387054416e-10,1.3322676295501878e-15 +K4,0.20000000000000001,0.20000000000000001,0,1,1,information,2,2,2.5811253175020012,1.2731249312094471e-13,4.4408920985006262e-16 +K4,0.20000000000000001,0.20000000000000001,0,1,1,covariance,2,2,3.1294117124955343,1.4223404387054416e-10,3.1086244689504383e-15 +K4,0.20000000000000001,0.20000000000000001,0,1,1,information,2,3,-1.4798148329071803,1.8668920789260843e-12,1.3322676295501878e-15 +K4,0.20000000000000001,0.20000000000000001,0,1,1,covariance,2,3,1.3321037052816882,1.4223404387054416e-10,1.7763568394002505e-15 +K4,0.20000000000000001,0.20000000000000001,0,1,1,information,2,4,-2.1383901718099465,4.853323869156725e-14,4.4408920985006262e-16 +K4,0.20000000000000001,0.20000000000000001,0,1,1,covariance,2,4,2.5736667797413739,1.4223404387054416e-10,1.3322676295501878e-15 +K4,0.20000000000000001,0.20000000000000001,0,1,1,information,3,1,-0.85705081652806958,1.2352772520377106e-13,1.8873791418627661e-15 +K4,0.20000000000000001,0.20000000000000001,0,1,1,covariance,3,1,-0.54135636065459936,1.4223404387054416e-10,3.3306690738754696e-16 +K4,0.20000000000000001,0.20000000000000001,0,1,1,information,3,2,-1.4798148329071803,1.8668920789260843e-12,1.3322676295501878e-15 +K4,0.20000000000000001,0.20000000000000001,0,1,1,covariance,3,2,1.3321037052816882,1.4223404387054416e-10,1.9984014443252818e-15 +K4,0.20000000000000001,0.20000000000000001,0,1,1,information,3,3,4.4532295274345364,1.4098521730390255e-12,8.8817841970012523e-16 +K4,0.20000000000000001,0.20000000000000001,0,1,1,covariance,3,3,0.82024017966704421,1.4223404387054416e-10,1.3322676295501878e-15 +K4,0.20000000000000001,0.20000000000000001,0,1,1,information,3,4,-1.03830517324992,4.2996277718053686e-14,2.886579864025407e-15 +K4,0.20000000000000001,0.20000000000000001,0,1,1,covariance,3,4,1.1031639888507221,1.4223404387054416e-10,1.3322676295501878e-15 +K4,0.20000000000000001,0.20000000000000001,0,1,1,information,4,1,1.655655298971344,2.1773945240129797e-14,4.4408920985006262e-16 +K4,0.20000000000000001,0.20000000000000001,0,1,1,covariance,4,1,-1.8043136019858617,1.4223404387054416e-10,1.7763568394002505e-15 +K4,0.20000000000000001,0.20000000000000001,0,1,1,information,4,2,-2.1383901718099465,4.853323869156725e-14,4.4408920985006262e-16 +K4,0.20000000000000001,0.20000000000000001,0,1,1,covariance,4,2,2.5736667797413739,1.4223404387054416e-10,1.3322676295501878e-15 +K4,0.20000000000000001,0.20000000000000001,0,1,1,information,4,3,-1.03830517324992,4.2996277718053686e-14,2.886579864025407e-15 +K4,0.20000000000000001,0.20000000000000001,0,1,1,covariance,4,3,1.1031639888507223,1.4223404387054416e-10,1.1102230246251565e-15 +K4,0.20000000000000001,0.20000000000000001,0,1,1,information,4,4,4.1666666666666652,5.023558147542987e-13,2.6645352591003757e-15 +K4,0.20000000000000001,0.20000000000000001,0,1,1,covariance,4,4,2.5526990400032159,1.4223404387054416e-10,4.4408920985006262e-16 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,score_mean,1,0,1.1102230246251565e-16,4.2392312095439115e-14,1.1102230246251565e-16 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,score_mean,2,0,-1.5265566588595902e-16,9.9838225751155517e-15,2.2204460492503131e-16 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,score_mean,3,0,2.9490299091605721e-17,7.3031245470008441e-15,7.7889084071358639e-16 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,score_mean,4,0,1.3877787807814457e-17,9.764210803268642e-15,4.9960036108132044e-16 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,information,1,1,0.90406282888283718,6.3464850113610699e-14,2.2204460492503131e-16 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,covariance,1,1,8.6744398092647845,6.8764011681871096e-10,5.6843418860808015e-14 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,information,1,2,-0.64646673385812081,3.5843284986717385e-14,0 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,covariance,1,2,-6.3692755780025916,6.8764011681871096e-10,6.0396132539608516e-14 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,information,1,3,-0.41369970625051533,8.9669886173885182e-15,4.4408920985006262e-16 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,covariance,1,3,-3.3688695627086336,6.8764011681871096e-10,3.2418512319054571e-14 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,information,1,4,0.83395326199470887,1.1136118018352561e-14,2.2204460492503131e-16 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,covariance,1,4,-14.813136760005488,6.8764011681871096e-10,1.2612133559741778e-13 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,information,2,1,-0.64646673385812081,3.5843284986717385e-14,0 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,covariance,2,1,-6.3692755780025907,6.8764011681871096e-10,5.8619775700208265e-14 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,information,2,2,1.720239473653129,1.9159196195462778e-14,4.4408920985006262e-16 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,covariance,2,2,7.6052055432666963,6.8764011681871096e-10,5.9507954119908391e-14 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,information,2,3,0.33132360843604525,1.6307734239994232e-14,0 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,covariance,2,3,3.5369067567418297,6.8764011681871096e-10,3.3306690738754696e-14 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,information,2,4,-1.1935758337830673,3.2015584750660031e-14,4.4408920985006262e-16 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,covariance,2,4,14.554718500801616,6.8764011681871096e-10,1.2612133559741778e-13 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,information,3,1,-0.41369970625051533,8.9669886173885182e-15,4.4408920985006262e-16 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,covariance,3,1,-3.3688695627086327,6.8764011681871096e-10,3.2418512319054571e-14 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,information,3,2,0.33132360843604525,1.6307734239994232e-14,0 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,covariance,3,2,3.5369067567418293,6.8764011681871096e-10,3.3750779948604759e-14 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,information,3,3,1.9608424411720398,2.8706600224446818e-13,1.3322676295501878e-15 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,covariance,3,3,2.4184050035980649,6.8764011681871096e-10,1.865174681370263e-14 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,information,3,4,-0.83066315968686366,7.9310751676972196e-14,8.8817841970012523e-16 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,covariance,3,4,7.5935379525078668,6.8764011681871096e-10,7.1942451995710144e-14 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,information,4,1,0.83395326199470887,1.1136118018352561e-14,2.2204460492503131e-16 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,covariance,4,1,-14.81313676000549,6.8764011681871096e-10,1.2434497875801753e-13 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,information,4,2,-1.1935758337830673,3.2015584750660031e-14,4.4408920985006262e-16 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,covariance,4,2,14.554718500801613,6.8764011681871096e-10,1.2789769243681803e-13 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,information,4,3,-0.83066315968686366,7.9310751676972196e-14,8.8817841970012523e-16 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,covariance,4,3,7.5935379525078677,6.8764011681871096e-10,7.1054273576010019e-14 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,information,4,4,1.1904761904761902,1.3241288466645756e-14,4.4408920985006262e-16 +K4,-0.20000000000000001,-0.20000000000000001,0,1,1,covariance,4,4,31.107968825039183,6.8764011681871096e-10,2.6645352591003757e-13 +K4,0.40000000000000002,-1,0,1,1,score_mean,1,0,-2.7755575615628914e-17,5.1904553785282903e-14,8.3266726846886741e-17 +K4,0.40000000000000002,-1,0,1,1,score_mean,2,0,1.3877787807814457e-17,1.2534684018911071e-13,1.3877787807814457e-17 +K4,0.40000000000000002,-1,0,1,1,score_mean,3,0,-7.9797279894933126e-17,1.0767481159232199e-14,2.0816681711721685e-17 +K4,0.40000000000000002,-1,0,1,1,score_mean,4,0,1.3877787807814457e-17,4.3186164064484242e-15,5.5511151231257827e-17 +K4,0.40000000000000002,-1,0,1,1,information,1,1,1.197234451969466,1.408843861815332e-12,1.5543122344752192e-14 +K4,0.40000000000000002,-1,0,1,1,covariance,1,1,7.2142625065116253,3.1195006684206068e-08,2.4691360067663481e-12 +K4,0.40000000000000002,-1,0,1,1,information,1,2,2.0681716501493104,3.6331579107544694e-12,3.8191672047105385e-14 +K4,0.40000000000000002,-1,0,1,1,covariance,1,2,-6.1462921936683603,3.1195006684206068e-08,2.5668356329333619e-12 +K4,0.40000000000000002,-1,0,1,1,information,1,3,-3.3618280941867953,6.1370424512254564e-12,2.6645352591003757e-15 +K4,0.40000000000000002,-1,0,1,1,covariance,1,3,-1.5786406285843446,3.1195006684206068e-08,7.6982864527508355e-13 +K4,0.40000000000000002,-1,0,1,1,information,1,4,0.026426127993552973,1.0010749776634642e-13,3.4694469519536142e-18 +K4,0.40000000000000002,-1,0,1,1,covariance,1,4,-8.8054809136141188,3.1195006684206068e-08,3.7534420016527292e-12 +K4,0.40000000000000002,-1,0,1,1,information,2,1,2.0681716501493104,3.6331579107544694e-12,3.8191672047105385e-14 +K4,0.40000000000000002,-1,0,1,1,covariance,2,1,-6.1462921936683532,3.1195006684206068e-08,2.5774937739697634e-12 +K4,0.40000000000000002,-1,0,1,1,information,2,2,4.9414762592707273,5.1293604436086615e-12,1.7763568394002505e-15 +K4,0.40000000000000002,-1,0,1,1,covariance,2,2,7.0720790636173341,3.1195006684206068e-08,2.5854873797470646e-12 +K4,0.40000000000000002,-1,0,1,1,information,2,3,-7.8321880702106075,1.1100878076473396e-11,4.4408920985006262e-15 +K4,0.40000000000000002,-1,0,1,1,covariance,2,3,2.2439008287624178,3.1195006684206068e-08,7.5361938911555626e-13 +K4,0.40000000000000002,-1,0,1,1,information,2,4,-0.3506013466827842,7.7076321912584926e-14,0 +K4,0.40000000000000002,-1,0,1,1,covariance,2,4,10.439977658199723,3.1195006684206068e-08,3.7676528563679312e-12 +K4,0.40000000000000002,-1,0,1,1,information,3,1,-3.3618280941867953,6.1370424512254564e-12,2.6645352591003757e-15 +K4,0.40000000000000002,-1,0,1,1,covariance,3,1,-1.5786406285843426,3.1195006684206068e-08,7.7071682369478367e-13 +K4,0.40000000000000002,-1,0,1,1,information,3,2,-7.8321880702106075,1.1100878076473396e-11,4.4408920985006262e-15 +K4,0.40000000000000002,-1,0,1,1,covariance,3,2,2.2439008287624169,3.1195006684206068e-08,7.5450756753525638e-13 +K4,0.40000000000000002,-1,0,1,1,information,3,3,17.213939009145264,2.7559372411762979e-11,2.1316282072803006e-14 +K4,0.40000000000000002,-1,0,1,1,covariance,3,3,0.84521660364681916,3.1195006684206068e-08,2.156053113822054e-13 +K4,0.40000000000000002,-1,0,1,1,information,3,4,-0.37349663329303939,3.3419834578102404e-14,5.5511151231257827e-17 +K4,0.40000000000000002,-1,0,1,1,covariance,3,4,3.432352702676928,3.1195006684206068e-08,1.0964562591198046e-12 +K4,0.40000000000000002,-1,0,1,1,information,4,1,0.026426127993552973,1.0010749776634642e-13,3.4694469519536142e-18 +K4,0.40000000000000002,-1,0,1,1,covariance,4,1,-8.8054809136141063,3.1195006684206068e-08,3.765876499528531e-12 +K4,0.40000000000000002,-1,0,1,1,information,4,2,-0.3506013466827842,7.7076321912584926e-14,0 +K4,0.40000000000000002,-1,0,1,1,covariance,4,2,10.439977658199723,3.1195006684206068e-08,3.7676528563679312e-12 +K4,0.40000000000000002,-1,0,1,1,information,4,3,-0.37349663329303939,3.3419834578102404e-14,5.5511151231257827e-17 +K4,0.40000000000000002,-1,0,1,1,covariance,4,3,3.4323527026769307,3.1195006684206068e-08,1.0933476346508542e-12 +K4,0.40000000000000002,-1,0,1,1,information,4,4,0.33333333333333331,6.9369225327983265e-15,5.5511151231257827e-17 +K4,0.40000000000000002,-1,0,1,1,covariance,4,4,18.524811512085165,3.1195006684206068e-08,5.4853899200679734e-12 +K4,-0.40000000000000002,-1,0,1,1,score_mean,1,0,-5.5511151231257827e-17,5.190518001652141e-14,5.5511151231257827e-17 +K4,-0.40000000000000002,-1,0,1,1,score_mean,2,0,2.7755575615628914e-17,1.2534778573300287e-13,9.7144514654701197e-17 +K4,-0.40000000000000002,-1,0,1,1,score_mean,3,0,6.609296443471635e-16,1.0768726765151763e-14,5.8113236445223038e-16 +K4,-0.40000000000000002,-1,0,1,1,score_mean,4,0,1.3877787807814457e-17,4.3186164064484242e-15,5.5511151231257827e-17 +K4,-0.40000000000000002,-1,0,1,1,information,1,1,1.1972344519694664,1.4088465383857739e-12,1.5765166949677223e-14 +K4,-0.40000000000000002,-1,0,1,1,covariance,1,1,8.199126910996311,2.9700470505900323e-08,1.1830536550405668e-12 +K4,-0.40000000000000002,-1,0,1,1,information,1,2,-2.0681716501493113,3.6331552082422525e-12,3.8635761256955448e-14 +K4,-0.40000000000000002,-1,0,1,1,covariance,1,2,-0.27475514805134665,2.9700470505900323e-08,5.2646775827724923e-13 +K4,-0.40000000000000002,-1,0,1,1,information,1,3,-3.3618280941867988,6.1357084519940403e-12,2.2204460492503131e-15 +K4,-0.40000000000000002,-1,0,1,1,covariance,1,3,0.97332749686261189,2.9700470505900323e-08,3.028688411177427e-13 +K4,-0.40000000000000002,-1,0,1,1,information,1,4,0.55494868786461293,1.0664211207074178e-14,1.1102230246251565e-16 +K4,-0.40000000000000002,-1,0,1,1,covariance,1,4,-11.014276511887333,2.9700470505900323e-08,2.4265034426207421e-12 +K4,-0.40000000000000002,-1,0,1,1,information,2,1,-2.0681716501493113,3.6331552082422525e-12,3.8635761256955448e-14 +K4,-0.40000000000000002,-1,0,1,1,covariance,2,1,-0.27475514805135037,2.9700470505900323e-08,5.28355137419112e-13 +K4,-0.40000000000000002,-1,0,1,1,information,2,2,4.94147625927073,5.1285138172511935e-12,2.6645352591003757e-15 +K4,-0.40000000000000002,-1,0,1,1,covariance,2,2,1.5999088379674133,2.9700470505900323e-08,3.1086244689504383e-14 +K4,-0.40000000000000002,-1,0,1,1,information,2,3,7.8321880702106128,1.1100877425892034e-11,7.1054273576010019e-15 +K4,-0.40000000000000002,-1,0,1,1,covariance,2,3,-0.56919759559401706,2.9700470505900323e-08,6.6946448384896939e-14 +K4,-0.40000000000000002,-1,0,1,1,information,2,4,-0.97070505299486576,2.6314420704079338e-14,1.1102230246251565e-16 +K4,-0.40000000000000002,-1,0,1,1,covariance,2,4,3.10711242373403,2.9700470505900323e-08,7.3541173151170369e-13 +K4,-0.40000000000000002,-1,0,1,1,information,3,1,-3.3618280941867988,6.1357084519940403e-12,2.2204460492503131e-15 +K4,-0.40000000000000002,-1,0,1,1,covariance,3,1,0.97332749686261388,2.9700470505900323e-08,3.0508928716699302e-13 +K4,-0.40000000000000002,-1,0,1,1,information,3,2,7.8321880702106128,1.1100877425892034e-11,7.1054273576010019e-15 +K4,-0.40000000000000002,-1,0,1,1,covariance,3,2,-0.56919759559401706,2.9700470505900323e-08,6.6946448384896939e-14 +K4,-0.40000000000000002,-1,0,1,1,information,3,3,17.213939009145275,2.7562912710420496e-11,1.7763568394002505e-14 +K4,-0.40000000000000002,-1,0,1,1,covariance,3,3,0.37311807996320889,2.9700470505900323e-08,5.517808432387028e-14 +K4,-0.40000000000000002,-1,0,1,1,information,3,4,-1.1767626324871641,9.5388695796624078e-14,0 +K4,-0.40000000000000002,-1,0,1,1,covariance,3,4,-1.960795156308103,2.9700470505900323e-08,5.078160114635466e-13 +K4,-0.40000000000000002,-1,0,1,1,information,4,1,0.55494868786461293,1.0664211207074178e-14,1.1102230246251565e-16 +K4,-0.40000000000000002,-1,0,1,1,covariance,4,1,-11.014276511887338,2.9700470505900323e-08,2.4282797994601424e-12 +K4,-0.40000000000000002,-1,0,1,1,information,4,2,-0.97070505299486576,2.6314420704079338e-14,1.1102230246251565e-16 +K4,-0.40000000000000002,-1,0,1,1,covariance,4,2,3.107112423734022,2.9700470505900323e-08,7.2875039336395275e-13 +K4,-0.40000000000000002,-1,0,1,1,information,4,3,-1.1767626324871641,9.5388695796624078e-14,0 +K4,-0.40000000000000002,-1,0,1,1,covariance,4,3,-1.9607951563080994,2.9700470505900323e-08,5.042632977847461e-13 +K4,-0.40000000000000002,-1,0,1,1,information,4,4,0.33333333333333331,6.9369225327983265e-15,5.5511151231257827e-17 +K4,-0.40000000000000002,-1,0,1,1,covariance,4,4,23.463172674259337,2.9700470505900323e-08,4.3876013933186186e-12 +K4,-1,-0.20000000000000001,0,1,1,score_mean,1,0,6.3837823915946501e-16,1.3739022842921464e-14,6.3837823915946501e-16 +K4,-1,-0.20000000000000001,0,1,1,score_mean,2,0,-7.2164496600635175e-16,1.1243981938446503e-14,7.7715611723760958e-16 +K4,-1,-0.20000000000000001,0,1,1,score_mean,3,0,-7.9797279894933126e-17,7.1670599667775286e-15,5.8980598183211441e-17 +K4,-1,-0.20000000000000001,0,1,1,score_mean,4,0,1.3877787807814457e-17,9.764210803268642e-15,4.9960036108132044e-16 +K4,-1,-0.20000000000000001,0,1,1,information,1,1,9.5238095238095202,1.0577752789541867e-13,1.7763568394002505e-15 +K4,-1,-0.20000000000000001,0,1,1,covariance,1,1,13.427531025528127,1.2949625521018466e-08,8.2955864399991697e-13 +K4,-1,-0.20000000000000001,0,1,1,information,1,2,-8.0952380952380931,9.2137688695930224e-14,1.7763568394002505e-15 +K4,-1,-0.20000000000000001,0,1,1,covariance,1,2,4.1066619504401825,1.2949625521018466e-08,2.3003821070233244e-13 +K4,-1,-0.20000000000000001,0,1,1,information,1,3,-5.3793744108084249,1.3285191603528277e-13,8.8817841970012523e-16 +K4,-1,-0.20000000000000001,0,1,1,covariance,1,3,3.5755431719299575,1.2949625521018466e-08,2.1582735598713043e-13 +K4,-1,-0.20000000000000001,0,1,1,information,1,4,3.2738095238095228,3.6359757386463079e-14,2.2204460492503131e-15 +K4,-1,-0.20000000000000001,0,1,1,covariance,1,4,-22.726629515789927,1.2949625521018466e-08,1.4814816040598089e-12 +K4,-1,-0.20000000000000001,0,1,1,information,2,1,-8.0952380952380931,9.2137688695930224e-14,1.7763568394002505e-15 +K4,-1,-0.20000000000000001,0,1,1,covariance,2,1,4.1066619504401913,1.2949625521018466e-08,2.2026824808563106e-13 +K4,-1,-0.20000000000000001,0,1,1,information,2,2,7.3809523809523796,8.19691914471793e-14,1.7763568394002505e-15 +K4,-1,-0.20000000000000001,0,1,1,covariance,2,2,3.001548101904048,1.2949625521018466e-08,6.2172489379008766e-14 +K4,-1,-0.20000000000000001,0,1,1,information,2,3,4.8980205387375442,1.3042624521509811e-12,0 +K4,-1,-0.20000000000000001,0,1,1,covariance,2,3,-0.059303844664920558,1.2949625521018466e-08,5.7183424662099469e-14 +K4,-1,-0.20000000000000001,0,1,1,information,2,4,-2.6785714285714275,3.1065045272097907e-14,1.3322676295501878e-15 +K4,-1,-0.20000000000000001,0,1,1,covariance,2,4,-4.6220884660052883,1.2949625521018466e-08,3.9257486150745535e-13 +K4,-1,-0.20000000000000001,0,1,1,information,3,1,-5.3793744108084249,1.3285191603528277e-13,8.8817841970012523e-16 +K4,-1,-0.20000000000000001,0,1,1,covariance,3,1,3.5755431719299584,1.2949625521018466e-08,2.1538326677728037e-13 +K4,-1,-0.20000000000000001,0,1,1,information,3,2,4.8980205387375442,1.3042624521509811e-12,0 +K4,-1,-0.20000000000000001,0,1,1,covariance,3,2,-0.059303844664921307,1.2949625521018466e-08,5.7863436264682377e-14 +K4,-1,-0.20000000000000001,0,1,1,information,3,3,4.0812324244483049,3.4738982050257007e-12,8.8817841970012523e-16 +K4,-1,-0.20000000000000001,0,1,1,covariance,3,3,2.2717793853718038,1.2949625521018466e-08,5.595524044110789e-14 +K4,-1,-0.20000000000000001,0,1,1,information,3,4,-1.6511282267257334,5.7689160051905108e-14,4.4408920985006262e-16 +K4,-1,-0.20000000000000001,0,1,1,covariance,3,4,-6.8153381561154003,1.2949625521018466e-08,3.8546943414985435e-13 +K4,-1,-0.20000000000000001,0,1,1,information,4,1,3.2738095238095228,3.6359757386463079e-14,2.2204460492503131e-15 +K4,-1,-0.20000000000000001,0,1,1,covariance,4,1,-22.726629515789899,1.2949625521018466e-08,1.5099033134902129e-12 +K4,-1,-0.20000000000000001,0,1,1,information,4,2,-2.6785714285714275,3.1065045272097907e-14,1.3322676295501878e-15 +K4,-1,-0.20000000000000001,0,1,1,covariance,4,2,-4.6220884660052661,1.2949625521018466e-08,4.1477932199995848e-13 +K4,-1,-0.20000000000000001,0,1,1,information,4,3,-1.6511282267257334,5.7689160051905108e-14,4.4408920985006262e-16 +K4,-1,-0.20000000000000001,0,1,1,covariance,4,3,-6.8153381561153941,1.2949625521018466e-08,3.9168668308775523e-13 +K4,-1,-0.20000000000000001,0,1,1,information,4,4,1.1904761904761902,1.3241288466645756e-14,4.4408920985006262e-16 +K4,-1,-0.20000000000000001,0,1,1,covariance,4,4,43.486014468346227,1.2949625521018466e-08,2.6787461138155777e-12 +K4,-0.20000000000000001,-2,0,1,1,score_mean,1,0,-1.6653345369377348e-16,3.4358142817886565e-13,8.3266726846886741e-17 +K4,-0.20000000000000001,-2,0,1,1,score_mean,2,0,4.8572257327350599e-16,1.0663611673064595e-14,4.8572257327350599e-16 +K4,-0.20000000000000001,-2,0,1,1,score_mean,3,0,1.8457457784393227e-15,1.5417130123815516e-14,2.491062911502695e-15 +K4,-0.20000000000000001,-2,0,1,1,score_mean,4,0,0,3.2872037882233381e-15,1.3877787807814457e-17 +K4,-0.20000000000000001,-2,0,1,1,information,1,1,0.34647965940153425,4.9529811070279577e-13,2.9976021664879227e-15 +K4,-0.20000000000000001,-2,0,1,1,covariance,1,1,13.26304664768459,3.3750176920554732e-07,1.4974688156144111e-12 +K4,-0.20000000000000001,-2,0,1,1,information,1,2,-0.76105424038544844,1.3828139224643833e-12,1.4765966227514582e-14 +K4,-0.20000000000000001,-2,0,1,1,covariance,1,2,-2.4911561074733952,3.3750176920554732e-07,6.2305716141963785e-13 +K4,-0.20000000000000001,-2,0,1,1,information,1,3,-2.4364278694253434,3.8370193559462248e-12,4.4408920985006262e-16 +K4,-0.20000000000000001,-2,0,1,1,covariance,1,3,0.79116806652395766,3.3750176920554732e-07,1.517674874662589e-13 +K4,-0.20000000000000001,-2,0,1,1,information,1,4,0.17268338784395076,1.0122810417400717e-14,2.7755575615628914e-17 +K4,-0.20000000000000001,-2,0,1,1,covariance,1,4,-20.636940947169716,3.3750176920554732e-07,3.1867841698840493e-12 +K4,-0.20000000000000001,-2,0,1,1,information,2,1,-0.76105424038544844,1.3828139224643833e-12,1.4765966227514582e-14 +K4,-0.20000000000000001,-2,0,1,1,covariance,2,1,-2.4911561074733961,3.3750176920554732e-07,6.2350125062948791e-13 +K4,-0.20000000000000001,-2,0,1,1,information,2,2,3.9485509188161263,6.5769553461614248e-12,7.5495165674510645e-15 +K4,-0.20000000000000001,-2,0,1,1,covariance,2,2,2.1845233699196749,3.3750176920554732e-07,1.936228954946273e-13 +K4,-0.20000000000000001,-2,0,1,1,information,2,3,11.364273834370861,1.583522780810359e-11,1.5987211554602254e-14 +K4,-0.20000000000000001,-2,0,1,1,covariance,2,3,-0.45520806019782922,3.3750176920554732e-07,5.0626169922907138e-14 +K4,-0.20000000000000001,-2,0,1,1,information,2,4,-0.53008360588642045,7.1542018679312883e-14,1.1102230246251565e-16 +K4,-0.20000000000000001,-2,0,1,1,covariance,2,4,8.2034184467066904,3.3750176920554732e-07,1.1617373729677638e-12 +K4,-0.20000000000000001,-2,0,1,1,information,3,1,-2.4364278694253434,3.8370193559462248e-12,4.4408920985006262e-16 +K4,-0.20000000000000001,-2,0,1,1,covariance,3,1,0.79116806652395766,3.3750176920554732e-07,1.5065726444163374e-13 +K4,-0.20000000000000001,-2,0,1,1,information,3,2,11.364273834370861,1.583522780810359e-11,1.5987211554602254e-14 +K4,-0.20000000000000001,-2,0,1,1,covariance,3,2,-0.45520806019782922,3.3750176920554732e-07,5.0737192225369654e-14 +K4,-0.20000000000000001,-2,0,1,1,information,3,3,48.29843997426142,8.3423592489562697e-11,7.1054273576010019e-14 +K4,-0.20000000000000001,-2,0,1,1,covariance,3,3,0.12730469189665913,3.3750176920554732e-07,1.3045120539345589e-14 +K4,-0.20000000000000001,-2,0,1,1,information,3,4,-1.0860356359121204,1.5704537874472214e-13,2.2204460492503131e-16 +K4,-0.20000000000000001,-2,0,1,1,covariance,3,4,-1.7974686003071136,3.3750176920554732e-07,2.9021229863701592e-13 +K4,-0.20000000000000001,-2,0,1,1,information,4,1,0.17268338784395076,1.0122810417400717e-14,2.7755575615628914e-17 +K4,-0.20000000000000001,-2,0,1,1,covariance,4,1,-20.63694094716973,3.3750176920554732e-07,3.1974423109204508e-12 +K4,-0.20000000000000001,-2,0,1,1,information,4,2,-0.53008360588642045,7.1542018679312883e-14,1.1102230246251565e-16 +K4,-0.20000000000000001,-2,0,1,1,covariance,4,2,8.2034184467066922,3.3750176920554732e-07,1.1652900866465643e-12 +K4,-0.20000000000000001,-2,0,1,1,information,4,3,-1.0860356359121204,1.5704537874472214e-13,2.2204460492503131e-16 +K4,-0.20000000000000001,-2,0,1,1,covariance,4,3,-1.7974686003071141,3.3750176920554732e-07,2.9132252166164108e-13 +K4,-0.20000000000000001,-2,0,1,1,information,4,4,0.13333333333333333,1.48786142441661e-15,2.7755575615628914e-17 +K4,-0.20000000000000001,-2,0,1,1,covariance,4,4,52.200296654637555,3.3750176920554732e-07,6.3877791944833007e-12 +K4,0.48999999999999999,0.20000000000000001,0,1,1,score_mean,1,0,-1.1102230246251565e-16,1.1159224236908398e-14,1.1102230246251565e-16 +K4,0.48999999999999999,0.20000000000000001,0,1,1,score_mean,2,0,-8.3266726846886741e-17,1.4142326847634426e-14,3.0531133177191805e-16 +K4,0.48999999999999999,0.20000000000000001,0,1,1,score_mean,3,0,4.8572257327350599e-17,1.2361837259136265e-14,2.9143354396410359e-16 +K4,0.48999999999999999,0.20000000000000001,0,1,1,score_mean,4,0,1.6653345369377348e-16,9.2325991635400996e-15,1.0547118733938987e-15 +K4,0.48999999999999999,0.20000000000000001,0,1,1,information,1,1,13.222838166695656,1.0385507366180522e-11,1.2434497875801753e-14 +K4,0.48999999999999999,0.20000000000000001,0,1,1,covariance,1,1,1.9243354543059943,5.8348926560404255e-09,3.0420110874729289e-14 +K4,0.48999999999999999,0.20000000000000001,0,1,1,information,1,2,24.196626047070747,1.7481662157673191e-11,3.5527136788005009e-14 +K4,0.48999999999999999,0.20000000000000001,0,1,1,covariance,1,2,-1.6289888131230887,5.8348926560404255e-09,7.4384942649885488e-14 +K4,0.48999999999999999,0.20000000000000001,0,1,1,information,1,3,-48.334463213020975,3.2333717953031121e-11,7.815970093361102e-14 +K4,0.48999999999999999,0.20000000000000001,0,1,1,covariance,1,3,-0.3418856296911521,5.8348926560404255e-09,2.9032332093947844e-14 +K4,0.48999999999999999,0.20000000000000001,0,1,1,information,1,4,1.1513674797777786,6.077305051870333e-14,0 +K4,0.48999999999999999,0.20000000000000001,0,1,1,covariance,1,4,-1.3497007370655871,5.8348926560404255e-09,4.7295500849031669e-14 +K4,0.48999999999999999,0.20000000000000001,0,1,1,information,2,1,24.196626047070747,1.7481662157673191e-11,3.5527136788005009e-14 +K4,0.48999999999999999,0.20000000000000001,0,1,1,covariance,2,1,-1.6289888131230852,5.8348926560404255e-09,8.1712414612411521e-14 +K4,0.48999999999999999,0.20000000000000001,0,1,1,information,2,2,50.631078825907302,9.3389331268131353e-11,8.5265128291212022e-14 +K4,0.48999999999999999,0.20000000000000001,0,1,1,covariance,2,2,2.5860366515579134,5.8348926560404255e-09,1.389999226830696e-13 +K4,0.48999999999999999,0.20000000000000001,0,1,1,information,2,3,-97.541226933928129,7.3819858670575579e-11,1.1368683772161603e-13 +K4,0.48999999999999999,0.20000000000000001,0,1,1,covariance,2,3,0.8924151271693056,5.8348926560404255e-09,4.9182879990894435e-14 +K4,0.48999999999999999,0.20000000000000001,0,1,1,information,2,4,-1.9019711297052149,2.8290602632934809e-13,6.6613381477509392e-16 +K4,0.48999999999999999,0.20000000000000001,0,1,1,covariance,2,4,1.8246996040152887,5.8348926560404255e-09,9.5923269327613525e-14 +K4,0.48999999999999999,0.20000000000000001,0,1,1,information,3,1,-48.334463213020975,3.2333717953031121e-11,7.815970093361102e-14 +K4,0.48999999999999999,0.20000000000000001,0,1,1,covariance,3,1,-0.34188562969114988,5.8348926560404255e-09,3.3584246494910985e-14 +K4,0.48999999999999999,0.20000000000000001,0,1,1,information,3,2,-97.541226933928129,7.3819858670575579e-11,1.1368683772161603e-13 +K4,0.48999999999999999,0.20000000000000001,0,1,1,covariance,3,2,0.89241512716930449,5.8348926560404255e-09,5.1514348342607263e-14 +K4,0.48999999999999999,0.20000000000000001,0,1,1,information,3,3,196.27926753343951,1.2371031377604526e-10,2.2737367544323206e-13 +K4,0.48999999999999999,0.20000000000000001,0,1,1,covariance,3,3,0.36707684895767956,5.8348926560404255e-09,1.7486012637846216e-14 +K4,0.48999999999999999,0.20000000000000001,0,1,1,information,3,4,-0.90628587092799384,8.3495112609148868e-14,5.5511151231257827e-16 +K4,0.48999999999999999,0.20000000000000001,0,1,1,covariance,3,4,0.58167848764153873,5.8348926560404255e-09,3.6415315207705135e-14 +K4,0.48999999999999999,0.20000000000000001,0,1,1,information,4,1,1.1513674797777786,6.077305051870333e-14,0 +K4,0.48999999999999999,0.20000000000000001,0,1,1,covariance,4,1,-1.3497007370655842,5.8348926560404255e-09,5.2846615972157451e-14 +K4,0.48999999999999999,0.20000000000000001,0,1,1,information,4,2,-1.9019711297052149,2.8290602632934809e-13,6.6613381477509392e-16 +K4,0.48999999999999999,0.20000000000000001,0,1,1,covariance,4,2,1.8246996040152894,5.8348926560404255e-09,9.4591001698063337e-14 +K4,0.48999999999999999,0.20000000000000001,0,1,1,information,4,3,-0.90628587092799384,8.3495112609148868e-14,5.5511151231257827e-16 +K4,0.48999999999999999,0.20000000000000001,0,1,1,covariance,4,3,0.58167848764153973,5.8348926560404255e-09,3.4305891460917337e-14 +K4,0.48999999999999999,0.20000000000000001,0,1,1,information,4,4,4.1666666666666652,1.5601449106967037e-12,8.8817841970012523e-16 +K4,0.48999999999999999,0.20000000000000001,0,1,1,covariance,4,4,1.5724066795399976,5.8348926560404255e-09,6.4614980033184111e-14 +K4,0.20000000000000001,0.48999999999999999,0,1,1,score_mean,1,0,2.7755575615628914e-16,1.8821765381060019e-13,1.6653345369377348e-16 +K4,0.20000000000000001,0.48999999999999999,0,1,1,score_mean,2,0,1.6653345369377348e-16,1.2040819003428354e-14,1.9428902930940239e-16 +K4,0.20000000000000001,0.48999999999999999,0,1,1,score_mean,3,0,-8.0491169285323849e-16,1.6556651006400608e-13,5.5511151231257827e-16 +K4,0.20000000000000001,0.48999999999999999,0,1,1,score_mean,4,0,1.1102230246251565e-16,1.1105032248234636e-14,9.1593399531575415e-16 +K4,0.20000000000000001,0.48999999999999999,0,1,1,information,1,1,38.102398931266848,2.9181114951645697e-11,4.9737991503207013e-14 +K4,0.20000000000000001,0.48999999999999999,0,1,1,covariance,1,1,1.2031658024879486,2.8481035925015792e-09,1.1990408665951691e-14 +K4,0.20000000000000001,0.48999999999999999,0,1,1,information,1,2,-27.454161448922427,2.1048198185639183e-11,2.8421709430404007e-14 +K4,0.20000000000000001,0.48999999999999999,0,1,1,covariance,1,2,-0.86010733445184973,2.8481035925015792e-09,3.1197266991966899e-14 +K4,0.20000000000000001,0.48999999999999999,0,1,1,information,1,3,-10.543905746860107,9.6623303546081762e-12,1.2079226507921703e-13 +K4,0.20000000000000001,0.48999999999999999,0,1,1,covariance,1,3,-0.28226305820386954,2.8481035925015792e-09,1.4543921622589551e-14 +K4,0.20000000000000001,0.48999999999999999,0,1,1,information,1,4,85.097868867666833,1.2498553914762231e-10,1.1368683772161603e-13 +K4,0.20000000000000001,0.48999999999999999,0,1,1,covariance,1,4,-0.83942388972046722,2.8481035925015792e-09,1.6653345369377348e-14 +K4,0.20000000000000001,0.48999999999999999,0,1,1,information,2,1,-27.454161448922427,2.1048198185639183e-11,2.8421709430404007e-14 +K4,0.20000000000000001,0.48999999999999999,0,1,1,covariance,2,1,-0.86010733445185528,2.8481035925015792e-09,2.2426505097428162e-14 +K4,0.20000000000000001,0.48999999999999999,0,1,1,information,2,2,22.898412229102597,1.8249592259459305e-11,1.0658141036401503e-14 +K4,0.20000000000000001,0.48999999999999999,0,1,1,covariance,2,2,2.4031486918435516,2.8481035925015792e-09,1.6431300764452317e-14 +K4,0.20000000000000001,0.48999999999999999,0,1,1,information,2,3,5.6308393196041102,1.1270584945520772e-11,8.1712414612411521e-14 +K4,0.20000000000000001,0.48999999999999999,0,1,1,covariance,2,3,1.0707338417388244,2.8481035925015792e-09,5.5511151231257827e-15 +K4,0.20000000000000001,0.48999999999999999,0,1,1,information,2,4,-64.70673409303761,6.5711808361914868e-11,2.8421709430404007e-14 +K4,0.20000000000000001,0.48999999999999999,0,1,1,covariance,2,4,1.2930794052083445,2.8481035925015792e-09,1.4654943925052066e-14 +K4,0.20000000000000001,0.48999999999999999,0,1,1,information,3,1,-10.543905746860107,9.6623303546081762e-12,1.2079226507921703e-13 +K4,0.20000000000000001,0.48999999999999999,0,1,1,covariance,3,1,-0.28226305820387271,2.8481035925015792e-09,9.6034291630076041e-15 +K4,0.20000000000000001,0.48999999999999999,0,1,1,information,3,2,5.6308393196041102,1.1270584945520772e-11,8.1712414612411521e-14 +K4,0.20000000000000001,0.48999999999999999,0,1,1,covariance,3,2,1.0707338417388259,2.8481035925015792e-09,3.5527136788005009e-15 +K4,0.20000000000000001,0.48999999999999999,0,1,1,information,3,3,6.9221607763030395,2.6215357914900961e-12,6.2172489379008766e-14 +K4,0.20000000000000001,0.48999999999999999,0,1,1,covariance,3,3,0.72799003388453176,2.8481035925015792e-09,7.7715611723760958e-16 +K4,0.20000000000000001,0.48999999999999999,0,1,1,information,3,4,-23.210041465195534,1.2723280895350207e-11,2.7000623958883807e-13 +K4,0.20000000000000001,0.48999999999999999,0,1,1,covariance,3,4,0.56202180319547312,2.8481035925015792e-09,4.6629367034256575e-15 +K4,0.20000000000000001,0.48999999999999999,0,1,1,information,4,1,85.097868867666833,1.2498553914762231e-10,1.1368683772161603e-13 +K4,0.20000000000000001,0.48999999999999999,0,1,1,covariance,4,1,-0.83942388972046922,2.8481035925015792e-09,1.3433698597964394e-14 +K4,0.20000000000000001,0.48999999999999999,0,1,1,information,4,2,-64.70673409303761,6.5711808361914868e-11,2.8421709430404007e-14 +K4,0.20000000000000001,0.48999999999999999,0,1,1,covariance,4,2,1.2930794052083423,2.8481035925015792e-09,1.865174681370263e-14 +K4,0.20000000000000001,0.48999999999999999,0,1,1,information,4,3,-23.210041465195534,1.2723280895350207e-11,2.7000623958883807e-13 +K4,0.20000000000000001,0.48999999999999999,0,1,1,covariance,4,3,0.56202180319547124,2.8481035925015792e-09,7.5495165674510645e-15 +K4,0.20000000000000001,0.48999999999999999,0,1,1,information,4,4,196.07843137254875,4.837389185630795e-13,2.2737367544323206e-13 +K4,0.20000000000000001,0.48999999999999999,0,1,1,covariance,4,4,0.86265826127988221,2.8481035925015792e-09,1.2323475573339238e-14 +K4,-0.48999999999999999,-1,0,1,1,score_mean,1,0,0,5.9543543259883161e-14,0 +K4,-0.48999999999999999,-1,0,1,1,score_mean,2,0,2.6367796834847468e-16,1.1765205209262839e-13,3.7470027081099033e-16 +K4,-0.48999999999999999,-1,0,1,1,score_mean,3,0,3.0704605524789486e-16,1.3747792430025079e-14,8.1532003370909933e-17 +K4,-0.48999999999999999,-1,0,1,1,score_mean,4,0,-2.0816681711721685e-16,3.6672298267353278e-15,3.4694469519536142e-16 +K4,-0.48999999999999999,-1,0,1,1,information,1,1,12.419870334201333,5.4034015463242996e-12,1.7763568394002505e-14 +K4,-0.48999999999999999,-1,0,1,1,covariance,1,1,6.4658365601111871,7.175680824531357e-08,8.2600593032111647e-14 +K4,-0.48999999999999999,-1,0,1,1,information,1,2,-24.550515876228854,1.7850564267558604e-11,3.1974423109204508e-14 +K4,-0.48999999999999999,-1,0,1,1,covariance,1,2,0.75559312027406689,7.175680824531357e-08,5.773159728050814e-14 +K4,-0.48999999999999999,-1,0,1,1,information,1,3,-48.043504229631871,3.5323403975430527e-11,7.815970093361102e-14 +K4,-0.48999999999999999,-1,0,1,1,covariance,1,3,1.1546998215767283,7.175680824531357e-08,4.75175454539567e-14 +K4,-0.48999999999999999,-1,0,1,1,information,1,4,0.75728701823666444,1.678212092273131e-14,1.1102230246251565e-16 +K4,-0.48999999999999999,-1,0,1,1,covariance,1,4,-6.9707058149187864,7.175680824531357e-08,1.9095836023552692e-13 +K4,-0.48999999999999999,-1,0,1,1,information,2,1,-24.550515876228854,1.7850564267558604e-11,3.1974423109204508e-14 +K4,-0.48999999999999999,-1,0,1,1,covariance,2,1,0.75559312027408243,7.175680824531357e-08,1.9872992140790302e-14 +K4,-0.48999999999999999,-1,0,1,1,information,2,2,49.866591244632588,4.3957965139910082e-11,7.1054273576010019e-14 +K4,-0.48999999999999999,-1,0,1,1,covariance,2,2,1.5677214451137789,7.175680824531357e-08,1.0436096431476471e-14 +K4,-0.48999999999999999,-1,0,1,1,information,2,3,97.565309896546083,7.1029353480233606e-11,1.4210854715202004e-13 +K4,-0.48999999999999999,-1,0,1,1,covariance,2,3,-0.58318246228440029,7.175680824531357e-08,3.3306690738754696e-16 +K4,-0.48999999999999999,-1,0,1,1,information,2,4,-1.2053476562653018,3.2033855317782616e-14,2.2204460492503131e-16 +K4,-0.48999999999999999,-1,0,1,1,covariance,2,4,1.433898282942075,7.175680824531357e-08,9.1038288019262836e-15 +K4,-0.48999999999999999,-1,0,1,1,information,3,1,-48.043504229631871,3.5323403975430527e-11,7.815970093361102e-14 +K4,-0.48999999999999999,-1,0,1,1,covariance,3,1,1.154699821576721,7.175680824531357e-08,2.9309887850104133e-14 +K4,-0.48999999999999999,-1,0,1,1,information,3,2,97.565309896546083,7.1029353480233606e-11,1.4210854715202004e-13 +K4,-0.48999999999999999,-1,0,1,1,covariance,3,2,-0.58318246228440362,7.175680824531357e-08,8.4376949871511897e-15 +K4,-0.48999999999999999,-1,0,1,1,information,3,3,196.49049770324064,1.6730342729324121e-10,3.1263880373444408e-13 +K4,-0.48999999999999999,-1,0,1,1,covariance,3,3,0.56004651527246463,7.175680824531357e-08,1.099120794378905e-14 +K4,-0.48999999999999999,-1,0,1,1,information,3,4,-1.439485012786331,1.3747366269439988e-13,8.8817841970012523e-16 +K4,-0.48999999999999999,-1,0,1,1,covariance,3,4,-2.3135947011955604,7.175680824531357e-08,4.929390229335695e-14 +K4,-0.48999999999999999,-1,0,1,1,information,4,1,0.75728701823666444,1.678212092273131e-14,1.1102230246251565e-16 +K4,-0.48999999999999999,-1,0,1,1,covariance,4,1,-6.9707058149187624,7.175680824531357e-08,1.3233858453531866e-13 +K4,-0.48999999999999999,-1,0,1,1,information,4,2,-1.2053476562653018,3.2033855317782616e-14,2.2204460492503131e-16 +K4,-0.48999999999999999,-1,0,1,1,covariance,4,2,1.4338982829420941,7.175680824531357e-08,5.7287508070658077e-14 +K4,-0.48999999999999999,-1,0,1,1,information,4,3,-1.439485012786331,1.3747366269439988e-13,8.8817841970012523e-16 +K4,-0.48999999999999999,-1,0,1,1,covariance,4,3,-2.3135947011955644,7.175680824531357e-08,5.9063864910058328e-14 +K4,-0.48999999999999999,-1,0,1,1,information,4,4,0.33333333333333331,3.5380106544752541e-13,5.5511151231257827e-17 +K4,-0.48999999999999999,-1,0,1,1,covariance,4,4,14.030358174657051,7.175680824531357e-08,2.5224267119483557e-13 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,score_mean,1,0,2.2204460492503131e-16,1.7443717096975477e-13,2.2204460492503131e-16 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,score_mean,2,0,-5.5511151231257827e-16,1.2413322653753735e-14,2.3592239273284576e-16 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,score_mean,3,0,-1.4779844015322396e-15,2.4259957864448059e-14,1.2108369862318114e-15 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,score_mean,4,0,4.163336342344337e-17,2.3150548358732575e-15,1.2490009027033011e-16 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,information,1,1,1.5568100984804258,1.0418582416246072e-12,3.3306690738754696e-15 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,covariance,1,1,14.221803484293781,2.6788951675358219e-06,1.0658141036401503e-13 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,information,1,2,-6.9778284951268823,4.9948476909783576e-12,1.5099033134902129e-14 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,covariance,1,2,-2.2761704154007556,2.6788951675358219e-06,7.9047879353311146e-14 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,information,1,3,-33.393558019457686,3.5110822581474804e-11,8.5265128291212022e-14 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,covariance,1,3,1.0097730989908009,2.6788951675358219e-06,1.8873791418627661e-14 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,information,1,4,0.15487526858711703,2.1935188498309513e-14,2.7755575615628914e-17 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,covariance,1,4,-21.329793515778729,2.6788951675358219e-06,3.1263880373444408e-13 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,information,2,1,-6.9778284951268823,4.9948476909783576e-12,1.5099033134902129e-14 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,covariance,2,1,-2.2761704154007862,2.6788951675358219e-06,3.9523939676655573e-14 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,information,2,2,35.095320624851439,2.8918195000067778e-11,1.0658141036401503e-13 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,covariance,2,2,2.1089921425216107,2.6788951675358219e-06,6.1284310959308641e-14 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,information,2,3,166.21793249728742,1.331070059143109e-10,4.5474735088646412e-13 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,covariance,2,3,-0.50868749902556698,2.6788951675358219e-06,1.3988810110276972e-14 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,information,2,4,-0.52873645101713773,1.9448491419011882e-13,1.1102230246251565e-16 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,covariance,2,4,8.2186462420579147,2.6788951675358219e-06,2.0605739337042905e-13 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,information,3,1,-33.393558019457686,3.5110822581474804e-11,8.5265128291212022e-14 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,covariance,3,1,1.0097730989908063,2.6788951675358219e-06,1.2212453270876722e-14 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,information,3,2,166.21793249728742,1.331070059143109e-10,4.5474735088646412e-13 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,covariance,3,2,-0.50868749902556587,2.6788951675358219e-06,1.532107773982716e-14 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,information,3,3,817.58822447928651,5.8506449124230739e-10,2.3874235921539366e-12 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,covariance,3,3,0.14198928208394115,2.6788951675358219e-06,3.5527136788005009e-15 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,information,3,4,-1.2976386809457474,3.5618694175236e-13,4.4408920985006262e-16 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,covariance,3,4,-2.4537923811456301,2.6788951675358219e-06,5.3734794391857577e-14 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,information,4,1,0.15487526858711703,2.1935188498309513e-14,2.7755575615628914e-17 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,covariance,4,1,-21.329793515778803,2.6788951675358219e-06,2.1316282072803006e-13 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,information,4,2,-0.52873645101713773,1.9448491419011882e-13,1.1102230246251565e-16 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,covariance,4,2,8.2186462420578863,2.6788951675358219e-06,2.4513724383723456e-13 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,information,4,3,-1.2976386809457474,3.5618694175236e-13,4.4408920985006262e-16 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,covariance,4,3,-2.4537923811456275,2.6788951675358219e-06,5.773159728050814e-14 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,information,4,4,0.098255956767378985,1.5309344082915243e-15,2.7755575615628914e-17 +K4,-0.20000000000000001,-2.4500000000000002,0,1,1,covariance,4,4,55.618199820784774,2.6788951675358219e-06,8.8107299234252423e-13 +GNO,0,NA,0,1,1,moment,1,0,0,4.9999999999999999e-13,NA +GNO,0,NA,0,1,1,moment,2,0,1,4.9999999999999999e-13,NA +GNO,0,NA,0,1,1,moment,3,0,0,4.9999999999999999e-13,NA +GNO,0,NA,0,1,1,moment,4,0,3,1.5000000000000001e-12,NA +GNO,-9.9999999999999995e-07,NA,0,1,1,moment,1,0,5.0000000000012502e-07,4.9999999999999999e-13,NA +GNO,-9.9999999999999995e-07,NA,0,1,1,moment,2,0,1.0000000000007501,5.0000000000037501e-13,NA +GNO,-9.9999999999999995e-07,NA,0,1,1,moment,3,0,3.00000000000175e-06,4.9999999999999999e-13,NA +GNO,-9.9999999999999995e-07,NA,0,1,1,moment,4,0,3.0000000000160001,1.5000000000080001e-12,NA +GNO,9.9999999999999995e-07,NA,0,1,1,moment,1,0,-5.0000000000012502e-07,4.9999999999999999e-13,NA +GNO,9.9999999999999995e-07,NA,0,1,1,moment,2,0,1.0000000000007501,5.0000000000037501e-13,NA +GNO,9.9999999999999995e-07,NA,0,1,1,moment,3,0,-3.00000000000175e-06,4.9999999999999999e-13,NA +GNO,9.9999999999999995e-07,NA,0,1,1,moment,4,0,3.0000000000160001,1.5000000000080001e-12,NA +GNO,-0.00010001,NA,0,1,1,moment,1,0,5.0005000125037506e-05,4.9999999999999999e-13,NA +GNO,-0.00010001,NA,0,1,1,moment,2,0,1.0000000075015001,5.0000000375075001e-13,NA +GNO,-0.00010001,NA,0,1,1,moment,3,0,0.00030003000175052503,4.9999999999999999e-13,NA +GNO,-0.00010001,NA,0,1,1,moment,4,0,3.0000001600320041,1.5000000800160019e-12,NA +GNO,0.00010001,NA,0,1,1,moment,1,0,-5.0005000125037506e-05,4.9999999999999999e-13,NA +GNO,0.00010001,NA,0,1,1,moment,2,0,1.0000000075015001,5.0000000375075001e-13,NA +GNO,0.00010001,NA,0,1,1,moment,3,0,-0.00030003000175052503,4.9999999999999999e-13,NA +GNO,0.00010001,NA,0,1,1,moment,4,0,3.0000001600320041,1.5000000800160019e-12,NA +GNO,-0.00020000000000000001,NA,0,1,1,moment,1,0,0.00010000000100000001,4.9999999999999999e-13,NA +GNO,-0.00020000000000000001,NA,0,1,1,moment,2,0,1.0000000300000005,5.0000001500000022e-13,NA +GNO,-0.00020000000000000001,NA,0,1,1,moment,3,0,0.00060000001400000033,4.9999999999999999e-13,NA +GNO,-0.00020000000000000001,NA,0,1,1,moment,4,0,3.000000640000037,1.5000003200000184e-12,NA +GNO,0.00020000000000000001,NA,0,1,1,moment,1,0,-0.00010000000100000001,4.9999999999999999e-13,NA +GNO,0.00020000000000000001,NA,0,1,1,moment,2,0,1.0000000300000005,5.0000001500000022e-13,NA +GNO,0.00020000000000000001,NA,0,1,1,moment,3,0,-0.00060000001400000033,4.9999999999999999e-13,NA +GNO,0.00020000000000000001,NA,0,1,1,moment,4,0,3.000000640000037,1.5000003200000184e-12,NA +GNO,-0.01,NA,0,1,1,moment,1,0,0.0050001250020833598,4.9999999999999999e-13,NA +GNO,-0.01,NA,0,1,1,moment,2,0,1.0000750030209193,5.000375015104597e-13,NA +GNO,-0.01,NA,0,1,1,moment,3,0,0.030001750090628672,4.9999999999999999e-13,NA +GNO,-0.01,NA,0,1,1,moment,4,0,3.0016002300236688,1.5008001150118344e-12,NA +GNO,0.01,NA,0,1,1,moment,1,0,-0.0050001250020833598,4.9999999999999999e-13,NA +GNO,0.01,NA,0,1,1,moment,2,0,1.0000750030209193,5.000375015104597e-13,NA +GNO,0.01,NA,0,1,1,moment,3,0,-0.030001750090628672,4.9999999999999999e-13,NA +GNO,0.01,NA,0,1,1,moment,4,0,3.0016002300236688,1.5008001150118344e-12,NA +GNO,-0.20000000000000001,NA,0,1,1,moment,1,0,0.10100670013377906,4.9999999999999999e-13,NA +GNO,-0.20000000000000001,NA,0,1,1,moment,2,0,1.0304888825524796,5.1524444127623981e-13,NA +GNO,-0.20000000000000001,NA,0,1,1,moment,3,0,0.61429476198666333,4.9999999999999999e-13,NA +GNO,-0.20000000000000001,NA,0,1,1,moment,4,0,3.6783657771754372,1.8391828885877186e-12,NA +GNO,0.20000000000000001,NA,0,1,1,moment,1,0,-0.10100670013377906,4.9999999999999999e-13,NA +GNO,0.20000000000000001,NA,0,1,1,moment,2,0,1.0304888825524796,5.1524444127623981e-13,NA +GNO,0.20000000000000001,NA,0,1,1,moment,3,0,-0.61429476198666333,4.9999999999999999e-13,NA +GNO,0.20000000000000001,NA,0,1,1,moment,4,0,3.6783657771754372,1.8391828885877186e-12,NA +GLO,0,-1,0,1,1,moment,1,0,0,4.9999999999999999e-13,NA +GLO,0,-1,0,1,1,moment,2,0,1.8137993642342178,9.0689968211710886e-13,NA +GLO,0,-1,0,1,1,moment,3,0,0,4.9999999999999999e-13,NA +GLO,0,-1,0,1,1,moment,4,0,4.2000000000000002,2.0999999999999999e-12,NA +GLO,-9.9999999999999995e-07,-1,0,1,1,moment,1,0,1.6449340668501204e-06,4.9999999999999999e-13,NA +GLO,-9.9999999999999995e-07,-1,0,1,1,moment,2,0,1.8137993642407817,9.0689968212039082e-13,NA +GLO,-9.9999999999999995e-07,-1,0,1,1,moment,3,0,8.7062369483845339e-06,4.9999999999999999e-13,NA +GLO,-9.9999999999999995e-07,-1,0,1,1,moment,4,0,4.2000000001867877,2.1000000000933938e-12,NA +GLO,9.9999999999999995e-07,-1,0,1,1,moment,1,0,-1.6449340668501204e-06,4.9999999999999999e-13,NA +GLO,9.9999999999999995e-07,-1,0,1,1,moment,2,0,1.8137993642407817,9.0689968212039082e-13,NA +GLO,9.9999999999999995e-07,-1,0,1,1,moment,3,0,-8.7062369483845339e-06,4.9999999999999999e-13,NA +GLO,9.9999999999999995e-07,-1,0,1,1,moment,4,0,4.2000000001867877,2.1000000000933938e-12,NA +GLO,-0.00010001,-1,0,1,1,moment,1,0,0.00016450985792012507,4.9999999999999999e-13,NA +GLO,-0.00010001,-1,0,1,1,moment,2,0,1.8137994298861164,9.0689971494305819e-13,NA +GLO,-0.00010001,-1,0,1,1,moment,3,0,0.00087071081750537309,4.9999999999999999e-13,NA +GLO,-0.00010001,-1,0,1,1,moment,4,0,4.2000018682670905,2.1000009341335454e-12,NA +GLO,0.00010001,-1,0,1,1,moment,1,0,-0.00016450985792012507,4.9999999999999999e-13,NA +GLO,0.00010001,-1,0,1,1,moment,2,0,1.8137994298861164,9.0689971494305819e-13,NA +GLO,0.00010001,-1,0,1,1,moment,3,0,-0.00087071081750537309,4.9999999999999999e-13,NA +GLO,0.00010001,-1,0,1,1,moment,4,0,4.2000018682670905,2.1000009341335454e-12,NA +GLO,-0.00020000000000000001,-1,0,1,1,moment,1,0,0.0003289868285221712,4.9999999999999999e-13,NA +GLO,-0.00020000000000000001,-1,0,1,1,moment,2,0,1.8137996267893222,9.0689981339466106e-13,NA +GLO,-0.00020000000000000001,-1,0,1,1,moment,3,0,0.0017412478719479969,4.9999999999999999e-13,NA +GLO,-0.00020000000000000001,-1,0,1,1,moment,4,0,4.200007471578358,2.1000037357891789e-12,NA +GLO,0.00020000000000000001,-1,0,1,1,moment,1,0,-0.0003289868285221712,4.9999999999999999e-13,NA +GLO,0.00020000000000000001,-1,0,1,1,moment,2,0,1.8137996267893222,9.0689981339466106e-13,NA +GLO,0.00020000000000000001,-1,0,1,1,moment,3,0,-0.0017412478719479969,4.9999999999999999e-13,NA +GLO,0.00020000000000000001,-1,0,1,1,moment,4,0,4.200007471578358,2.1000037357891789e-12,NA +GLO,-0.01,-1,0,1,1,moment,1,0,0.016451234931271402,4.9999999999999999e-13,NA +GLO,-0.01,-1,0,1,1,moment,2,0,1.8144559529175475,9.0722797645877371e-13,NA +GLO,-0.01,-1,0,1,1,moment,3,0,0.087122707401954672,4.9999999999999999e-13,NA +GLO,-0.01,-1,0,1,1,moment,4,0,4.2187154924720112,2.1093577462360056e-12,NA +GLO,0.01,-1,0,1,1,moment,1,0,-0.016451234931271402,4.9999999999999999e-13,NA +GLO,0.01,-1,0,1,1,moment,2,0,1.8144559529175475,9.0722797645877371e-13,NA +GLO,0.01,-1,0,1,1,moment,3,0,-0.087122707401954672,4.9999999999999999e-13,NA +GLO,0.01,-1,0,1,1,moment,4,0,4.2187154924720112,2.1093577462360056e-12,NA +GLO,-0.20000000000000001,-1,0,1,1,moment,1,0,0.34479666057797509,4.9999999999999999e-13,NA +GLO,-0.20000000000000001,-1,0,1,1,moment,2,0,2.1132459982254237,1.0566229991127118e-12,NA +GLO,-0.20000000000000001,-1,0,1,1,moment,3,0,2.4852755496867016,1.2426377748433508e-12,NA +GLO,-0.20000000000000001,-1,0,1,1,moment,4,0,29.556191909249165,1.4778095954624583e-11,NA +GLO,0.20000000000000001,-1,0,1,1,moment,1,0,-0.34479666057797509,4.9999999999999999e-13,NA +GLO,0.20000000000000001,-1,0,1,1,moment,2,0,2.1132459982254237,1.0566229991127118e-12,NA +GLO,0.20000000000000001,-1,0,1,1,moment,3,0,-2.4852755496867016,1.2426377748433508e-12,NA +GLO,0.20000000000000001,-1,0,1,1,moment,4,0,29.556191909249165,1.4778095954624583e-11,NA diff --git a/docs/distributions/oracles/generalized-fisher.md b/docs/distributions/oracles/generalized-fisher.md new file mode 100644 index 00000000..5574d766 --- /dev/null +++ b/docs/distributions/oracles/generalized-fisher.md @@ -0,0 +1,88 @@ +# Independent generalized-family Fisher information oracle + +Generated with R version 4.4.3 (2025-02-28 ucrt) on x86_64-w64-mingw32 . + +The generator uses base R only. It does not load Numerics or any production numerical helper. +All CSV matrices use location=0, scale=1, sample_size=1 and parameter order xi, alpha, kappa[, hondo]. +GNO denotes Hosking's transformed normal, not the symmetric exponential-power distribution. +GLO fixes Kappa Four hondo=-1; GEV fixes hondo=0. A fixed-shape family inverts its own information block. + +## Method and acceptance checks + +R integrate (QUADPACK): relative tolerance 2e-12; absolute tolerance 2e-13; at most 1500 subdivisions. +GNO uses direct integration over the entire normal latent line, with an independent probability-coordinate cross-check. +GLO, GEV and K4 pair p=u^a/2 and 1-p=u^a/2 over the complete u interval [0,1]. Powers 16/24 are cross-checked; boundary-approach cases use 128/192. +Log probabilities and log survival probabilities are retained separately. Below exp(-36), log(t)=log(survival) is used to binary64 relative precision; no probability tail is discarded. +Analytical scores are checked against five-point fixed-observation derivatives of an independently expressed log density at probabilities .1, .5 and .9, using two step sizes. +Finite matrices, near-zero mean scores, positive eigenvalues, inverse residuals and agreement between integration coordinates are required before writing the fixture. +The covariance error column is a conservative matrix-norm perturbation estimate from the numerical integration error estimates; it is not a rigorous interval bound. +The alternate-coordinate difference is independent numerical corroboration, not a rigorous bound. Values are binary64 R references, not arbitrary-precision goldens. +The exact GNO kappa=0 covariance is [[7/6,0,1/3],[0,1/2,0],[1/3,0,2/3]] with all three parameters estimated. + +The additional moment rows use row=1 mean, row=2 standard deviation, row=3 skewness, row=4 non-excess kurtosis. GNO uses analytical lognormal moments; GLO uses trigonometric moments, with factored coefficient-array series for abs(kappa)<=.01. +Moment absolute_error_estimate is an arithmetic comparison allowance of 5e-13*max(1,abs(value)), not a quadrature error estimate. The GLO near-zero series is cross-checked against independent 70-digit Decimal values at kappa=.00010001 and .0002. + +## Mathematical domain + +GNO information is finite for every finite kappa. GLO uses abs(kappa)<1/2. K4 uses kappa<1/2, hondo<1/2 and kappa*hondo<1/2. GEV uses kappa<1/2. +These are local asymptotic-information conditions, not guarantees of global-MLE existence, estimator convergence, or finite-sample coverage. +MLE covariance is not L-moment or product-moment covariance. The fixture does not authorize substitution between estimators. + +## Primary-source mapping and references + +- [Hosking lmom R defining quantiles](https://raw.githubusercontent.com/cran/lmom/master/R/lmom.r): quagno(p,c(xi,alpha,k)), quaglo(p,c(xi,alpha,k)), quakap(p,c(xi,alpha,k,h)), quagev(p,c(xi,alpha,k)). +- [Park and Kim (2007), Fisher information matrix for a four-parameter kappa distribution](https://doi.org/10.1016/j.spl.2007.03.002). The present scores/domain are independently derived; inaccessible full-paper formulae were not treated as verified values. +- [Wang and Flournoy (2015), local likelihood estimation for the three-parameter lognormal](https://doi.org/10.1016/j.spl.2015.05.021): finite Fisher information does not imply bounded global likelihood. + +## Numerical summary + +| Family | kappa | hondo | Minimum eigenvalue | Condition number | Max mean score | Information error norm | Coordinate delta norm | Covariance error estimate | Fixed-x derivative relative error | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | +| GNO | 0 | NA | 0.75 | 2.66667 | 2.88e-17 | 2.05e-12 | 1.48e-15 | 3.65e-12 | 2.84e-12 | +| GNO | -1e-06 | NA | 0.75 | 2.66667 | 9.17e-17 | 2.05e-12 | 1.3e-15 | 3.65e-12 | 1.3e-11 | +| GNO | 1e-06 | NA | 0.75 | 2.66667 | 9.17e-17 | 2.05e-12 | 1.38e-15 | 3.65e-12 | 3.87e-12 | +| GNO | -0.2 | NA | 0.742867 | 3.98025 | 1.34e-16 | 3.56e-12 | 1.09e-15 | 6.45e-12 | 4.91e-12 | +| GNO | 0.2 | NA | 0.742867 | 3.98025 | 1.34e-16 | 3.56e-12 | 1.09e-15 | 6.45e-12 | 3.5e-12 | +| GNO | -1 | NA | 0.422625 | 79.9633 | 5.42e-17 | 4.83e-11 | 2.93e-14 | 2.71e-10 | 5.08e-12 | +| GNO | 1 | NA | 0.422625 | 79.9633 | 5.42e-17 | 4.83e-11 | 2.93e-14 | 2.71e-10 | 2.47e-12 | +| GNO | -2 | NA | 0.177334 | 110247 | 1.11e-16 | 1.54e-08 | 3.14e-11 | 4.89e-07 | 3.47e-12 | +| GNO | 2 | NA | 0.177334 | 110247 | 1.11e-16 | 1.54e-08 | 3.14e-11 | 4.89e-07 | 3.5e-12 | +| GLO | 0 | -1 | 0.320008 | 11.8793 | 1.11e-16 | 5.58e-14 | 9.32e-16 | 5.45e-13 | 7.35e-12 | +| GLO | -1e-06 | -1 | 0.320008 | 11.8793 | 2.01e-16 | 5.58e-14 | 1.13e-15 | 5.45e-13 | 8.1e-12 | +| GLO | 1e-06 | -1 | 0.320008 | 11.8793 | 2.78e-17 | 5.58e-14 | 9.35e-16 | 5.45e-13 | 6.36e-12 | +| GLO | -0.2 | -1 | 0.309535 | 18.5564 | 8.33e-17 | 6.61e-13 | 2.37e-15 | 6.9e-12 | 7.92e-12 | +| GLO | 0.2 | -1 | 0.309535 | 18.5564 | 4.37e-16 | 6.61e-13 | 2.49e-15 | 6.9e-12 | 7.31e-12 | +| GLO | -0.4 | -1 | 0.276988 | 78.6219 | 6.61e-16 | 3.37e-11 | 4.96e-14 | 4.39e-10 | 7.97e-12 | +| GLO | 0.4 | -1 | 0.276988 | 78.6219 | 7.98e-17 | 3.37e-11 | 4.84e-14 | 4.39e-10 | 5.35e-12 | +| GLO | -0.45 | -1 | 0.266216 | 178.618 | 9.71e-17 | 9.57e-12 | 1.67e-14 | 1.35e-10 | 5.44e-12 | +| GLO | 0.45 | -1 | 0.266216 | 178.618 | 4.65e-16 | 9.58e-12 | 1.52e-14 | 1.35e-10 | 5.37e-12 | +| GLO | -0.49 | -1 | 0.257239 | 999.511 | 3.07e-16 | 2.07e-10 | 3.97e-13 | 3.13e-09 | 4.25e-12 | +| GLO | 0.49 | -1 | 0.257239 | 999.511 | 1.39e-16 | 2.07e-10 | 3.04e-13 | 3.13e-09 | 6.35e-12 | +| GEV | 0 | 0 | 0.672476 | 3.87267 | 1.11e-16 | 1.1e-12 | 4.78e-16 | 2.43e-12 | 7.93e-12 | +| GEV | -1e-06 | 0 | 0.672476 | 3.87266 | 1.67e-16 | 1.16e-12 | 1.01e-15 | 2.56e-12 | 5.94e-12 | +| GEV | 1e-06 | 0 | 0.672477 | 3.87268 | 7.29e-17 | 1.16e-12 | 5.04e-16 | 2.56e-12 | 5.56e-12 | +| GEV | -0.2 | 0 | 0.58145 | 4.69117 | 1.11e-16 | 7.71e-13 | 2.01e-15 | 2.28e-12 | 6.77e-12 | +| GEV | 0.2 | 0 | 0.779251 | 6.89922 | 6.38e-16 | 3.32e-12 | 2.63e-16 | 5.47e-12 | 6.48e-12 | +| GEV | -1 | 0 | 0.292005 | 48.1418 | 3.47e-16 | 1.46e-12 | 2.59e-15 | 1.71e-11 | 1.24e-11 | +| GEV | 0.45 | 0 | 0.822612 | 57.7779 | 1.39e-16 | 9.02e-12 | 2.49e-14 | 1.33e-11 | 4.14e-12 | +| GEV | 0.49 | 0 | 0.804731 | 319.524 | 2.22e-16 | 1.83e-10 | 2.81e-13 | 2.82e-10 | 5.09e-12 | +| K4 | 0 | 0 | 0.0696442 | 56.6234 | 1.25e-16 | 1.1e-12 | 7.19e-16 | 2.27e-10 | 7.93e-12 | +| K4 | 1e-06 | 0 | 0.0696443 | 56.6233 | 1.25e-16 | 1.16e-12 | 5.51e-15 | 2.39e-10 | 5.56e-12 | +| K4 | -1e-06 | 0 | 0.069644 | 56.6236 | 1.67e-16 | 1.16e-12 | 1.02e-15 | 2.39e-10 | 5.94e-12 | +| K4 | 0 | 1e-06 | 0.0696443 | 56.6234 | 3.05e-16 | 1.1e-12 | 1.22e-15 | 2.27e-10 | 4.28e-12 | +| K4 | 0 | -1e-06 | 0.069644 | 56.6235 | 3.33e-16 | 1.1e-12 | 2.3e-15 | 2.27e-10 | 8.2e-12 | +| K4 | 0 | -1 | 0.00507523 | 770.598 | 1.11e-16 | 1.45e-13 | 9.33e-16 | 5.63e-09 | 7.35e-12 | +| K4 | 0.2 | -1 | 0.0155506 | 370.485 | 4.37e-16 | 6.7e-13 | 2.58e-15 | 2.77e-09 | 8.15e-12 | +| K4 | -0.2 | -1 | 0.00824959 | 719.157 | 8.33e-17 | 6.78e-13 | 2.46e-15 | 9.96e-09 | 8.89e-12 | +| K4 | 0.2 | 0.2 | 0.139053 | 45.1301 | 2.91e-16 | 2.75e-12 | 4.88e-15 | 1.42e-10 | 7.47e-12 | +| K4 | -0.2 | -0.2 | 0.0212752 | 171.195 | 1.53e-16 | 3.11e-13 | 2.05e-15 | 6.88e-10 | 1.07e-11 | +| K4 | 0.4 | -1 | 0.0328533 | 662.914 | 7.98e-17 | 3.37e-11 | 4.84e-14 | 3.12e-08 | 5.35e-12 | +| K4 | -0.4 | -1 | 0.0336709 | 650.134 | 6.61e-16 | 3.37e-11 | 4.96e-14 | 2.97e-08 | 8.89e-12 | +| K4 | -1 | -0.2 | 0.0174094 | 1205.62 | 7.22e-16 | 3.92e-12 | 4.99e-15 | 1.29e-08 | 6.07e-12 | +| K4 | -0.2 | -2 | 0.0160321 | 3193.35 | 1.85e-15 | 8.67e-11 | 7.51e-14 | 3.38e-07 | 1.11e-11 | +| K4 | 0.49 | 0.2 | 0.18099 | 1420.86 | 1.67e-16 | 1.91e-10 | 3.15e-13 | 5.83e-09 | 4.44e-12 | +| K4 | 0.2 | 0.49 | 0.242565 | 1060.47 | 8.05e-16 | 1.68e-10 | 5e-13 | 2.85e-09 | 4.31e-12 | +| K4 | -0.49 | -1 | 0.0537668 | 4782.28 | 3.07e-16 | 2.07e-10 | 3.97e-13 | 7.18e-08 | 4.25e-12 | +| K4 | -0.2 | -2.45 | 0.01518 | 56179.1 | 1.48e-15 | 6.17e-10 | 2.48e-12 | 2.68e-06 | 1.38e-11 | + +1333 CSV rows from 46 parameter cases. diff --git a/docs/distributions/oracles/generalized-scalar-variance.py b/docs/distributions/oracles/generalized-scalar-variance.py new file mode 100644 index 00000000..65e17e6c --- /dev/null +++ b/docs/distributions/oracles/generalized-scalar-variance.py @@ -0,0 +1,81 @@ +"""Independent scalar references for generalized-family range regressions. + +Run with Python 3.11+ from any directory. Uses only decimal and the previously +frozen R covariance fixture. It never imports Numerics or regenerates that +fixture. Binary64 inputs are converted exactly before 450-digit arithmetic. +""" + +import csv +from decimal import Decimal as D, localcontext +from pathlib import Path + + +def covariance(rows, family, kappa, hondo): + """Read the original R covariance without symmetrizing its last-bit output.""" + size = 4 if family == "K4" else 3 + result = [[D(0)] * size for _ in range(size)] + for row in rows: + if (row["family"] == family and row["quantity"] == "covariance" + and float(row["kappa"]) == kappa + and (row["hondo"] == "NA" or float(row["hondo"]) == hondo)): + result[int(row["row"]) - 1][int(row["column"]) - 1] = D(row["value"]) + return result + + +def gno_covariance(k): + """Exact closed-form GNO information inverse, evaluated directly.""" + v = k * k + r = ((1 + v) * v.exp() - 1 - 2 * v) / v**2 + c = (1 - (-v / 2).exp()) / v + return [[1 + c * c / r, -k, c / r], + [-k, v + D(".5"), k / 2], [c / r, k / 2, v / 2 + 1 / r]] + + +def transformed_gradient(z, k): + e = (-k * z).exp() + return [D(1), (1 - e) / k, (e - 1 + k * z * e) / k**2] + + +def kappa_gradient(p, k, h): + logp = p.ln() + if h == 0: + t, dt = -logp, -logp * logp / 2 + else: + e = (h * logp).exp() + t, dt = (1 - e) / h, (-h * e * logp - 1 + e) / h**2 + logt = t.ln() + if k == 0: + return [D(1), -logt, -logt * logt / 2, -dt / t] + e = (k * logt).exp() + return [D(1), (1 - e) / k, (e - 1 - k * logt * e) / k**2, -e * dt / t] + + +def quadratic(matrix, gradient, alpha=1.0, sample_size=100): + return (sum(gradient[i] * matrix[i][j] * gradient[j] + for i in range(len(gradient)) for j in range(len(gradient))) + * D.from_float(alpha)**2 / sample_size) + + +if __name__ == "__main__": + with localcontext() as context: + context.prec = 450 + with Path(__file__).with_name("generalized-fisher.csv").open(newline="") as stream: + rows = list(csv.DictReader(stream)) + print("GLO_zero_median", float(covariance(rows, "GLO", 0, -1)[0][0] / 100)) + print("K4_zero_median", float(quadratic(covariance(rows, "K4", 0, 0), + kappa_gradient(D(".5"), D(0), D(0))))) + print("GNO_covariance_overflow", float(quadratic(gno_covariance(D(2)), + [D(1), D(0), D(0)], 1e155))) + p, k = D.from_float(1e-300), D.from_float(.2) + print("GLO_covariance_underflow", float(quadratic(covariance(rows, "GLO", .2, -1), + transformed_gradient(p.ln() - (1 - p).ln(), k), 1e-170))) + print("K4_covariance_underflow", float(quadratic(covariance(rows, "K4", -1, -.2), + kappa_gradient(D.from_float(1 - 1e-16), D(-1), D.from_float(-.2)), 1e-170))) + # Independently frozen R qnorm values for the exact binary64 probabilities. + for name, k, alpha, z in [ + ("GNO_unit_gradient_overflow", 20.0, 1e-200, -37.047096299361199), + ("GNO_endpoint_cancellation", 30.0, 1e200, 8.209536151601387), + ]: + shape = D.from_float(k) + print(name, float(quadratic(gno_covariance(shape), + transformed_gradient(D.from_float(z), shape), alpha))) diff --git a/docs/distributions/oracles/generate-gamma-temme-coefficients.py b/docs/distributions/oracles/generate-gamma-temme-coefficients.py new file mode 100644 index 00000000..cdbcdb61 --- /dev/null +++ b/docs/distributions/oracles/generate-gamma-temme-coefficients.py @@ -0,0 +1,37 @@ +"""Exact rational coefficients for DLMF 8.12.9-11, generated with Python 3.12. + +Lagrange inversion of eta^2/2 = t-log(1+t) gives t(eta). The identity +t'(eta)=1+eta+eta*c0(eta) then gives c0 without subtracting singular terms. +These are defining mathematical coefficients, not fitted probability data. +""" +from fractions import Fraction as F + +def multiply(a, b, degree): + c = [F(0)] * (degree + 1) + for i, x in enumerate(a): + for j, y in enumerate(b[:degree + 1 - i]): + c[i+j] += x*y + return c + +def inverse_coefficient(n): + degree = n-1 + # (eta/t)^2 = 2*(t-log(1+t))/t^2. + u = [F(0)] + [F(2*(-1)**j, j+2) for j in range(1, degree+1)] + power = [F(1)] + [F(0)]*degree + factor = F(1) + answer = F(0) + for m in range(degree+1): + answer += factor*power[degree] + power = multiply(power, u, degree) + factor *= (F(-n, 2)-m)/(m+1) + return answer/n + +c0 = [(n+2)*inverse_coefficient(n+2) for n in range(24)] +c0[0] -= 1 +rows = [c0] +for k, g in enumerate([F(1,12), F(1,288), F(-139,51840)], 1): + previous = rows[-1] + rows.append([(n+2)*previous[n+2]+(-1)**k*g*c0[n] + for n in range(len(previous)-2)]) +for row in rows: + print("new double[] { " + ", ".join(format(float(v), ".17g") for v in row) + " },") diff --git a/docs/distributions/oracles/manifest.json b/docs/distributions/oracles/manifest.json new file mode 100644 index 00000000..6ffb3712 --- /dev/null +++ b/docs/distributions/oracles/manifest.json @@ -0,0 +1,25 @@ +{ + "hashConvention": "SHA-256 of UTF-8 text without BOM, CRLF normalized to LF", + "files": [ + { + "file": "extreme-positive.csv", + "rows": 2497, + "sha256Utf8Lf": "35aa23bcebb14d300be780b8b1f9002c487d2ffd21224766ad88766016f5603e" + }, + { + "file": "generalized-adjacent-boundaries.csv", + "rows": 180, + "sha256Utf8Lf": "121e9c7137a8a22f18d238b3a4814a73746e3ffab08adf3ec1bea92e01d81e46" + }, + { + "file": "generalized-fisher.csv", + "rows": 1333, + "sha256Utf8Lf": "97d9f2dc8dc4d0dc363aee01e1cce2917546a4fabfea5ec08da3913240c32164" + }, + { + "file": "normal-pearson.csv", + "rows": 50, + "sha256Utf8Lf": "3fa790f12e66d4dc0791b462e16e7042c45ec4f2c783051ad8b67e41caafd548" + } + ] +} diff --git a/docs/distributions/oracles/normal-pearson-evidence.txt b/docs/distributions/oracles/normal-pearson-evidence.txt new file mode 100644 index 00000000..71cd0271 --- /dev/null +++ b/docs/distributions/oracles/normal-pearson-evidence.txt @@ -0,0 +1,25 @@ +R version 4.4.3 (2025-02-28 ucrt) +Normal logCDF(-40): -804.6084420137538 +Normal logPDF(0), sigma=1e308: -710.11514717537079 +Logistic logPDF(-1000): -1000 +LogNormal moment sigma, mean10 sd2 base10: 0.086008634833056805 +PIII(0,1,-2), quantile 1e-20: -45.051701859880914 +LnNormal indirect-MoM median variance(1,1), n100: 0.0034657359027997266 +LnNormal(10,2) exact physical mean derivative at median = 135/(26*sqrt(26)): 1.018295317063648 +LnNormal direct physical-moment estimator is a different estimator; its median variance would be 0.00875. +LP3(0,.8,1), base e, finite mean and SD: mean sd third fourth +1.5578435030451807 4.8009895965830660 Inf Inf +Legacy LP3 default and (1,1,1) corrected modes: + default custom +265.6768581650194392 0.4980296964878923 +Asymptotic MLE covariance uses independent inversion of location/scale/shape Fisher information, transformed to public coordinates. +Quantile gradients use five-point finite differences of R stats qgamma/qlnorm, with public parameter perturbations. +Independent review overflow regressions (defining lognormal identities): +LP3 zero-skew Sigma16 skew: 5.8759900382892355e+166 +LP3 zero-skew Sigma12 kurtosis: 1.4243659274306933e+250 +LogNormal Mu370 Sigma1e-10 base e median variance n100: 2.3873528283845398e+299 +Nonzero LP3 large-moment checks from its gamma moment generating function, log normalized before exponentiation: +Sigma 12 Gamma -0.001 Skewness 9.1837430055580643e+91 Kurtosis 1.8770290525950149e+244 +Sigma 16 Gamma -0.001 Skewness 2.6371094491313571e+162 Kurtosis Inf +Sigma 12 Gamma 0.001 Skewness 5.204487503190545e+95 Kurtosis 1.9321207191996086e+256 +Sigma 16 Gamma 0.001 Skewness 2.0889174692524114e+171 Kurtosis Inf diff --git a/docs/distributions/oracles/normal-pearson.R b/docs/distributions/oracles/normal-pearson.R new file mode 100644 index 00000000..8ab07655 --- /dev/null +++ b/docs/distributions/oracles/normal-pearson.R @@ -0,0 +1,100 @@ +# Independent R stats oracle for the approved Normal/Pearson family repairs. +# Run with R 4.4.3. No Numerics assembly is loaded; CSV is frozen for runtime-independent tests. +options(digits = 17) +arguments <- commandArgs(trailingOnly = TRUE) +directory <- if (length(arguments)) arguments[[1]] else "." +qpearson <- function(p, theta) { + mu <- theta[1]; sigma <- theta[2]; skew <- theta[3] + if (skew == 0) return(qnorm(p, mu, sigma)) + shape <- 4/skew^2 + mu + sigma * skew/2 * (qgamma(p, shape, lower.tail = skew > 0) - shape) +} +qfamily <- function(p, family, theta, base) { + if (family == "PearsonTypeIII") return(qpearson(p, theta)) + if (family == "LogPearsonTypeIII") return(base^qpearson(p, theta)) + if (family == "LogNormal") return(qlnorm(p, theta[1] * log(base), theta[2] * log(base))) + variance <- log1p((theta[2]/theta[1])^2) + qlnorm(p, log(theta[1]) - variance/2, sqrt(variance)) +} +gradient <- function(p, family, theta, base) { + vapply(seq_along(theta), function(i) { + step <- 1e-4 * max(1, abs(theta[i])) + at <- function(offset) { candidate <- theta; candidate[i] <- candidate[i] + step * offset; qfamily(p, family, candidate, base) } + (at(-2) - 8*at(-1) + 8*at(1) - at(2))/(12*step) + }, 0.0) +} +covariance <- function(family, theta, base, n = 100) { + if (family == "LogNormal") return(diag(c(theta[2]^2, theta[2]^2/2))/n) + if (family == "LnNormal") { + variance <- log1p((theta[2]/theta[1])^2); s <- sqrt(variance) + J <- matrix(c(theta[1], theta[1]*s, theta[2], theta[2]*s + theta[1]^2*exp(variance)*s/theta[2]), 2, byrow = TRUE) + return(J %*% diag(c(variance, variance/2)) %*% t(J)/n) + } + sigma <- theta[2]; skew <- theta[3] + if (skew == 0) return(diag(c(sigma^2, sigma^2/2, 6))/n) + shape <- 4/skew^2; beta <- sigma*skew/2 + information <- matrix(c(1/(beta^2*(shape-2)), 1/beta^2, 1/(beta*(shape-1)), + 1/beta^2, shape/beta^2, 1/beta, + 1/(beta*(shape-1)), 1/beta, trigamma(shape)), 3, byrow = TRUE) + J <- matrix(c(1, shape, beta, 0, sign(beta)*sqrt(shape), abs(beta)/(2*sqrt(shape)), 0, 0, -sign(beta)/shape^1.5), 3, byrow = TRUE) + J %*% solve(information) %*% t(J)/n +} +rows <- list() +add <- function(family, theta, base, p) { + grad <- gradient(p, family, theta, base) + cov <- covariance(family, theta, base) + count <- length(theta) + rows[[length(rows)+1]] <<- data.frame(family=family, mu=theta[1], sigma=theta[2], gamma=if(count==3) theta[3] else 0, + base=base, probability=p, quantile=qfamily(p,family,theta,base), gradient_mu=grad[1], gradient_sigma=grad[2], + gradient_gamma=if(count==3) grad[3] else 0, cov11=cov[1,1], cov12=cov[1,2], cov13=if(count==3) cov[1,3] else 0, + cov22=cov[2,2], cov23=if(count==3) cov[2,3] else 0, cov33=if(count==3) cov[3,3] else 0, + variance_mle=as.numeric(t(grad)%*%cov%*%grad)) +} +for (skew in c(-1.2,-0.2,0,0.2,1.2)) for (p in c(0.01,0.83,0.99)) { + add("PearsonTypeIII", c(2,3,skew), exp(1), p) + add("LogPearsonTypeIII", c(0.3,0.2,skew), exp(1), p) +} +for (p in c(0.01,0.5,0.83,0.99)) { + add("LnNormal", c(10,2), exp(1), p) + add("LnNormal", c(1,1), exp(1), p) + for (base in c(2,exp(1),10)) add("LogNormal", c(0.3,0.2), base, p) +} +write.csv(do.call(rbind,rows), file.path(directory,"normal-pearson.csv"), row.names=FALSE, quote=FALSE) +sink(file.path(directory,"normal-pearson-evidence.txt")) +cat(R.version.string, "\n") +cat("Normal logCDF(-40):", pnorm(-40,log.p=TRUE), "\n") +cat("Normal logPDF(0), sigma=1e308:", dnorm(0,sd=1e308,log=TRUE), "\n") +cat("Logistic logPDF(-1000):", dlogis(-1000,log=TRUE), "\n") +cat("LogNormal moment sigma, mean10 sd2 base10:", sqrt(log1p(0.2^2))/log(10), "\n") +cat("PIII(0,1,-2), quantile 1e-20:", qpearson(1e-20,c(0,1,-2)), "\n") +cat("LnNormal indirect-MoM median variance(1,1), n100:", log(2)/200, "\n") +cat("LnNormal(10,2) exact physical mean derivative at median = 135/(26*sqrt(26)):",135/(26*sqrt(26)),"\n") +cat("LnNormal direct physical-moment estimator is a different estimator; its median variance would be 0.00875.\n") +cat("LP3(0,.8,1), base e, finite mean and SD:") +raw <- function(r) if(1-0.4*r <= 0) Inf else exp(-1.6*r)*(1-0.4*r)^-4 +print(c(mean=raw(1),sd=sqrt(raw(2)-raw(1)^2),third=raw(3),fourth=raw(4))) +cat("Legacy LP3 default and (1,1,1) corrected modes:\n") +lp3mode <- function(mu,sigma,skew,base=10) { + t <- sigma*log(base); beta <- sigma*skew/2*log(base) + if(skew==0) return(exp(mu*log(base)-t*t)) + exp(mu*log(base)-(t*t+beta)/(1+beta)) +} +print(c(default=lp3mode(3,.5,0), custom=lp3mode(1,1,1))) +cat("Asymptotic MLE covariance uses independent inversion of location/scale/shape Fisher information, transformed to public coordinates.\n") +cat("Quantile gradients use five-point finite differences of R stats qgamma/qlnorm, with public parameter perturbations.\n") +cat("Independent review overflow regressions (defining lognormal identities):\n") +cat("LP3 zero-skew Sigma16 skew:",(exp(256)+2)*sqrt(expm1(256)),"\n") +cat("LP3 zero-skew Sigma12 kurtosis:",3+expm1(576)+2*expm1(432)+3*expm1(288),"\n") +cat("LogNormal Mu370 Sigma1e-10 base e median variance n100:",exp(740+log(1e-22)),"\n") +cat("Nonzero LP3 large-moment checks from its gamma moment generating function, log normalized before exponentiation:\n") +for (skew in c(-0.001,0.001)) for (s in c(12,16)) { + shape <- 4/skew^2; beta <- s*skew/2 + d <- function(r) shape*(r*log1p(-beta)-log1p(-r*beta)) + logvar <- log(expm1(d(2))) + third <- exp(d(3)-1.5*logvar)*(1-3*exp(d(2)-d(3))+2*exp(-d(3))) + fourth <- exp(d(4)-2*logvar)*(1-4*exp(d(3)-d(4))+6*exp(d(2)-d(4))-3*exp(-d(4))) + cat("Sigma",s,"Gamma",skew,"Skewness",third,"Kurtosis",fourth,"\n") +} +sink() +evidence_path <- file.path(directory,"normal-pearson-evidence.txt") +writeLines(sub("[[:blank:]]+$", "", readLines(evidence_path)), evidence_path) diff --git a/docs/distributions/oracles/normal-pearson.csv b/docs/distributions/oracles/normal-pearson.csv new file mode 100644 index 00000000..9ab42357 --- /dev/null +++ b/docs/distributions/oracles/normal-pearson.csv @@ -0,0 +1,51 @@ +family,mu,sigma,gamma,base,probability,quantile,gradient_mu,gradient_sigma,gradient_gamma,cov11,cov12,cov13,cov22,cov23,cov33,variance_mle +PearsonTypeIII,2,3,-1.2,2.71828182845905,0.01,-7.44830964561176,0.999999999998039,-3.14943654854331,1.86654556621788,0.0899999999999999,-0.0539999999999999,0,0.0778417123158923,-0.0233541065357456,0.0166073646476413,1.53468452749583 +LogPearsonTypeIII,0.3,0.2,-1.2,2.71828182845905,0.01,0.719004753722782,0.719004753722737,-2.26445984994559,0.0894703423433439,4e-04,-0.00024,0,0.000345963165848411,-0.00155694043571638,0.0166073646476414,0.00352614461746499 +PearsonTypeIII,2,3,-1.2,2.71828182845905,0.83,4.7345915816387,0.999999999997299,0.911530527213995,0.264345413522913,0.0899999999999999,-0.0539999999999999,0,0.0778417123158923,-0.0233541065357456,0.0166073646476413,0.0461381774859358 +LogPearsonTypeIII,0.3,0.2,-1.2,2.71828182845905,0.83,1.61980554071328,1.61980554071184,1.47650219850877,0.0285458776994349,4e-04,-0.00024,0,0.000345963165848411,-0.00155694043571638,0.0166073646476414,0.000538026513192351 +PearsonTypeIII,2,3,-1.2,2.71828182845905,0.99,6.3482454995161,0.999999999998039,1.44941516650787,2.03260889520767,0.0899999999999999,-0.0539999999999999,0,0.0778417123158923,-0.0233541065357456,0.0166073646476413,0.0279998832609029 +LogPearsonTypeIII,0.3,0.2,-1.2,2.71828182845905,0.99,1.8037774211679,1.80377742116803,2.61442235123939,0.244424935416345,4e-04,-0.00024,0,0.000345963165848411,-0.00155694043571638,0.0166073646476414,0.00040489237226354 +PearsonTypeIII,2,3,-0.2,2.71828182845905,0.01,-5.41676844721624,0.999999999995819,-2.47225614907334,2.16946875306453,0.089999999999156,-0.00899999999997963,2.3283064365387e-15,0.0463365272712935,-0.00882107999052285,0.0582191279373897,0.786349056787591 +LogPearsonTypeIII,0.3,0.2,-0.2,2.71828182845905,0.01,0.823286329104492,0.823286329104666,-2.03537468957515,0.119072931053787,0.000399999999997934,-3.99999999999389e-05,1.45519152283669e-16,0.00020594012120564,-0.000588071999359418,0.0582191279365284,0.00236883417446154 +PearsonTypeIII,2,3,-0.2,2.71828182845905,0.83,4.86658266452312,0.99999999999915,0.955527554842048,0.00406242757803691,0.089999999999156,-0.00899999999997963,2.3283064365387e-15,0.0463365272712935,-0.00882107999052285,0.0582191279373897,0.115039756407425 +LogPearsonTypeIII,0.3,0.2,-0.2,2.71828182845905,0.83,1.6341217612791,1.63412176127827,1.56144837086684,0.00044256675342987,0.000399999999997934,-3.99999999999389e-05,1.45519152283669e-16,0.00020594012120564,-0.000588071999359418,0.0582191279365284,0.00136531940315103 +PearsonTypeIII,2,3,-0.2,2.71828182845905,0.99,8.53520508386125,1.00000000000359,2.17840169462185,2.23016718514361,0.089999999999156,-0.00899999999997963,2.3283064365387e-15,0.0463365272712935,-0.00882107999052285,0.0582191279373897,0.47452787142232 +LogPearsonTypeIII,0.3,0.2,-0.2,2.71828182845905,0.99,2.08690130983367,2.08690130983051,4.54610934984815,0.310275921323599,0.000399999999997934,-3.99999999999389e-05,1.45519152283669e-16,0.00020594012120564,-0.000588071999359418,0.0582191279365284,0.00918508185533607 +PearsonTypeIII,2,3,0,2.71828182845905,0.01,-4.97904362212252,1.00000000000211,-2.32634787404231,2.20594722404395,0.09,0,0,0.045,0,0.06,0.625507438714167 +LogPearsonTypeIII,0.3,0.2,0,2.71828182845905,0.01,0.847665164110984,0.847665164111037,-1.97196405242841,0.124660307712632,4e-04,0,0,2e-04,0,0.06,0.00199755447613341 +PearsonTypeIII,2,3,0,2.71828182845905,0.83,4.86249575943858,0.999999999996559,0.954165253148373,-0.0447843000313715,0.09,0,0,0.045,0,0.06,0.131089747875345 +LogPearsonTypeIII,0.3,0.2,0,2.71828182845905,0.83,1.63367658855882,1.63367658855921,1.55879743568084,-0.00487753749870793,4e-04,0,0,2e-04,0,0.06,0.0015549569898227 +PearsonTypeIII,2,3,0,2.71828182845905,0.99,8.97904362212252,1.00000000000137,2.32634787404231,2.20594722404766,0.09,0,0,0.045,0,0.06,0.625507438715014 +LogPearsonTypeIII,0.3,0.2,0,2.71828182845905,0.99,2.14957376749287,2.14957376749296,5.00065636410104,0.316123085685183,4e-04,0,0,2e-04,0,0.06,0.0128456080853097 +PearsonTypeIII,2,3,0.2,2.71828182845905,0.01,-4.53520508386125,1.00000000000285,-2.17840169462185,2.23016718516655,0.089999999999156,0.00899999999997963,2.3283064365387e-15,0.0463365272712935,0.00882107999052285,0.0582191279373897,0.474527871427293 +LogPearsonTypeIII,0.3,0.2,0.2,2.71828182845905,0.01,0.873121690903407,0.873121690903311,-1.90200977107369,0.129813822913975,0.000399999999997934,3.99999999999389e-05,1.45519152283669e-16,0.00020594012120564,0.000588071999359418,0.0582191279365284,0.00160778792526281 +PearsonTypeIII,2,3,0.2,2.71828182845905,0.83,4.84872195031142,0.99999999999989,0.949573983438522,-0.0928345241206034,0.089999999999156,0.00899999999997963,2.3283064365387e-15,0.0463365272712935,0.00882107999052285,0.0582191279373897,0.147820082943837 +LogPearsonTypeIII,0.3,0.2,0.2,2.71828182845905,0.83,1.63217714713109,1.63217714713189,1.5498729552745,-0.0101014925820338,0.000399999999997934,3.99999999999389e-05,1.45519152283669e-16,0.00020594012120564,0.000588071999359418,0.0582191279365284,0.0017501912534363 +PearsonTypeIII,2,3,0.2,2.71828182845905,0.99,9.41676844721623,0.999999999994709,2.47225614907087,2.16946875307341,0.089999999999156,0.00899999999997963,2.3283064365387e-15,0.0463365272712935,0.00882107999052285,0.0582191279373897,0.786349056789268 +LogPearsonTypeIII,0.3,0.2,0.2,2.71828182845905,0.99,2.21322611098434,2.21322611098492,5.47166186216715,0.32010165941565,0.000399999999997934,3.99999999999389e-05,1.45519152283669e-16,0.00020594012120564,0.000588071999359418,0.0582191279365284,0.0171192377174164 +PearsonTypeIII,2,3,1.2,2.71828182845905,0.01,-2.3482454995161,0.99999999999952,-1.44941516650787,2.0326088952089,0.0899999999999999,0.0539999999999999,0,0.0778417123158923,0.0233541065357456,0.0166073646476413,0.0279998832609374 +LogPearsonTypeIII,0.3,0.2,1.2,2.71828182845905,0.01,1.01016831622758,1.01016831622724,-1.46415327826221,0.13688514034848,4e-04,0.00024,0,0.000345963165848411,0.00155694043571638,0.0166073646476414,0.000126987562816826 +PearsonTypeIII,2,3,1.2,2.71828182845905,0.83,4.63926728428982,0.99999999999989,0.879755761431511,-0.322852764891548,0.0899999999999999,0.0539999999999999,0,0.0778417123158923,0.0233541065357456,0.0166073646476413,0.233725230823726 +LogPearsonTypeIII,0.3,0.2,1.2,2.71828182845905,0.83,1.60954439147402,1.60954439147469,1.41600595167805,-0.0346430571333051,4e-04,0.00024,0,0.000345963165848411,0.00155694043571638,0.0166073646476414,0.0026910948023351 +PearsonTypeIII,2,3,1.2,2.71828182845905,0.99,11.4483096456118,0.999999999996189,3.14943654854133,1.86654556622528,0.0899999999999999,0.0539999999999999,0,0.0778417123158923,0.0233541065357456,0.0166073646476413,1.53468452749506 +LogPearsonTypeIII,0.3,0.2,1.2,2.71828182845905,0.99,2.53422357912955,2.53422357912851,7.98137636227418,0.315349585694458,4e-04,0.00024,0,0.000345963165848411,0.00155694043571638,0.0166073646476414,0.0438052790580856 +LnNormal,10,2,0,2.71828182845905,0.01,6.18582067170488,0.921847351651017,-1.51632642241273,0,0.0399898453234073,0.0119974563493366,0,0.023996722607005,0,0,0.0556172314685104 +LnNormal,1,1,0,2.71828182845905,0.01,0.10193853555074,0.295327587135364,-0.193389051585055,0,0.00933373687519046,0.0141382670143725,0,0.0285518574319185,0,0,0.000266932510354946 +LogNormal,0.3,0.2,0,2,0.01,0.891761873670117,0.618122228465504,-1.43796733208736,0,4e-04,0,0,2e-04,0,0,0.000566380045359353 +LogNormal,0.3,0.2,0,2.71828182845905,0.01,0.847665164110984,0.847665164110297,-1.97196405242841,0,4e-04,0,0,2e-04,0,0,0.00106514293699243 +LogNormal,0.3,0.2,0,10,0.01,0.683487262097862,1.57378758095711,-3.66117739315275,0,4e-04,0,0,2e-04,0,0,0.00367156692081648 +LnNormal,10,2,0,2.71828182845905,0.5,9.8058067569092,1.01829531706373,-0.18857320685924,0,0.0399898453234073,0.0119974563493366,0,0.023996722607005,0,0,0.0377122241859214 +LnNormal,1,1,0,2.71828182845905,0.5,0.707106781186548,1.06066017177988,-0.353553390593138,0,0.00933373687519046,0.0141382670143725,0,0.0285518574319185,0,0,0.00346573590280161 +LogNormal,0.3,0.2,0,2,0.5,1.23114441334492,0.853364278971536,-1.85037170770859e-13,0,4e-04,0,0,2e-04,0,0,0.000291292237049844 +LogNormal,0.3,0.2,0,2.71828182845905,0.5,1.349858807576,1.34985880757702,3.70074341541719e-13,0,4e-04,0,0,2e-04,0,0,0.000728847520157306 +LogNormal,0.3,0.2,0,10,0.5,1.99526231496888,4.59426126305951,0,0,4e-04,0,0,2e-04,0,0,0.00844289462129966 +LnNormal,10,2,0,2.71828182845905,0.83,11.8454013760313,1.01059545018408,0.869723437078769,0,0.0399898453234073,0.0119974563493366,0,0.023996722607005,0,0,0.0800833827772956 +LnNormal,1,1,0,2.71828182845905,0.83,1.5648986961984,1.45060689326996,0.11429180292899,0,0.00933373687519046,0.0141382670143725,0,0.0285518574319185,0,0,0.024701611084357 +LogNormal,0.3,0.2,0,2,0.83,1.40525607388199,0.974049285574578,0.929403983147003,0,4e-04,0,0,2e-04,0,0,0.000552267157069241 +LogNormal,0.3,0.2,0,2.71828182845905,0.83,1.63367658855882,1.63367658855921,1.55879743568084,0,4e-04,0,0,2e-04,0,0,0.00155352956749962 +LogNormal,0.3,0.2,0,10,0.83,3.09622883319781,7.12933035581694,6.8025593037239,0,4e-04,0,0,2e-04,0,0,0.0295859031450853 +LnNormal,10,2,0,2.71828182845905,0.99,15.544234347703,0.911924744293172,3.21249345239138,0,0.0399898453234073,0.0119974563493366,0,0.023996722607005,0,0,0.351199093130153 +LnNormal,1,1,0,2.71828182845905,0.99,4.90491645086585,0.504645855332608,4.40027059554297,0,0.00933373687519046,0.0141382670143725,0,0.0285518574319185,0,0,0.617999208072161 +LogNormal,0.3,0.2,0,2,0.99,1.69968756375774,1.17813364265177,2.74074869491714,0,4e-04,0,0,2e-04,0,0,0.00205754023371717 +LogNormal,0.3,0.2,0,2.71828182845905,0.99,2.14957376749287,2.14957376749296,5.00065636409808,0,4e-04,0,0,2e-04,0,0,0.00684957976711646 +LogNormal,0.3,0.2,0,10,0.99,5.82464652423173,13.4117442586409,31.2003827432987,0,4e-04,0,0,2e-04,0,0,0.26664273028934 From 46792b4219c8184ab135690446760535ad6213c5 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 9 Sep 2026 12:08:24 -0600 Subject: [PATCH 204/222] Restore distribution constraints and fitting performance --- .../Univariate/Base/DistributionNumerics.cs | 42 + .../Base/GammaDistributionNumerics.cs | 30 +- .../Univariate/CompetingRisks.cs | 206 +- .../Distributions/Univariate/Exponential.cs | 52 +- .../Univariate/GammaDistribution.cs | 47 +- .../Univariate/GeneralizedExtremeValue.cs | 44 +- .../Univariate/GeneralizedLogistic.cs | 74 + .../Univariate/GeneralizedNormal.cs | 62 + .../Univariate/GeneralizedPareto.cs | 60 +- Numerics/Distributions/Univariate/Gumbel.cs | 32 + Numerics/Distributions/Univariate/LnNormal.cs | 35 +- .../Distributions/Univariate/LogNormal.cs | 44 +- .../Univariate/LogPearsonTypeIII.cs | 74 +- Numerics/Distributions/Univariate/Logistic.cs | 31 +- Numerics/Distributions/Univariate/Mixture.cs | 90 +- Numerics/Distributions/Univariate/Normal.cs | 64 +- .../Univariate/PearsonTypeIII.cs | 89 +- Numerics/Distributions/Univariate/Weibull.cs | 90 +- Numerics/Numerics.csproj | 2 +- .../Test_CompetingRisksConfigurationCache.cs | 209 + .../Test_CompetingRisksDensityStepCache.cs | 189 + ...est_CompetingRisksEvaluationAllocations.cs | 81 + ...Test_DependentCompetingRisksRegressions.cs | 57 + ...Test_DistributionPerformanceRegressions.cs | 93 + .../Test_ExtremePositiveRobustness.cs | 2 +- .../Test_GeneralizedBulkSetterRegressions.cs | 110 + .../Test_LegacyParameterConstraints.cs | 897 +++ .../Test_LogPearsonPerformanceRegressions.cs | 43 + .../Test_MixturePerformanceRegressions.cs | 108 + .../Univariate/Test_MixtureWeightLogCache.cs | 224 + .../Test_NonpositiveConstraintRegressions.cs | 67 + .../Univariate/Test_NormalLogDensityCache.cs | 294 + ...Test_NormalMixtureValidationAllocations.cs | 114 + .../bestfit-regression-inventory.json | 5251 +++++++++++++++++ .../bestfit-regression-repair-plan.md | 37 + .../bestfit-regression-runs.jsonl | 804 +++ .../bestfit-regression-status.md | 138 + scripts/bestfit-regression-evidence.ps1 | 17 + scripts/run-bestfit-regression-allowlist.ps1 | 82 + scripts/run-bestfit-regression-method.ps1 | 105 + 40 files changed, 10009 insertions(+), 81 deletions(-) create mode 100644 Test_Numerics/Distributions/Univariate/Test_CompetingRisksConfigurationCache.cs create mode 100644 Test_Numerics/Distributions/Univariate/Test_CompetingRisksDensityStepCache.cs create mode 100644 Test_Numerics/Distributions/Univariate/Test_CompetingRisksEvaluationAllocations.cs create mode 100644 Test_Numerics/Distributions/Univariate/Test_DependentCompetingRisksRegressions.cs create mode 100644 Test_Numerics/Distributions/Univariate/Test_DistributionPerformanceRegressions.cs create mode 100644 Test_Numerics/Distributions/Univariate/Test_GeneralizedBulkSetterRegressions.cs create mode 100644 Test_Numerics/Distributions/Univariate/Test_LegacyParameterConstraints.cs create mode 100644 Test_Numerics/Distributions/Univariate/Test_LogPearsonPerformanceRegressions.cs create mode 100644 Test_Numerics/Distributions/Univariate/Test_MixturePerformanceRegressions.cs create mode 100644 Test_Numerics/Distributions/Univariate/Test_MixtureWeightLogCache.cs create mode 100644 Test_Numerics/Distributions/Univariate/Test_NonpositiveConstraintRegressions.cs create mode 100644 Test_Numerics/Distributions/Univariate/Test_NormalLogDensityCache.cs create mode 100644 Test_Numerics/Distributions/Univariate/Test_NormalMixtureValidationAllocations.cs create mode 100644 docs/distributions/bestfit-regression-inventory.json create mode 100644 docs/distributions/bestfit-regression-repair-plan.md create mode 100644 docs/distributions/bestfit-regression-runs.jsonl create mode 100644 docs/distributions/bestfit-regression-status.md create mode 100644 scripts/bestfit-regression-evidence.ps1 create mode 100644 scripts/run-bestfit-regression-allowlist.ps1 create mode 100644 scripts/run-bestfit-regression-method.ps1 diff --git a/Numerics/Distributions/Univariate/Base/DistributionNumerics.cs b/Numerics/Distributions/Univariate/Base/DistributionNumerics.cs index 4e3a08a9..dd1877d7 100644 --- a/Numerics/Distributions/Univariate/Base/DistributionNumerics.cs +++ b/Numerics/Distributions/Univariate/Base/DistributionNumerics.cs @@ -9,6 +9,48 @@ namespace Numerics.Distributions /// Numerical primitives shared by the reviewed univariate distributions. internal static partial class DistributionNumerics { + + /// Uses the established prior envelope whenever its initialization and bounds are usable. + /// The original family-specific initialization and bounds. + /// The exceptional-input hardened initialization and bounds. + /// The legacy constraints if finite and within ordered bounds; otherwise the hardened constraints. + /// The fallback must not narrow previously valid prior envelopes or alter their rounding. + internal static Tuple PreferLegacyConstraints( + Func> legacy, Func> fallback) + { + Exception? legacyFailure = null; + try + { + var constraints = legacy(); + bool usable = true; + for (int i = 0; i < constraints.Item1.Length; i++) + { + double initial = constraints.Item1[i], lower = constraints.Item2[i], upper = constraints.Item3[i]; + if (!IsFinite(initial) || !IsFinite(lower) || !IsFinite(upper) || !(lower < upper && lower <= initial && initial <= upper)) + { + usable = false; + break; + } + } + if (usable) return constraints; + } + catch (Exception exception) when (exception is ArgumentException || exception is InvalidOperationException || exception is ArithmeticException) + { + // Preserve initialization context if the exceptional-input path also fails. + legacyFailure = exception; + } + try + { + return fallback(); + } + catch (Exception exception) when (legacyFailure != null && + (exception is ArgumentException || exception is InvalidOperationException || exception is ArithmeticException)) + { + exception.Data["LegacyParameterConstraintsFailure"] = legacyFailure; + throw; + } + } + /// A cache key including nested distribution settings omitted from flattened parameter vectors. internal static string ConfigurationState(UnivariateDistributionBase distribution) { diff --git a/Numerics/Distributions/Univariate/Base/GammaDistributionNumerics.cs b/Numerics/Distributions/Univariate/Base/GammaDistributionNumerics.cs index 9fdc0e98..a31839a4 100644 --- a/Numerics/Distributions/Univariate/Base/GammaDistributionNumerics.cs +++ b/Numerics/Distributions/Univariate/Base/GammaDistributionNumerics.cs @@ -5,6 +5,29 @@ namespace Numerics.Distributions { internal static partial class DistributionNumerics { + /// Immutable integer zeta values reused by the log-gamma and GEV series. + private static readonly double[] IntegerZetaValues = CreateIntegerZetaValues(); + + /// Precomputes the exact integer zeta range consumed by the bounded series. + /// The zeta values for integer arguments 2 through 59, indexed by argument minus two. + /// The existing ordered power sum for arguments 17 through 59 is evaluated once during static initialization. + private static double[] CreateIntegerZetaValues() + { + double[] values = { 1.6449340668482264365, 1.2020569031595942854, 1.0823232337111381915, + 1.0369277551433699263, 1.0173430619844491397, 1.0083492773819228268, + 1.0040773561979443394, 1.0020083928260822144, 1.0009945751278180853, + 1.0004941886041194646, 1.0002460865533080483, 1.0001227133475784891, + 1.0000612481350587048, 1.0000305882363070205, 1.0000152822594086519 }; + Array.Resize(ref values, 58); + for (int n = 17; n < 60; n++) + { + double sum = 1; + for (int k = 2; k <= 32; k++) sum += Math.Pow(k, -n); + values[n - 2] = sum; + } + return values; + } + /// Trigamma with an exact recurrence and a sufficiently large asymptotic argument for covariance work. internal static double AccurateTrigamma(double shape) { @@ -261,12 +284,7 @@ internal static double LogGammaOnePlus(double a) /// Integer zeta constants for the log Gamma(1+a) series. internal static double ZetaInteger(int n) { - double[] values = { 1.6449340668482264365, 1.2020569031595942854, 1.0823232337111381915, - 1.0369277551433699263, 1.0173430619844491397, 1.0083492773819228268, - 1.0040773561979443394, 1.0020083928260822144, 1.0009945751278180853, - 1.0004941886041194646, 1.0002460865533080483, 1.0001227133475784891, - 1.0000612481350587048, 1.0000305882363070205, 1.0000152822594086519 }; - if (n <= 16) return values[n - 2]; + if (n < 60) return IntegerZetaValues[n - 2]; double sum = 1; for (int k = 2; k <= 32; k++) sum += Math.Pow(k, -n); return sum; diff --git a/Numerics/Distributions/Univariate/CompetingRisks.cs b/Numerics/Distributions/Univariate/CompetingRisks.cs index e4d20543..524bfb13 100644 --- a/Numerics/Distributions/Univariate/CompetingRisks.cs +++ b/Numerics/Distributions/Univariate/CompetingRisks.cs @@ -10,6 +10,7 @@ using System.Collections.ObjectModel; using System.Globalization; using System.Linq; +using System.Threading; using System.Xml.Linq; namespace Numerics.Distributions @@ -62,16 +63,118 @@ public CompetingRisks(IUnivariateDistribution[] distributions) private int _prngSeed = MultivariateNormal.DefaultMVNUNISeed; private string? _cachedConfiguration; + [NonSerialized] private WeibullConfiguration? _weibullConfiguration; + + /// Immutable scalar state whose equality implies identical built-in Weibull configuration XML. + private sealed class WeibullConfiguration + { + private readonly bool _minimum; + private readonly Probability.DependencyType _dependency; + private readonly int _seed; + private readonly Transform _xTransform; + private readonly Transform _probabilityTransform; + private readonly long[] _parameterBits; + private readonly long[]? _matrixBits; + private readonly int _matrixRows; + private readonly int _matrixColumns; + private DensityStep? _densityStep; + + /// Publishes the lazily computed step atomically for concurrent readers of unchanged state. + private sealed class DensityStep + { + internal readonly double Value; + internal DensityStep(double value) { Value = value; } + } + + /// Returns a previously computed component-derived step, excluding observation-dependent fallbacks. + internal bool TryGetDensityStep(out double step) + { + var cached = Volatile.Read(ref _densityStep); + step = cached is null ? 0d : cached.Value; + return cached is not null; + } + + /// Stores the unchanged derivative-step expression for this exact component configuration. + internal void CacheDensityStep(double step) => Volatile.Write(ref _densityStep, new DensityStep(step)); + + private WeibullConfiguration(CompetingRisks owner) + { + _minimum = owner.MinimumOfRandomVariables; + _dependency = owner.Dependency; + _seed = owner.PRNGSeed; + _xTransform = owner.XTransform; + _probabilityTransform = owner.ProbabilityTransform; + _parameterBits = new long[2 * owner._distributions.Length]; + for (int i = 0; i < owner._distributions.Length; i++) + { + var weibull = (Weibull)owner._distributions[i]; + _parameterBits[2 * i] = BitConverter.DoubleToInt64Bits(weibull.Lambda); + _parameterBits[2 * i + 1] = BitConverter.DoubleToInt64Bits(weibull.Kappa); + } + var matrix = owner._correlationMatrix; + if (matrix is not null) + { + _matrixRows = matrix.GetLength(0); + _matrixColumns = matrix.GetLength(1); + _matrixBits = new long[matrix.Length]; + int rowStart = matrix.GetLowerBound(0), columnStart = matrix.GetLowerBound(1), index = 0; + for (int row = 0; row < _matrixRows; row++) + for (int column = 0; column < _matrixColumns; column++) + _matrixBits[index++] = BitConverter.DoubleToInt64Bits(matrix[rowStart + row, columnStart + column]); + } + } + + /// Captures only exact built-in Weibulls; derived and custom XML callbacks retain the generic path. + internal static WeibullConfiguration? Capture(CompetingRisks owner) + { + if (owner._distributions is null) return null; + foreach (var distribution in owner._distributions) + if (distribution is null || distribution.GetType() != typeof(Weibull)) return null; + return new WeibullConfiguration(owner); + } + + /// Compares live values without allocating wrappers, parameter arrays, or XML. + internal bool Matches(CompetingRisks owner) + { + if (owner._distributions is null || owner._distributions.Length != _parameterBits.Length / 2 + || owner.MinimumOfRandomVariables != _minimum || owner.Dependency != _dependency + || owner.PRNGSeed != _seed || owner.XTransform != _xTransform + || owner.ProbabilityTransform != _probabilityTransform) return false; + for (int i = 0; i < owner._distributions.Length; i++) + { + var distribution = owner._distributions[i]; + if (distribution is null || distribution.GetType() != typeof(Weibull)) return false; + var weibull = (Weibull)distribution; + if (BitConverter.DoubleToInt64Bits(weibull.Lambda) != _parameterBits[2 * i] + || BitConverter.DoubleToInt64Bits(weibull.Kappa) != _parameterBits[2 * i + 1]) return false; + } + var matrix = owner._correlationMatrix; + if (matrix is null) return _matrixBits is null; + if (_matrixBits is null || matrix.GetLength(0) != _matrixRows || matrix.GetLength(1) != _matrixColumns) return false; + int rowStart = matrix.GetLowerBound(0), columnStart = matrix.GetLowerBound(1), index = 0; + for (int row = 0; row < _matrixRows; row++) + for (int column = 0; column < _matrixColumns; column++) + if (BitConverter.DoubleToInt64Bits(matrix[rowStart + row, columnStart + column]) != _matrixBits[index++]) return false; + return true; + } + } /// Invalidates derived caches when mutable components or configuration change. private void RefreshCachedConfiguration() { + var previous = Volatile.Read(ref _weibullConfiguration); + if (previous is not null && previous.Matches(this)) return; + // Capture before canonical serialization: fallback callbacks may mutate their configuration. + var next = WeibullConfiguration.Capture(this); string configuration = DistributionNumerics.ConfigurationState(this); - if (configuration == _cachedConfiguration) return; - _cachedConfiguration = configuration; - _momentsComputed = false; - _empiricalCDFCreated = false; - _mvnCreated = false; + if (configuration != _cachedConfiguration) + { + _cachedConfiguration = configuration; + _momentsComputed = false; + _empiricalCDFCreated = false; + _mvnCreated = false; + } + Volatile.Write(ref _weibullConfiguration, next); } /// @@ -320,13 +423,13 @@ public override double Kurtosis /// public override double Minimum { - get { return MinimumOfRandomVariables ? Distributions.Min(p => p.Minimum) : Distributions.Max(p => p.Minimum); } + get { return MinimumOfRandomVariables ? _distributions.Min(p => p.Minimum) : _distributions.Max(p => p.Minimum); } } /// public override double Maximum { - get { return MinimumOfRandomVariables ? Distributions.Min(p => p.Maximum) : Distributions.Max(p => p.Maximum); } + get { return MinimumOfRandomVariables ? _distributions.Min(p => p.Maximum) : _distributions.Max(p => p.Maximum); } } /// @@ -521,14 +624,25 @@ double logLH(double[] x) public override double PDF(double x) => Math.Exp(LogPDF(x)); /// + /// Dependent densities use a support-bounded CDF derivative. An interior + /// derivative that does not exceed 1E-300 returns negative infinity, retaining the + /// established rejection of unresolved estimation candidates. public override double LogPDF(double x) { ValidateEvaluation(); if (double.IsNaN(x)) return double.NaN; - if (x < Minimum || x > Maximum || double.IsInfinity(x)) return double.NegativeInfinity; - if (Distributions.Count == 1) return Distributions[0].LogPDF(x); + double minimum = Minimum; + if (x < minimum) return double.NegativeInfinity; + double maximum = Maximum; + if (x > maximum || double.IsInfinity(x)) return double.NegativeInfinity; + if (_distributions.Length == 1) return _distributions[0].LogPDF(x); if (Dependency != Probability.DependencyType.Independent) - return Math.Log(DependentDensity(x)); + { + double density = DependentDensity(x, minimum, maximum, out bool reuseBounds); + if (x > (reuseBounds ? minimum : Minimum) && x < (reuseBounds ? maximum : Maximum)) + return density > 1E-300 ? Math.Log(density) : double.NegativeInfinity; + return Math.Log(density); + } // Sum f_i times the other factors, without dividing by possibly zero tails. // Computing each excluded product also avoids infinity-minus-infinity in the log sum. @@ -576,27 +690,47 @@ private double IndependentEndpointLogDensity(double x) /// Checks current component validity, including mutations through public component references. private void ValidateEvaluation() { - if (!_parametersValid || Distributions.Any(d => !d.ParametersValid)) ValidateParameters(GetParameters, true); + if (!_parametersValid || Array.Exists(_distributions, d => !d.ParametersValid)) ValidateParameters(GetParameters, true); } - /// Numerically differentiates the existing dependent CDF within its mathematical support. - /// The dependence model and its probability-combination rule are unchanged. A centered - /// local step is used in the interior; endpoints use a one-sided step. Negative or unresolved - /// density is reported rather than replaced by a positive likelihood floor. - private double DependentDensity(double x) + /// Numerically differentiates the dependent CDF with a support-bounded stencil. + /// Observation at which to evaluate the density. + /// Lower support bound already read by the caller. + /// Upper support bound already read by the caller. + /// Whether exact built-in Weibulls permit reuse of the caller's support bounds. + /// The resolved finite CDF derivative. + /// A centered local step is used in the interior; endpoints use a one-sided + /// step. Finite negative interior slopes are returned for candidate rejection by + /// . Negative boundary or nonfinite density remains a failure. + private double DependentDensity(double x, double minimum, double maximum, out bool reuseBounds) { - double scale = double.PositiveInfinity; - foreach (var distribution in Distributions) + var configuration = Volatile.Read(ref _weibullConfiguration); + if (configuration is not null && !configuration.Matches(this)) configuration = null; + // Exact built-in Weibulls have fixed support and no custom evaluation callbacks. + // Derived owners and generic components retain every live support read. + reuseBounds = configuration is not null && GetType() == typeof(CompetingRisks); + double step; + if (configuration is null || !configuration.TryGetDensityStep(out step)) { - double width = distribution.InverseCDF(.75) - distribution.InverseCDF(.25); - if (width > 0 && DistributionNumerics.IsFinite(width)) scale = Math.Min(scale, width); + double scale = double.PositiveInfinity; + foreach (var distribution in Distributions) + { + double width = distribution.InverseCDF(.75) - distribution.InverseCDF(.25); + if (width > 0 && DistributionNumerics.IsFinite(width)) scale = Math.Min(scale, width); + } + bool componentScale = DistributionNumerics.IsFinite(scale); + if (!componentScale) scale = Math.Max(1, Math.Abs(x)); + step = Math.Pow(Tools.DoubleMachineEpsilon, 1.0 / 3) * scale; + if (componentScale && configuration is not null) configuration.CacheDensityStep(step); } - if (!DistributionNumerics.IsFinite(scale)) scale = Math.Max(1, Math.Abs(x)); - double step = Math.Pow(Tools.DoubleMachineEpsilon, 1.0 / 3) * scale; - double left = Math.Max(Minimum, x - step), right = Math.Min(Maximum, x + step); + double left = Math.Max(reuseBounds ? minimum : Minimum, x - step), right = Math.Min(reuseBounds ? maximum : Maximum, x + step); if (!(right > left)) throw new InvalidOperationException("The dependent density cannot be resolved at this floating-point scale."); - double density = (CDF(right) - CDF(left)) / (right - left); - if (!DistributionNumerics.IsFinite(density) || density < 0) + // The matching built-in case has already validated this fixed configuration and + // clamped both endpoints to support. Generic and derived cases retain virtual calls. + double density = reuseBounds + ? (DependentCDFCore(right) - DependentCDFCore(left)) / (right - left) + : (CDF(right) - CDF(left)) / (right - left); + if (!DistributionNumerics.IsFinite(density) || (density < 0 && (x == (reuseBounds ? minimum : Minimum) || x == (reuseBounds ? maximum : Maximum)))) throw new InvalidOperationException("Numerical differentiation of the dependent CDF did not produce a nonnegative finite density."); return density; } @@ -642,18 +776,28 @@ public override double CDF(double x) RefreshCachedConfiguration(); if (x < Minimum) return 0; if (x > Maximum) return 1; - if (Distributions.Count == 1) + if (_distributions.Length == 1) { - return Distributions[0].CDF(x); + return _distributions[0].CDF(x); } + return DependentCDFCore(x); + } + + /// Combines component CDFs after validation, configuration refresh, and support checks. + /// Observation within the current support. + /// The dependent composite probability with the existing probability bounds. + /// The density reuses this body only for an already validated, unchanged exact + /// built-in Weibull configuration. Public and generic evaluation retain their live guards. + private double DependentCDFCore(double x) + { double p = double.NaN; - var ind = new int[Distributions.Count]; - var cdf = new double[Distributions.Count]; - for (int i = 0; i < Distributions.Count; i++) + var ind = new int[_distributions.Length]; + var cdf = new double[_distributions.Length]; + for (int i = 0; i < _distributions.Length; i++) { ind[i] = 1; - cdf[i] = Distributions[i].CDF(x); + cdf[i] = _distributions[i].CDF(x); } if (MinimumOfRandomVariables == true) diff --git a/Numerics/Distributions/Univariate/Exponential.cs b/Numerics/Distributions/Univariate/Exponential.cs index aef50c56..4ce23181 100644 --- a/Numerics/Distributions/Univariate/Exponential.cs +++ b/Numerics/Distributions/Univariate/Exponential.cs @@ -321,10 +321,58 @@ public double[] LinearMomentsFromParameters(IList parameters) } /// - /// Requires at least four finite, nonconstant observations. The existing initialization - /// estimator is evaluated in unit coordinates before finite location and positive scale bounds are formed. + /// Requires at least four finite, nonconstant observations. Preserves the legacy initialization + /// and family-specific bounds whenever they are finite, ordered, and contain the initial values. + /// Otherwise, the exceptional-input fallback evaluates the estimator in unit coordinates before + /// forming finite location and positive scale bounds. /// The sample or a representable feasible initialization is invalid. public Tuple GetParameterConstraints(IList sample) + { + DistributionNumerics.ValidateSample(sample, 4); + return DistributionNumerics.PreferLegacyConstraints( + () => GetLegacyParameterConstraints(sample), () => GetRobustParameterConstraints(sample)); + } + + /// Preserves the established initialization and family-specific prior envelope. + /// The validated observations. + /// The legacy initial values and lower and upper bounds. + private Tuple GetLegacyParameterConstraints(IList sample) + { + var initialVals = new double[NumberOfParameters]; + var lowerVals = new double[NumberOfParameters]; + var upperVals = new double[NumberOfParameters]; + + // Get initial values + var moments = Statistics.ProductMoments(sample); + double minData = Statistics.Minimum(sample); + initialVals[0] = (sample.Count * minData - moments[0]) / (sample.Count - 1); + initialVals[1] = sample.Count * (moments[0] - minData) / (sample.Count - 1); + + // Get bounds of location + if (initialVals[0] == 0d) initialVals[0] = Tools.DoubleMachineEpsilon; + lowerVals[0] = initialVals[0] - Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])))); + upperVals[0] = minData; + + // Get bounds of scale + lowerVals[1] = Tools.DoubleMachineEpsilon; + upperVals[1] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[1]) + 1d)); + + // Correct initial values if necessary + if (initialVals[0] <= lowerVals[0] || initialVals[0] >= upperVals[0]) + { + initialVals[0] = Statistics.Mean([lowerVals[0], upperVals[0]]); + } + if (initialVals[1] <= lowerVals[1] || initialVals[1] >= upperVals[1]) + { + initialVals[1] = Statistics.Mean([lowerVals[1], upperVals[1]]); + } + return new Tuple(initialVals, lowerVals, upperVals); + } + + /// Handles samples whose legacy initialization or bounds are not finite or outside ordered bounds. + /// The validated observations. + /// Finite initial values and bounds from the hardened initialization path. + private Tuple GetRobustParameterConstraints(IList sample) { DistributionNumerics.ValidateSample(sample, 4); var initialVals = new double[NumberOfParameters]; diff --git a/Numerics/Distributions/Univariate/GammaDistribution.cs b/Numerics/Distributions/Univariate/GammaDistribution.cs index 38c14871..acfeb923 100644 --- a/Numerics/Distributions/Univariate/GammaDistribution.cs +++ b/Numerics/Distributions/Univariate/GammaDistribution.cs @@ -478,10 +478,53 @@ public double[] LinearMomentsFromParameters(IList parameters) } /// - /// Requires at least four finite, strictly positive, nonconstant observations. The - /// existing moment initialization is evaluated in unit coordinates to preserve small and large scales. + /// Requires at least four finite, nonconstant observations. Preserves the + /// legacy moment initialization and family-specific bounds whenever they are finite, ordered, and + /// contain the initial values, including usable samples containing nonpositive observations. + /// Otherwise, the exceptional-input fallback requires positive observations and evaluates the moment + /// initialization in unit coordinates to preserve small and large scales. /// The sample or a finite feasible initialization is invalid. public Tuple GetParameterConstraints(IList sample) + { + DistributionNumerics.ValidateSample(sample, 4); + return DistributionNumerics.PreferLegacyConstraints( + () => GetLegacyParameterConstraints(sample), () => GetRobustParameterConstraints(sample)); + } + + /// Preserves the established initialization and family-specific prior envelope. + /// The validated observations. + /// The legacy initial values and lower and upper bounds. + private Tuple GetLegacyParameterConstraints(IList sample) + { + var initialVals = new double[NumberOfParameters]; + var lowerVals = new double[NumberOfParameters]; + var upperVals = new double[NumberOfParameters]; + // Get initial values + initialVals = LegacyConstraintParametersFromMoments(Statistics.ProductMoments(sample)); + // Get bounds of scale + lowerVals[0] = Tools.DoubleMachineEpsilon; + upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[0]) + 1d)); + // Get bounds of shape + lowerVals[1] = Tools.DoubleMachineEpsilon; + upperVals[1] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[1]) + 1d)); + return new Tuple(initialVals, lowerVals, upperVals); + } + + /// Retains the established constraint initializer arithmetic for ordinary samples. + /// Sample moments. + /// The legacy initial parameter values. + private double[] LegacyConstraintParametersFromMoments(IList moments) + { + var parms = new double[NumberOfParameters]; + parms[0] = 1d / (moments[0] / Math.Pow(moments[1], 2d)); + parms[1] = Math.Pow(moments[0], 2d) / Math.Pow(moments[1], 2d); + return parms; + } + + /// Handles samples whose legacy initialization or bounds are not finite or outside ordered bounds. + /// The validated observations. + /// Finite initial values and bounds from the hardened initialization path. + private Tuple GetRobustParameterConstraints(IList sample) { DistributionNumerics.ValidateSample(sample, 4, true); var lowerVals = new double[NumberOfParameters]; diff --git a/Numerics/Distributions/Univariate/GeneralizedExtremeValue.cs b/Numerics/Distributions/Univariate/GeneralizedExtremeValue.cs index 93ec18f4..2706226a 100644 --- a/Numerics/Distributions/Univariate/GeneralizedExtremeValue.cs +++ b/Numerics/Distributions/Univariate/GeneralizedExtremeValue.cs @@ -318,9 +318,9 @@ public IUnivariateDistribution Bootstrap(ParameterEstimationMethod estimationMet public void SetParameters(double location, double scale, double shape) { _parametersValid = ValidateParameters(location, scale, shape, false) is null; - Xi = location; + _xi = location; _alpha = scale; - Kappa = shape; + _kappa = shape; } /// @@ -534,6 +534,46 @@ public double[] LinearMomentsFromParameters(IList parameters) /// the existing linear-moment estimator and preserves the fitting shape bounds. /// The sample or a finite feasible initialization is invalid. public Tuple GetParameterConstraints(IList sample) + { + DistributionNumerics.ValidateSample(sample, 4); + return DistributionNumerics.PreferLegacyConstraints( + () => GetLegacyParameterConstraints(sample), () => GetRobustParameterConstraints(sample)); + } + + /// Preserves the established initialization and family-specific prior envelope. + /// The validated observations. + /// The legacy initial values and lower and upper bounds. + private Tuple GetLegacyParameterConstraints(IList sample) + { + var initialVals = new double[NumberOfParameters]; + var lowerVals = new double[NumberOfParameters]; + var upperVals = new double[NumberOfParameters]; + // Get initial values + // initialVals = DirectMethodOfMoments(Statistics.ComputeProductMoments(sample)) + initialVals = ParametersFromLinearMoments(Statistics.LinearMoments(sample)); + // Get bounds of location + if (initialVals[0] == 0d) initialVals[0] = Tools.DoubleMachineEpsilon; + lowerVals[0] = -Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); + upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); + // Get bounds of scale + lowerVals[1] = Tools.DoubleMachineEpsilon; + upperVals[1] = Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[1])) + 1d)); + // Get bounds of shape + lowerVals[2] = -10; + upperVals[2] = 10d; + // Correct initial value of kappa if necessary + if (initialVals[2] <= lowerVals[2] || initialVals[2] >= upperVals[2]) + { + initialVals[2] = 0d; + } + // + return new Tuple(initialVals, lowerVals, upperVals); + } + + /// Handles samples whose legacy initialization or bounds are not finite or outside ordered bounds. + /// The validated observations. + /// Finite initial values and bounds from the hardened initialization path. + private Tuple GetRobustParameterConstraints(IList sample) { DistributionNumerics.ValidateSample(sample, 4); var lowerVals = new double[NumberOfParameters]; diff --git a/Numerics/Distributions/Univariate/GeneralizedLogistic.cs b/Numerics/Distributions/Univariate/GeneralizedLogistic.cs index e55eca2a..cbd84ab9 100644 --- a/Numerics/Distributions/Univariate/GeneralizedLogistic.cs +++ b/Numerics/Distributions/Univariate/GeneralizedLogistic.cs @@ -485,6 +485,80 @@ public double[] LinearMomentsFromParameters(IList parameters) } /// public Tuple GetParameterConstraints(IList sample) + { + DistributionNumerics.ValidateSample(sample, 4); + return DistributionNumerics.PreferLegacyConstraints( + () => GetLegacyParameterConstraints(sample), () => GetRobustParameterConstraints(sample)); + } + + /// Preserves the established initialization and family-specific prior envelope. + /// The validated observations. + /// The legacy initial values and lower and upper bounds. + private Tuple GetLegacyParameterConstraints(IList sample) + { + // Estimate initial values using the method of moments (a.k.a product moments). + var initialVals = new double[NumberOfParameters]; + var lowerVals = new double[NumberOfParameters]; + var upperVals = new double[NumberOfParameters]; + // Get initial values + // initialVals = DirectMethodOfMoments(Statistics.ComputeProductMoments(sample)) + initialVals = LegacyConstraintParametersFromLinearMoments(Statistics.LinearMoments(sample)); + // Get bounds of location + if (initialVals[0] == 0d) initialVals[0] = Tools.DoubleMachineEpsilon; + lowerVals[0] = -Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); + upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); + // Get bounds of scale + lowerVals[1] = Tools.DoubleMachineEpsilon; + upperVals[1] = Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[1]))) + 1d); + // Get bounds of shape + lowerVals[2] = -10; + upperVals[2] = 10d; + // Correct initial value of kappa if necessary + if (initialVals[2] <= lowerVals[2] || initialVals[2] >= upperVals[2]) + { + initialVals[2] = 0d; + } + return new Tuple(initialVals, lowerVals, upperVals); + } + + /// Retains the established constraint initializer arithmetic for ordinary samples. + /// Sample moments. + /// The legacy initial parameter values. + private double[] LegacyConstraintParametersFromLinearMoments(IList moments) + { + double L1 = moments[0]; + double L2 = moments[1]; + double T3 = moments[2]; + double T4 = moments[3]; + double kappa = -T3; + double alpha; + double xi; + if (kappa == 0.0d) + { + alpha = L2; + xi = L1; + } + else if (Math.Abs(kappa) <= NearZero) + { + double kappa2 = kappa * kappa; + double pi2 = Math.PI * Math.PI; + double sinc = 1.0d - pi2 * kappa2 / 6.0d + pi2 * pi2 * kappa2 * kappa2 / 120.0d; + double reciprocalDifference = -pi2 * kappa / 6.0d - 7.0d * pi2 * pi2 * kappa * kappa2 / 360.0d; + alpha = L2 * sinc; + xi = L1 - alpha * reciprocalDifference; + } + else + { + alpha = L2 * Math.Sin(kappa * Math.PI) / (kappa * Math.PI); + xi = L1 - alpha * (1.0d / kappa - Math.PI / Math.Sin(kappa * Math.PI)); + } + return [xi, alpha, kappa]; + } + + /// Handles samples whose legacy initialization or bounds are not finite or outside ordered bounds. + /// The validated observations. + /// Finite initial values and bounds from the hardened initialization path. + private Tuple GetRobustParameterConstraints(IList sample) { DistributionNumerics.ValidateSample(sample, 4); // Estimate initial values using the method of moments (a.k.a product moments). diff --git a/Numerics/Distributions/Univariate/GeneralizedNormal.cs b/Numerics/Distributions/Univariate/GeneralizedNormal.cs index 9d8fd375..767d82be 100644 --- a/Numerics/Distributions/Univariate/GeneralizedNormal.cs +++ b/Numerics/Distributions/Univariate/GeneralizedNormal.cs @@ -404,6 +404,68 @@ public double[] LinearMomentsFromParameters(IList parameters) /// public Tuple GetParameterConstraints(IList sample) + { + DistributionNumerics.ValidateSample(sample, 4); + return DistributionNumerics.PreferLegacyConstraints( + () => GetLegacyParameterConstraints(sample), () => GetRobustParameterConstraints(sample)); + } + + /// Preserves the established initialization and family-specific prior envelope. + /// The validated observations. + /// The legacy initial values and lower and upper bounds. + private Tuple GetLegacyParameterConstraints(IList sample) + { + // Estimate initial values using the method of moments (a.k.a product moments). + var initialVals = new double[NumberOfParameters]; + var lowerVals = new double[NumberOfParameters]; + var upperVals = new double[NumberOfParameters]; + // Get initial values + initialVals = LegacyConstraintParametersFromLinearMoments(Statistics.LinearMoments(sample)); + // Get bounds of location + if (initialVals[0] == 0d) initialVals[0] = Tools.DoubleMachineEpsilon; + lowerVals[0] = -Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); + upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); + // Get bounds of scale + lowerVals[1] = Tools.DoubleMachineEpsilon; + upperVals[1] = Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[1]))) + 1d); + // Get bounds of shape + lowerVals[2] = -10d; + upperVals[2] = 10d; + // Correct initial value of kappa if necessary + if (initialVals[2] <= lowerVals[2] || initialVals[2] >= upperVals[2]) + { + initialVals[2] = 0d; + } + return new Tuple(initialVals, lowerVals, upperVals); + } + + /// Retains the established constraint initializer arithmetic for ordinary samples. + /// Sample moments. + /// The legacy initial parameter values. + private double[] LegacyConstraintParametersFromLinearMoments(IList moments) + { + double L1 = moments[0]; + double L2 = moments[1]; + double T3 = moments[2]; + + double E0 = 2.0466534; + double E1 = -3.6544371; + double E2 = 1.8396733; + double E3 = -0.20360244; + double F1 = -2.0182173; + double F2 = 1.2420401; + double F3 = -0.21741801; + + double kappa = -T3 * (E0 + E1 * Math.Pow(T3, 2d) + E2 * Math.Pow(T3, 4d) + E3 * Math.Pow(T3, 6d)) / (1d + F1 * Math.Pow(T3, 2d) + F2 * Math.Pow(T3, 4d) + F3 * Math.Pow(T3, 6d)); + double alpha = (L2 * kappa * Math.Exp(-(kappa * kappa) / 2d)) / (1d - 2 * Normal.StandardCDF(-kappa / Tools.Sqrt2)); + double xi = L1 - alpha * (1.0d - Math.Exp(kappa * kappa / 2d)) / kappa; + return [xi, alpha, kappa]; + } + + /// Handles samples whose legacy initialization or bounds are not finite or outside ordered bounds. + /// The validated observations. + /// Finite initial values and bounds from the hardened initialization path. + private Tuple GetRobustParameterConstraints(IList sample) { DistributionNumerics.ValidateSample(sample, 4); // Estimate initial values using the method of moments (a.k.a product moments). diff --git a/Numerics/Distributions/Univariate/GeneralizedPareto.cs b/Numerics/Distributions/Univariate/GeneralizedPareto.cs index 104c83db..151f0391 100644 --- a/Numerics/Distributions/Univariate/GeneralizedPareto.cs +++ b/Numerics/Distributions/Univariate/GeneralizedPareto.cs @@ -308,9 +308,9 @@ public IUnivariateDistribution Bootstrap(ParameterEstimationMethod estimationMet public void SetParameters(double location, double scale, double shape) { _parametersValid = ValidateParameters(location, scale, shape, false) is null; - Xi = location; + _xi = location; _alpha = scale; - Kappa = shape; + _kappa = shape; } /// @@ -485,10 +485,62 @@ public double[] LinearMomentsFromParameters(IList parameters) } /// - /// Requires at least four finite, nonconstant observations. The existing linear- - /// moment initialization is rescaled algebraically; the upper location bound is the sample minimum. + /// Requires at least four finite, nonconstant observations. Preserves the legacy linear-moment + /// initialization and family-specific bounds whenever they are finite, ordered, and contain the initial + /// values; the legacy upper location bound is the sample minimum plus . + /// Otherwise, the exceptional-input fallback evaluates the linear-moment initialization in normalized + /// coordinates and rescales it algebraically; its upper location bound is the sample minimum. /// The sample or a finite feasible initialization is invalid. public Tuple GetParameterConstraints(IList sample) + { + DistributionNumerics.ValidateSample(sample, 4); + return DistributionNumerics.PreferLegacyConstraints( + () => GetLegacyParameterConstraints(sample), () => GetRobustParameterConstraints(sample)); + } + + /// Preserves the established initialization and family-specific prior envelope. + /// The validated observations. + /// The legacy initial values and lower and upper bounds. + private Tuple GetLegacyParameterConstraints(IList sample) + { + var initialVals = new double[NumberOfParameters]; + var lowerVals = new double[NumberOfParameters]; + var upperVals = new double[NumberOfParameters]; + // + // Get initial values + initialVals = ParametersFromLinearMoments(Statistics.LinearMoments(sample)); + double minData = Statistics.Minimum(sample); + // Get bounds of location + if (initialVals[0] == 0d) initialVals[0] = Tools.DoubleMachineEpsilon; + lowerVals[0] = initialVals[0] - Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])))); + upperVals[0] = minData + Tools.DoubleMachineEpsilon; + + // Get bounds of scale + lowerVals[1] = Tools.DoubleMachineEpsilon; + upperVals[1] = Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[1])) + 1d)); + // Get bounds of shape + lowerVals[2] = -10d; + upperVals[2] = 10d; + // Correct initial values if necessary + if (initialVals[0] <= lowerVals[0] || initialVals[0] >= upperVals[0]) + { + initialVals[0] = Statistics.Mean(new[] { lowerVals[0], upperVals[0] }); + } + if (initialVals[1] <= lowerVals[1] || initialVals[1] >= upperVals[1]) + { + initialVals[1] = Statistics.Mean(new[] { lowerVals[1], upperVals[1] }); + } + if (initialVals[2] <= lowerVals[2] || initialVals[2] >= upperVals[2]) + { + initialVals[2] = 0d; + } + return new Tuple(initialVals, lowerVals, upperVals); + } + + /// Handles samples whose legacy initialization or bounds are not finite or outside ordered bounds. + /// The validated observations. + /// Finite initial values and bounds from the hardened initialization path. + private Tuple GetRobustParameterConstraints(IList sample) { DistributionNumerics.ValidateSample(sample, 4); var lowerVals = new double[NumberOfParameters]; diff --git a/Numerics/Distributions/Univariate/Gumbel.cs b/Numerics/Distributions/Univariate/Gumbel.cs index d4d64edf..163bc2d2 100644 --- a/Numerics/Distributions/Univariate/Gumbel.cs +++ b/Numerics/Distributions/Univariate/Gumbel.cs @@ -328,6 +328,38 @@ public double[] LinearMomentsFromParameters(IList parameters) /// scale-aware bounds around the existing linear-moment initialization. /// The sample or a feasible finite initialization is invalid. public Tuple GetParameterConstraints(IList sample) + { + DistributionNumerics.ValidateSample(sample, 4); + return DistributionNumerics.PreferLegacyConstraints( + () => GetLegacyParameterConstraints(sample), () => GetRobustParameterConstraints(sample)); + } + + /// Preserves the established initialization and family-specific prior envelope. + /// The validated observations. + /// The legacy initial values and lower and upper bounds. + private Tuple GetLegacyParameterConstraints(IList sample) + { + var initialVals = new double[NumberOfParameters]; + var lowerVals = new double[NumberOfParameters]; + var upperVals = new double[NumberOfParameters]; + // + // Get initial values + // initialVals = DirectMethodOfMoments(Statistics.ComputeProductMoments(sample)) + initialVals = ParametersFromLinearMoments(Statistics.LinearMoments(sample)); + // Get bounds of location + if (initialVals[0] == 0d) initialVals[0] = Tools.DoubleMachineEpsilon; + lowerVals[0] = -Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); + upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); + // Get bounds of scale + lowerVals[1] = Tools.DoubleMachineEpsilon; + upperVals[1] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[1]) + 1d)); + return new Tuple(initialVals, lowerVals, upperVals); + } + + /// Handles samples whose legacy initialization or bounds are not finite or outside ordered bounds. + /// The validated observations. + /// Finite initial values and bounds from the hardened initialization path. + private Tuple GetRobustParameterConstraints(IList sample) { DistributionNumerics.ValidateSample(sample, 4); var lowerVals = new double[NumberOfParameters]; diff --git a/Numerics/Distributions/Univariate/LnNormal.cs b/Numerics/Distributions/Univariate/LnNormal.cs index bd15a128..971f7a03 100644 --- a/Numerics/Distributions/Univariate/LnNormal.cs +++ b/Numerics/Distributions/Univariate/LnNormal.cs @@ -430,10 +430,43 @@ public double[] LinearMomentsFromParameters(IList parameters) } /// + /// Preserves usable legacy physical-moment initialization and rounded prior bounds, + /// including samples containing nonpositive observations. Density support is unchanged. public Tuple GetParameterConstraints(IList sample) + { + DistributionNumerics.ValidateSample(sample, 4); + return DistributionNumerics.PreferLegacyConstraints( + () => GetLegacyParameterConstraints(sample), () => GetRobustParameterConstraints(sample)); + } + + /// Preserves the established initialization and family-specific prior envelope. + /// The validated observations. + /// The legacy initial values and lower and upper bounds. + private Tuple GetLegacyParameterConstraints(IList sample) + { + var initialVals = new double[NumberOfParameters]; + var lowerVals = new double[NumberOfParameters]; + var upperVals = new double[NumberOfParameters]; + // Get initial values + var moments = Statistics.ProductMoments(sample); + initialVals[0] = moments[0]; + initialVals[1] = moments[1]; + // Get bounds of mean + lowerVals[0] = Tools.DoubleMachineEpsilon; + upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[0]) + 1d)); + // Get bounds of standard deviation + lowerVals[1] = Tools.DoubleMachineEpsilon; + upperVals[1] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[1]) + 1d)); + return new Tuple(initialVals, lowerVals, upperVals); + } + + /// Handles samples whose legacy initialization or bounds are not finite or outside ordered bounds. + /// The validated observations. + /// Finite initial values and bounds from the hardened initialization path. + private Tuple GetRobustParameterConstraints(IList sample) { DistributionNumerics.ValidateSample(sample, 4, positive: true); - var constraints = new Normal().GetParameterConstraints(sample); + var constraints = new Normal().GetRobustParameterConstraints(sample); constraints.Item2[0] = Math.Min(Tools.DoubleMachineEpsilon, constraints.Item1[0] / 10d); return constraints; } diff --git a/Numerics/Distributions/Univariate/LogNormal.cs b/Numerics/Distributions/Univariate/LogNormal.cs index 9e3690f6..f723dee9 100644 --- a/Numerics/Distributions/Univariate/LogNormal.cs +++ b/Numerics/Distributions/Univariate/LogNormal.cs @@ -413,12 +413,54 @@ public double[] LinearMomentsFromParameters(IList parameters) } /// + /// Preserves the legacy 0.1 substitution for nonpositive observations when constructing + /// initial values and rounded prior bounds. This does not modify the sample or density support. public Tuple GetParameterConstraints(IList sample) + { + DistributionNumerics.ValidateSample(sample, 4); + return DistributionNumerics.PreferLegacyConstraints( + () => GetLegacyParameterConstraints(sample), () => GetRobustParameterConstraints(sample)); + } + + /// Preserves the established initialization and family-specific prior envelope. + /// The validated observations. + /// The legacy initial values and lower and upper bounds. + private Tuple GetLegacyParameterConstraints(IList sample) + { + var initialVals = new double[NumberOfParameters]; + var lowerVals = new double[NumberOfParameters]; + var upperVals = new double[NumberOfParameters]; + // Estimate initial values using the method of moments (a.k.a product moments). + var transformedSample = new double[sample.Count]; + for (int i = 0; i < sample.Count; i++) + transformedSample[i] = Math.Log(sample[i] > 0d ? sample[i] : 0.1d, Base); + var mom = Statistics.ProductMoments(transformedSample); + initialVals = new double[] { mom[0], mom[1] }; + // Get bounds of mean. The mean is a location parameter on the log scale and is + // legitimately negative whenever the data are mostly below 1, so the bounds are + // symmetric about zero from the magnitude of the initial value, matching Normal's + // location bounds. A machine-epsilon floor here would reject any sub-unity sample + // before a fit could start. + if (initialVals[0] == 0d) initialVals[0] = Tools.DoubleMachineEpsilon; + lowerVals[0] = -Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); + upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); + // Get bounds of standard deviation + double real = Math.Exp(initialVals[1] / K); + lowerVals[1] = Tools.DoubleMachineEpsilon; + upperVals[1] = Math.Ceiling(Math.Log(Math.Pow(10d, Math.Ceiling(Math.Log10(real) + 1d)), Base)); + upperVals[1] = double.IsNaN(upperVals[1]) ? 4 : upperVals[1]; + return new Tuple(initialVals, lowerVals, upperVals); + } + + /// Handles samples whose legacy initialization or bounds are not finite or outside ordered bounds. + /// The validated observations. + /// Finite initial values and bounds from the hardened initialization path. + private Tuple GetRobustParameterConstraints(IList sample) { DistributionNumerics.ValidateSample(sample, 4, positive: true); var transformed = new double[sample.Count]; for (int i = 0; i < sample.Count; i++) transformed[i] = Math.Log(sample[i], Base); - return new Normal().GetParameterConstraints(transformed); + return new Normal().GetRobustParameterConstraints(transformed); } /// diff --git a/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs b/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs index 6724de72..1317f618 100644 --- a/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs +++ b/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs @@ -641,12 +641,64 @@ public double[] LinearMomentsFromParameters(IList parameters) } /// + /// Preserves the legacy 0.01 substitution for nonpositive observations when constructing + /// initial values and rounded prior bounds. This does not modify the sample or density support. public Tuple GetParameterConstraints(IList sample) + { + DistributionNumerics.ValidateSample(sample, 4); + return DistributionNumerics.PreferLegacyConstraints( + () => GetLegacyParameterConstraints(sample), () => GetRobustParameterConstraints(sample)); + } + + /// Preserves the established initialization and family-specific prior envelope. + /// The validated observations. + /// The legacy initial values and lower and upper bounds. + private Tuple GetLegacyParameterConstraints(IList sample) + { + var initialVals = new double[NumberOfParameters]; + var lowerVals = new double[NumberOfParameters]; + var upperVals = new double[NumberOfParameters]; + // + // Estimate initial values using the method of moments. + var transformedSample = new double[sample.Count]; + for (int i = 0; i < sample.Count; i++) + transformedSample[i] = Math.Log(sample[i] > 0d ? sample[i] : 0.01d, Base); + var mom = Statistics.ProductMoments(transformedSample); + initialVals = [mom[0], mom[1], mom[2]]; + // Get bounds of mean. The mean is a location parameter on the log scale and is + // legitimately negative whenever the data are mostly below 1, so the bounds are + // symmetric about zero from the magnitude of the initial value, matching Normal's + // location bounds. A machine-epsilon floor here would reject any sub-unity sample + // before a fit could start. + if (initialVals[0] == 0d) initialVals[0] = Tools.DoubleMachineEpsilon; + lowerVals[0] = -Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); + upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); + // Get bounds of standard deviation + double real = Math.Exp(initialVals[1] / K); + lowerVals[1] = Tools.DoubleMachineEpsilon; + upperVals[1] = Math.Ceiling(Math.Log(Math.Pow(10d, Math.Ceiling(Math.Log10(real) + 1d)), Base)); + upperVals[1] = double.IsNaN(upperVals[1]) ? 4 : upperVals[1]; + + // Get bounds of skew + lowerVals[2] = -6d; + upperVals[2] = 6d; + // Correct initial value of skew if necessary + if (initialVals[2] <= lowerVals[2] || initialVals[2] >= upperVals[2]) + { + initialVals[2] = 0.01; + } + return new Tuple(initialVals, lowerVals, upperVals); + } + + /// Handles samples whose legacy initialization or bounds are not finite or outside ordered bounds. + /// The validated observations. + /// Finite initial values and bounds from the hardened initialization path. + private Tuple GetRobustParameterConstraints(IList sample) { DistributionNumerics.ValidateSample(sample, 4, positive: true); var transformed = new double[sample.Count]; for (int i = 0; i < sample.Count; i++) transformed[i] = Math.Log(sample[i], Base); - return new PearsonTypeIII().GetParameterConstraints(transformed); + return new PearsonTypeIII().GetRobustParameterConstraints(transformed); } /// @@ -689,20 +741,23 @@ public override double PDF(double x) public override double LogPDF(double x) { if (!_parametersValid) ValidateParameters(Mu, Sigma, Gamma, true); - if (x < Minimum || x > Maximum || double.IsPositiveInfinity(x)) return double.NegativeInfinity; + double logBase = Math.Log(Base); + double boundary = Gamma == 0d ? double.NaN : Math.Exp(Xi * logBase); + double minimum = Gamma > 0d ? boundary : 0d; + double maximum = Gamma < 0d ? boundary : double.PositiveInfinity; + if (x < minimum || x > maximum || double.IsPositiveInfinity(x)) return double.NegativeInfinity; if (x == 0d) { if (Gamma >= 0d) return double.NegativeInfinity; - double rate = -1d / (Beta * Math.Log(Base)); + double rate = -1d / (Beta * logBase); if (rate > 1d) return double.NegativeInfinity; if (rate < 1d || Alpha > 1d) return double.PositiveInfinity; - return Alpha == 1d ? -Xi * Math.Log(Base) : double.NegativeInfinity; + return Alpha == 1d ? -Xi * logBase : double.NegativeInfinity; } - double logX = Math.Log(x), logBase = Math.Log(Base); - var pearson = new PearsonTypeIII(Mu, Sigma, Gamma); + double logX = Math.Log(x); // Preserve exact transformed endpoint density limits despite logarithm roundoff. - double transformed = (Gamma > 0d && x == Minimum) || (Gamma < 0d && x == Maximum) ? Xi : logX / logBase; - return pearson.LogPDF(transformed) - Math.Log(logBase) - logX; + double transformed = (Gamma > 0d && x == boundary) || (Gamma < 0d && x == boundary) ? Xi : logX / logBase; + return PearsonTypeIII.LogPDF(Mu, Sigma, Gamma, transformed) - Math.Log(logBase) - logX; } /// @@ -785,7 +840,8 @@ public double[] QuantileGradient(double probability) DistributionNumerics.ValidateProbability(probability); if (!_parametersValid) ValidateParameters(Mu, Sigma, Gamma, true); double[] gradient = new PearsonTypeIII(Mu, Sigma, Gamma).QuantileGradient(probability); - double factor = InverseCDF(probability) * Math.Log(Base); + double quantile = Math.Exp((Mu + Sigma * gradient[1]) * Math.Log(Base)); + double factor = quantile * Math.Log(Base); for (int i = 0; i < gradient.Length; i++) gradient[i] *= factor; return gradient; } diff --git a/Numerics/Distributions/Univariate/Logistic.cs b/Numerics/Distributions/Univariate/Logistic.cs index 0ec80661..e2d33ea7 100644 --- a/Numerics/Distributions/Univariate/Logistic.cs +++ b/Numerics/Distributions/Univariate/Logistic.cs @@ -308,7 +308,36 @@ public double[] MomentsFromParameters(IList parameters) /// public Tuple GetParameterConstraints(IList sample) { - var normal = new Normal().GetParameterConstraints(sample); + DistributionNumerics.ValidateSample(sample, 4); + return DistributionNumerics.PreferLegacyConstraints( + () => GetLegacyParameterConstraints(sample), () => GetRobustParameterConstraints(sample)); + } + + /// Preserves the established initialization and family-specific prior envelope. + /// The validated observations. + /// The legacy initial values and lower and upper bounds. + private Tuple GetLegacyParameterConstraints(IList sample) + { + var initialVals = new double[NumberOfParameters]; + var lowerVals = new double[NumberOfParameters]; + var upperVals = new double[NumberOfParameters]; + // Get initial values + initialVals = ParametersFromMoments(Statistics.ProductMoments(sample)); + // Get bounds of location + lowerVals[0] = -Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[0]) + 1d)); + upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[0]) + 1d)); + // Get bounds of scale + lowerVals[1] = Tools.DoubleMachineEpsilon; + upperVals[1] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[1]) + 1d)); + return new Tuple(initialVals, lowerVals, upperVals); + } + + /// Handles samples whose legacy initialization or bounds are not finite or outside ordered bounds. + /// The validated observations. + /// Finite initial values and bounds from the hardened initialization path. + private Tuple GetRobustParameterConstraints(IList sample) + { + var normal = new Normal().GetRobustParameterConstraints(sample); double correction = Math.Sqrt(3d) / Math.PI; normal.Item1[1] *= correction; normal.Item2[1] *= correction; diff --git a/Numerics/Distributions/Univariate/Mixture.cs b/Numerics/Distributions/Univariate/Mixture.cs index 6d9e0ab6..084d72ad 100644 --- a/Numerics/Distributions/Univariate/Mixture.cs +++ b/Numerics/Distributions/Univariate/Mixture.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Threading; using System.Xml.Linq; namespace Numerics.Distributions @@ -64,6 +65,20 @@ public Mixture(double[] weights, IUnivariateDistribution[] distributions) private bool _momentsComputed = false; private double u1, u2, u3, u4; private bool _empiricalCDFCreated = false; + [NonSerialized] private WeightLogEntry?[]? _logWeightCache; + + /// An immutable weight/log pair published atomically to concurrent density readers. + private sealed class WeightLogEntry + { + internal readonly long WeightBits; + internal readonly double LogValue; + + internal WeightLogEntry(long weightBits, double logValue) + { + WeightBits = weightBits; + LogValue = logValue; + } + } /// /// Returns the array of distribution weights. @@ -184,7 +199,8 @@ private static double BitDecrement(double value) /// Gets the component log probability above zero without requiring representable probability mass. private double PositiveLogMass(int componentIndex) { - double log = Distributions[componentIndex].LogCCDF(0); + double log = Distributions[componentIndex] is Normal normal + ? normal.LogCCDFAtZero() : Distributions[componentIndex].LogCCDF(0); if (!Tools.IsFinite(log) || log > 0) throw new InvalidOperationException("The active component must have positive probability above zero."); return log; } @@ -241,7 +257,51 @@ private void RefreshCachedConfiguration() } /// Checks mutable weights and current component validity before evaluation. - private void ValidateEvaluation() => ValidateParameters(GetParameters, true); + /// Validates live component parameters directly so evaluation does not flatten and re-slice + /// the same state. Sealed Normal components delegate to their existing scalar validator without + /// allocating parameter arrays; other components retain their list validator. Every component is + /// checked on every call because public arrays and nested component settings remain mutable. + private void ValidateEvaluation() + { + ArgumentOutOfRangeException? error = null; + if (_distributions is null || _weights is null || _distributions.Length == 0 + || _distributions.Length != _weights.Length || Array.Exists(_distributions, d => d is null)) + error = new ArgumentOutOfRangeException(nameof(Distributions), "At least one non-null component and a matching weight vector are required."); + else if (IsZeroInflated && (!Tools.IsFinite(ZeroWeight) || ZeroWeight < 0 || ZeroWeight >= 1)) + error = new ArgumentOutOfRangeException(nameof(ZeroWeight), "The zero weight must be finite and in [0,1)."); + else + { + int count = Distributions.Length; + double mass = IsZeroInflated ? ZeroWeight : 0; + for (int i = 0; i < count; i++) + { + double weight = Weights[i]; + if (!Tools.IsFinite(weight) || weight < 0 || weight > 1) + { error = new ArgumentOutOfRangeException(nameof(Weights), "Weights must be finite and between zero and one."); break; } + mass += weight; + } + if (error is null && (!Tools.IsFinite(mass) || !mass.AlmostEquals(1, 1E-8))) + error = new ArgumentOutOfRangeException(nameof(Weights), "Component and zero weights must sum to one."); + for (int i = 0; i < count && error is null; i++) + { + if (Distributions[i] is Normal normal) + error = normal.ValidateParameters(normal.Mu, normal.Sigma, false); + else + { + double[] parameters = Distributions[i].GetParameters; + error = Distributions[i].ValidateParameters(parameters, false); + } + if (error is null && IsZeroInflated && Weights[i] > 0) + { + double logMass = Distributions[i] is Normal zeroNormal + ? zeroNormal.LogCCDFAtZero() : Distributions[i].LogCCDF(0); + if (!Tools.IsFinite(logMass) || logMass > 0) + error = new ArgumentOutOfRangeException(nameof(Distributions), "Each active component must have positive probability above zero."); + } + } + } + if (error != null) throw error; + } /// /// Refreshes validity and cached results after zero-inflation configuration changes. @@ -1010,6 +1070,30 @@ InvalidOperationException CreateImpossibleRowException(int rowIndex, double valu /// public override double PDF(double x) => Math.Exp(LogPDF(x)); + /// Reuses the exact logarithm of a weight read at its existing post-callback evaluation point. + /// The component index. + /// The live weight already read after the component callback. + /// The value of for the supplied weight. + /// Entries are immutable and published with release/acquire semantics. No other live weight + /// is read, and field-based deserialization starts with an empty transient cache. + private double LogWeight(int index, double weight) + { + var cache = Volatile.Read(ref _logWeightCache); + if (cache is null || index >= cache.Length) + { + var expanded = new WeightLogEntry?[_weights.Length]; + if (cache is not null) Array.Copy(cache, expanded, cache.Length); + Volatile.Write(ref _logWeightCache, expanded); + cache = expanded; + } + long bits = BitConverter.DoubleToInt64Bits(weight); + var entry = Volatile.Read(ref cache[index]); + if (entry is not null && entry.WeightBits == bits) return entry.LogValue; + double log = Math.Log(weight); + Volatile.Write(ref cache[index], new WeightLogEntry(bits, log)); + return log; + } + /// public override double LogPDF(double x) { @@ -1020,7 +1104,7 @@ public override double LogPDF(double x) { if (Weights[i] == 0) continue; double log = IsZeroInflated ? PositiveConditionalLogPDF(i, x) : Distributions[i].LogPDF(x); - total = DistributionNumerics.LogSum(total, Math.Log(Weights[i]) + log); + total = DistributionNumerics.LogSum(total, LogWeight(i, Weights[i]) + log); } return total; } diff --git a/Numerics/Distributions/Univariate/Normal.cs b/Numerics/Distributions/Univariate/Normal.cs index 41ba6144..4ebb73cf 100644 --- a/Numerics/Distributions/Univariate/Normal.cs +++ b/Numerics/Distributions/Univariate/Normal.cs @@ -64,6 +64,11 @@ public Normal(double mean, double standardDeviation) private double _mu; private double _sigma; + private static readonly double _logSqrt2PI = Math.Log(Tools.Sqrt2PI); + [NonSerialized] private double _logSigma; + [NonSerialized] private volatile bool _logSigmaInitialized; + [NonSerialized] private double _logSurvivalAtZero; + [NonSerialized] private volatile bool _logSurvivalAtZeroInitialized; /// /// Gets and sets the location parameter µ (Mu). @@ -75,6 +80,7 @@ public double Mu { _parametersValid = ValidateParameters(value, Sigma, false) is null; _mu = value; + _logSurvivalAtZeroInitialized = false; } } @@ -89,6 +95,9 @@ public double Sigma if (value < 1E-16 && Math.Sign(value) != -1) value = 1E-16; _parametersValid = ValidateParameters(Mu, value, false) is null; _sigma = value; + _logSigma = Math.Log(value); + _logSigmaInitialized = true; + _logSurvivalAtZeroInitialized = false; } } @@ -331,6 +340,38 @@ public double[] LinearMomentsFromParameters(IList parameters) /// public Tuple GetParameterConstraints(IList sample) + { + DistributionNumerics.ValidateSample(sample, 4); + return DistributionNumerics.PreferLegacyConstraints( + () => GetLegacyParameterConstraints(sample), () => GetRobustParameterConstraints(sample)); + } + + /// Preserves the established initialization and family-specific prior envelope. + /// The validated observations. + /// The legacy initial values and lower and upper bounds. + private Tuple GetLegacyParameterConstraints(IList sample) + { + var initialVals = new double[NumberOfParameters]; + var lowerVals = new double[NumberOfParameters]; + var upperVals = new double[NumberOfParameters]; + // Estimate initial values using the method of moments (a.k.a product moments). + var moments = Statistics.ProductMoments(sample); + initialVals[0] = moments[0]; + initialVals[1] = moments[1]; + // Get bounds of mean + if (initialVals[0] == 0d) initialVals[0] = Tools.DoubleMachineEpsilon; + lowerVals[0] = -Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); + upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); + // Get bounds of standard deviation + lowerVals[1] = Tools.DoubleMachineEpsilon; + upperVals[1] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[1]) + 1d)); + return new Tuple(initialVals, lowerVals, upperVals); + } + + /// Handles samples whose legacy initialization or bounds are not finite or outside ordered bounds. + /// The validated observations. + /// Finite initial values and bounds from the hardened initialization path. + internal Tuple GetRobustParameterConstraints(IList sample) { DistributionNumerics.ValidateSample(sample, 4); // Scale before computing sample moments so squaring large observations cannot overflow. @@ -394,7 +435,13 @@ public override double LogPDF(double x) { if (!_parametersValid) ValidateParameters(Mu, Sigma, true); double z = DistributionNumerics.Standardize(x, Mu, Sigma); - return -0.5d * z * z - Math.Log(Sigma) - Math.Log(Tools.Sqrt2PI); + // Field-based deserialization does not invoke the scale setter or restore transient caches. + if (!_logSigmaInitialized) + { + _logSigma = Math.Log(Sigma); + _logSigmaInitialized = true; + } + return -0.5d * z * z - _logSigma - _logSqrt2PI; } /// @@ -427,6 +474,21 @@ public override double LogCCDF(double x) return DistributionNumerics.NormalLogSurvival(DistributionNumerics.Standardize(x, Mu, Sigma)); } + /// Gets the existing log probability above zero, cached until either parameter changes. + /// The value of at zero. + /// Thrown when the distribution parameters are invalid. + /// The transient cache is initialized lazily after field-based deserialization. + internal double LogCCDFAtZero() + { + if (!_parametersValid) ValidateParameters(Mu, Sigma, true); + if (!_logSurvivalAtZeroInitialized) + { + _logSurvivalAtZero = LogCCDF(0d); + _logSurvivalAtZeroInitialized = true; + } + return _logSurvivalAtZero; + } + private static readonly double[] a = [3.3871328727963666080, 1.3314166789178437745e+2, 1.9715909503065514427e+3, 1.3731693765509461125e+4, 4.5921953931549871457e+4, 6.7265770927008700853e+4, 3.3430575583588128105e+4, 2.5090809287301226727e+3]; private static readonly double[] b = [1.0, 4.2313330701600911252e+1, 6.8718700749205790830e+2, 5.3941960214247511077e+3, 2.1213794301586595867e+4, 3.9307895800092710610e+4, 2.8729085735721942674e+4, 5.2264952788528545610e+3]; private static readonly double[] c = [1.42343711074968357734, 4.63033784615654529590, 5.76949722146069140550, 3.64784832476320460504, 1.27045825245236838258, 2.41780725177450611770e-1, 2.27238449892691845833e-2, 7.74545014278341407640e-4]; diff --git a/Numerics/Distributions/Univariate/PearsonTypeIII.cs b/Numerics/Distributions/Univariate/PearsonTypeIII.cs index ba9c7a1c..de624393 100644 --- a/Numerics/Distributions/Univariate/PearsonTypeIII.cs +++ b/Numerics/Distributions/Univariate/PearsonTypeIII.cs @@ -150,15 +150,24 @@ private double LogTail(double x, bool upper) /// Forms the unit gamma coordinate in centered form for large shape. private double UnitGammaValue(double x, double z) + => UnitGammaValue(Mu, Sigma, Gamma, x, z); + + /// Forms the unit gamma coordinate directly from validated Pearson parameters. + private static double UnitGammaValue(double mu, double sigma, double gamma, double x, double z) { - if (x == Xi) return 0d; - return Math.Abs(Gamma) < 1d ? Alpha * (1d + (Gamma / 2d) * z) : DistributionNumerics.Standardize(x, Xi, Beta); + if (x == mu - sigma * (2d / gamma)) return 0d; + return Math.Abs(gamma) < 1d ? Math.Pow(2d / gamma, 2d) * (1d + (gamma / 2d) * z) + : DistributionNumerics.Standardize(x, mu - sigma * (2d / gamma), 0.5d * sigma * gamma); } /// Bounds local tail-expansion error while retaining every nonzero skew. private bool UseLocalTailExpansion(double z) + => UseLocalTailExpansion(Gamma, z); + + /// Bounds local tail-expansion error directly from a validated skew. + private static bool UseLocalTailExpansion(double gamma, double z) { - return Math.Abs(Gamma) <= 1E-3 && Math.Abs(Gamma) * Math.Pow(1d + Math.Abs(z), 3d) <= 1E-3; + return Math.Abs(gamma) <= 1E-3 && Math.Abs(gamma) * Math.Pow(1d + Math.Abs(z), 3d) <= 1E-3; } /// Avoids cancellation in centered gamma quantiles only where the skew expansion is accurate. @@ -538,7 +547,46 @@ public double[] LinearMomentsFromParameters(IList parameters) public Tuple GetParameterConstraints(IList sample) { DistributionNumerics.ValidateSample(sample, 4); - var normal = new Normal().GetParameterConstraints(sample); + return DistributionNumerics.PreferLegacyConstraints( + () => GetLegacyParameterConstraints(sample), () => GetRobustParameterConstraints(sample)); + } + + /// Preserves the established initialization and family-specific prior envelope. + /// The validated observations. + /// The legacy initial values and lower and upper bounds. + private Tuple GetLegacyParameterConstraints(IList sample) + { + var initialVals = new double[NumberOfParameters]; + var lowerVals = new double[NumberOfParameters]; + var upperVals = new double[NumberOfParameters]; + // Get initial values + var moments = Statistics.ProductMoments(sample); + initialVals = moments.Subset(0, 2); + // Get bounds of mean + lowerVals[0] = -Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[0]) + 1d)); + upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[0]) + 1d)); + // Get bounds of standard deviation + lowerVals[1] = Tools.DoubleMachineEpsilon; + upperVals[1] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[1]) + 1d)); + // Get bounds of skew + lowerVals[2] = -6d; + upperVals[2] = 6d; + + // Correct initial value of skew if necessary + if (initialVals[2] <= lowerVals[2] || initialVals[2] >= upperVals[2]) + { + initialVals[2] = 0.01; + } + return new Tuple(initialVals, lowerVals, upperVals); + } + + /// Handles samples whose legacy initialization or bounds are not finite or outside ordered bounds. + /// The validated observations. + /// Finite initial values and bounds from the hardened initialization path. + internal Tuple GetRobustParameterConstraints(IList sample) + { + DistributionNumerics.ValidateSample(sample, 4); + var normal = new Normal().GetRobustParameterConstraints(sample); var scaled = new double[sample.Count]; for (int i = 0; i < sample.Count; i++) scaled[i] = DistributionNumerics.Standardize(sample[i], normal.Item1[0], normal.Item1[1]); double skew = Statistics.ProductMoments(scaled)[2]; @@ -586,21 +634,34 @@ public override double PDF(double x) public override double LogPDF(double x) { if (!_parametersValid) ValidateParameters(Mu, Sigma, Gamma, true); - if (x < Minimum || x > Maximum || double.IsInfinity(x)) return double.NegativeInfinity; - double z = DistributionNumerics.Standardize(x, Mu, Sigma); - if (Gamma == 0d) return -0.5d * z * z - Math.Log(Sigma) - Math.Log(Tools.Sqrt2PI); - if (UseLocalTailExpansion(z)) + return LogPDF(Mu, Sigma, Gamma, x); + } + + /// Evaluates a Pearson log density from parameters already validated by an equivalent public contract. + /// The finite mean. + /// The finite positive standard deviation. + /// The finite skew in the supported interval. + /// The value at which to evaluate the density. + /// The log density, including the established support and endpoint limits. + internal static double LogPDF(double mu, double sigma, double gamma, double x) + { + if (x < (gamma > 0d ? mu - sigma * (2d / gamma) : double.NegativeInfinity) + || x > (gamma < 0d ? mu - sigma * (2d / gamma) : double.PositiveInfinity) + || double.IsInfinity(x)) return double.NegativeInfinity; + double z = DistributionNumerics.Standardize(x, mu, sigma); + if (gamma == 0d) return -0.5d * z * z - Math.Log(sigma) - Math.Log(Tools.Sqrt2PI); + if (UseLocalTailExpansion(gamma, z)) { // Log gamma density expanded in skew; the gate bounds the omitted fourth-order term. - double z2 = z * z, g2 = Gamma * Gamma; - double correction = Gamma * z * (z2 / 6d - 0.5d) + double z2 = z * z, g2 = gamma * gamma; + double correction = gamma * z * (z2 / 6d - 0.5d) + g2 * (-z2 * z2 / 16d + z2 / 8d - 1d / 48d) - + g2 * Gamma * z * z2 * (z2 / 40d - 1d / 24d) + + g2 * gamma * z * z2 * (z2 / 40d - 1d / 24d) + g2 * g2 * z2 * z2 * (1d / 64d - z2 / 96d); - return -0.5d * z2 - Math.Log(Sigma) - Math.Log(Tools.Sqrt2PI) + correction; + return -0.5d * z2 - Math.Log(sigma) - Math.Log(Tools.Sqrt2PI) + correction; } - double unit = UnitGammaValue(x, z); - return DistributionNumerics.GammaLogDensity(Alpha, unit) - Math.Log(Sigma) - Math.Log(Math.Abs(Gamma) / 2d); + double unit = UnitGammaValue(mu, sigma, gamma, x, z); + return DistributionNumerics.GammaLogDensity(Math.Pow(2d / gamma, 2d), unit) - Math.Log(sigma) - Math.Log(Math.Abs(gamma) / 2d); } /// diff --git a/Numerics/Distributions/Univariate/Weibull.cs b/Numerics/Distributions/Univariate/Weibull.cs index cdb9b23e..c40e16ec 100644 --- a/Numerics/Distributions/Univariate/Weibull.cs +++ b/Numerics/Distributions/Univariate/Weibull.cs @@ -304,10 +304,96 @@ public override void SetParameters(IList parameters) } /// - /// Requires finite, strictly positive, nonconstant observations. Returned bounds - /// retain a representable small scale and the existing Weibull MLE initialization. + /// Requires finite, nonconstant observations. Preserves the legacy initializer's treatment + /// of nonpositive observations whenever its initial values and rounded prior bounds are usable. + /// The exceptional-input fallback requires positive observations and retains representable small scales. + /// Density support is unchanged. /// The sample or a finite feasible initialization is invalid. public Tuple GetParameterConstraints(IList sample) + { + DistributionNumerics.ValidateSample(sample, 2); + return DistributionNumerics.PreferLegacyConstraints( + () => GetLegacyParameterConstraints(sample), () => GetRobustParameterConstraints(sample)); + } + + /// Preserves the established initialization and family-specific prior envelope. + /// The validated observations. + /// The legacy initial values and lower and upper bounds. + private Tuple GetLegacyParameterConstraints(IList sample) + { + var initialVals = new double[NumberOfParameters]; + var lowerVals = new double[NumberOfParameters]; + var upperVals = new double[NumberOfParameters]; + // Get initial values + initialVals = LegacyConstraintSolveMLE(sample); + // Get bounds of scale + lowerVals[0] = Tools.DoubleMachineEpsilon; + upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[0]) + 1d)); + // Get bounds of shape + lowerVals[1] = Tools.DoubleMachineEpsilon; + upperVals[1] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[1]) + 1d)); + return new Tuple(initialVals, lowerVals, upperVals); + } + + /// Retains the established constraint initializer arithmetic for ordinary samples. + /// Observations. + /// The legacy initial parameter values. + private double[] LegacyConstraintSolveMLE(IList samples) + { + double n = samples.Count; + if (n <= 1d) + { + throw new Exception("Observations not sufficient. There must be more than 1 data point."); + } + + double s1 = 0d; + double s2 = 0d; + double s3 = 0d; + double previousC = int.MinValue; + double QofC = 0d; + double c = 10d; // shape + double b = 0d; // scale + + // solve for the shape parameter + while (Math.Abs(c - previousC) >= 0.0001d) + { + s1 = 0d; + s2 = 0d; + s3 = 0d; + foreach (double x in samples) + { + if (x > 0d) + { + s1 += Math.Log(x); + s2 += Math.Pow(x, c); + s3 += Math.Pow(x, c) * Math.Log(x); + } + } + + QofC = n * s2 / (n * s3 - s1 * s2); + previousC = c; + c = (c + QofC) / 2d; + } + + // solve for scale + foreach (double x in samples) + { + if (x > 0d) + { + b += Math.Pow(x, c); + } + } + + b = Math.Pow(b / n, 1d / c); + + // return parameters + return [b, c]; + } + + /// Handles samples whose legacy initialization or bounds are not finite or outside ordered bounds. + /// The validated observations. + /// Finite initial values and bounds from the hardened initialization path. + private Tuple GetRobustParameterConstraints(IList sample) { DistributionNumerics.ValidateSample(sample, 2, true); var lowerVals = new double[NumberOfParameters]; diff --git a/Numerics/Numerics.csproj b/Numerics/Numerics.csproj index c4e9454a..a02c18ec 100644 --- a/Numerics/Numerics.csproj +++ b/Numerics/Numerics.csproj @@ -46,7 +46,7 @@ runtime; build; native; contentfiles; analyzers - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/Test_Numerics/Distributions/Univariate/Test_CompetingRisksConfigurationCache.cs b/Test_Numerics/Distributions/Univariate/Test_CompetingRisksConfigurationCache.cs new file mode 100644 index 00000000..a930493f --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Test_CompetingRisksConfigurationCache.cs @@ -0,0 +1,209 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +#if NETFRAMEWORK +using System.Runtime.Serialization.Formatters.Binary; +#endif +using System.Xml.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Data; +using Numerics.Data.Statistics; +using Numerics.Distributions; + +namespace Distributions.Univariate +{ + /// Guards canonical competing-risk cache invalidation while removing repeated built-in Weibull XML work. + [TestClass] + public class Test_CompetingRisksConfigurationCache + { +#if NET8_0_OR_GREATER + /// Unchanged below-support CDF calls allocate only the same validation/support work as LogPDF. + [TestMethod] + public void CDF_UnchangedWeibullsAvoidConfigurationSerializationAllocations() + { + CompetingRisks risks = Create(); + for (int i = 0; i < 100; i++) { _ = risks.CDF(-1d); _ = risks.LogPDF(-1d); } + long before = GC.GetAllocatedBytesForCurrentThread(); + for (int i = 0; i < 100; i++) _ = risks.LogPDF(-1d); + long supportWork = GC.GetAllocatedBytesForCurrentThread() - before; + before = GC.GetAllocatedBytesForCurrentThread(); + for (int i = 0; i < 100; i++) _ = risks.CDF(-1d); + long cdfWork = GC.GetAllocatedBytesForCurrentThread() - before; + Assert.AreEqual(supportWork, cdfWork, + $"Repeated CDF allocated {cdfWork} bytes; matching validation/support work allocated {supportWork} bytes."); + } +#endif + + /// Ordered scalar mutations, replacement, and count changes retain the original canonical invalidation decision. + [TestMethod] + public void Configuration_ObservesWeibullMutationsAndReplacement() + { + CompetingRisks risks = Create(); + AssertCanonicalInvalidation(risks, () => ((Weibull)risks.Distributions[0]).Lambda = 7d); + AssertCanonicalInvalidation(risks, () => ((Weibull)risks.Distributions[1]).Kappa = 3d); + AssertCanonicalInvalidation(risks, () => risks.SetParameters(new[] { 8d, 1.5d, 11d, 2.5d })); + AssertCanonicalInvalidation(risks, () => risks.SetParameters(new UnivariateDistributionBase[] + { new Weibull(8d, 1.5d), new Weibull(11d, 2.5d) })); + AssertCanonicalInvalidation(risks, () => risks.SetParameters(new UnivariateDistributionBase[] + { new Weibull(11d, 2.5d), new Weibull(8d, 1.5d) })); + AssertCanonicalInvalidation(risks, () => risks.SetParameters(new UnivariateDistributionBase[] + { new Weibull(11d, 2.5d) })); + } + + /// Flags, seed, matrix shape, live values, and signed zeros follow canonical string equality on each runtime. + [TestMethod] + public void Configuration_ObservesFlagsSeedAndEveryMatrixValue() + { + CompetingRisks risks = Create(); + AssertCanonicalInvalidation(risks, () => risks.MinimumOfRandomVariables = false); + AssertCanonicalInvalidation(risks, () => risks.Dependency = Probability.DependencyType.Independent); + AssertCanonicalInvalidation(risks, () => risks.PRNGSeed = 9876); + AssertCanonicalInvalidation(risks, () => risks.XTransform = Transform.Logarithmic); + AssertCanonicalInvalidation(risks, () => risks.ProbabilityTransform = Transform.None); + AssertCanonicalInvalidation(risks, () => risks.CorrelationMatrix = new[,] { { 1d, 0d }, { 0d, 1d } }); + AssertCanonicalInvalidation(risks, () => risks.CorrelationMatrix[0, 1] = -0d); + AssertCanonicalInvalidation(risks, () => risks.CorrelationMatrix[1, 0] = 0.3d); + AssertCanonicalInvalidation(risks, () => risks.CorrelationMatrix = (double[,])risks.CorrelationMatrix.Clone()); + AssertCanonicalInvalidation(risks, () => risks.CorrelationMatrix = new double[1, 3]); + AssertCanonicalInvalidation(risks, () => risks.CorrelationMatrix = null); + } + + /// Matrix lower bounds do not add an invalidation distinction absent from the original canonical state. + [TestMethod] + public void Configuration_RetainsMatrixEnumerationSemantics() + { + CompetingRisks risks = Create(); + risks.CorrelationMatrix = new[,] { { 1d, 0.2d }, { 0.2d, 1d } }; + AssertCanonicalInvalidation(risks, () => + { + var matrix = (double[,])Array.CreateInstance(typeof(double), new[] { 2, 2 }, new[] { 2, 3 }); + matrix[2, 3] = 1d; matrix[2, 4] = 0.2d; + matrix[3, 3] = 0.2d; matrix[3, 4] = 1d; + risks.CorrelationMatrix = matrix; + }); + } + + /// Derived Weibull and nested/custom children still execute their original XML callbacks on every refresh. + [TestMethod] + public void Configuration_PreservesDerivedAndNestedFallbackCallbacks() + { + var child = new CountingWeibull(5d, 1.4d); + var risks = new CompetingRisks(new UnivariateDistributionBase[] { child }); + _ = risks.InverseCDF(0d); + int before = child.SerializationCalls; + _ = risks.InverseCDF(0d); + _ = risks.InverseCDF(0d); + Assert.AreEqual(before + 2, child.SerializationCalls); + child.Token = "changed"; + _ = risks.InverseCDF(0d); + StringAssert.Contains(CachedConfiguration(risks), "changed"); + var nested = new Mixture(new[] { 1d }, new UnivariateDistributionBase[] { child }); + var nestedChild = (CountingWeibull)nested.Distributions[0]; + risks.SetParameters(new UnivariateDistributionBase[] { nested }); + _ = risks.InverseCDF(0d); + before = nestedChild.SerializationCalls; + _ = risks.InverseCDF(0d); + Assert.AreEqual(before + 1, nestedChild.SerializationCalls); + risks.SetParameters(new UnivariateDistributionBase[] { new Weibull(5d, 1.4d) }); + AssertCanonicalInvalidation(risks, () => ((Weibull)risks.Distributions[0]).Lambda = 6d); + } + + /// Correlated CDF evaluations retain the exact sequence of the unchanged generic configuration path. + [TestMethod] + public void CDF_CorrelatedWeibullsMatchGenericRefreshExactly() + { + CompetingRisks fast = Create(); + var generic = new CompetingRisks(new UnivariateDistributionBase[] + { new CountingWeibull(5d, 1.4d), new CountingWeibull(12d, 2.3d) }); + foreach (CompetingRisks risks in new[] { fast, generic }) + { + risks.Dependency = Probability.DependencyType.CorrelationMatrix; + risks.CorrelationMatrix = new[,] { { 1d, 0.35d }, { 0.35d, 1d } }; + risks.PRNGSeed = 417; + } + for (int round = 0; round < 2; round++) + { + foreach (double x in new[] { 0.5d, 1d, 3d, 8d, 15d }) + Assert.AreEqual(BitConverter.DoubleToInt64Bits(generic.CDF(x)), BitConverter.DoubleToInt64Bits(fast.CDF(x))); + ((Weibull)fast.Distributions[0]).Lambda = 7d; + ((Weibull)generic.Distributions[0]).Lambda = 7d; + } + } + + /// Clone, XML, and supported legacy binary serialization restore cold snapshots without changing canonical state. + [TestMethod] + public void Configuration_SerializationRestoresColdSnapshot() + { + CompetingRisks original = Create(); + _ = original.InverseCDF(0d); + string expected = CachedConfiguration(original); + var copies = new List { (CompetingRisks)original.Clone(), + (CompetingRisks)UnivariateDistributionFactory.CreateDistribution(original.ToXElement()) }; +#if NETFRAMEWORK + copies.Add(RoundTrip(original)); +#endif + foreach (CompetingRisks copy in copies) + { + _ = copy.InverseCDF(0d); + Assert.AreEqual(expected, CachedConfiguration(copy)); + AssertCanonicalInvalidation(copy, () => ((Weibull)copy.Distributions[0]).Lambda = 7d); + } + } + + private static readonly FieldInfo Configuration = typeof(CompetingRisks).GetField("_cachedConfiguration", BindingFlags.Instance | BindingFlags.NonPublic); + private static readonly FieldInfo[] DerivedFlags = + { + typeof(CompetingRisks).GetField("_momentsComputed", BindingFlags.Instance | BindingFlags.NonPublic), + typeof(CompetingRisks).GetField("_empiricalCDFCreated", BindingFlags.Instance | BindingFlags.NonPublic), + typeof(CompetingRisks).GetField("_mvnCreated", BindingFlags.Instance | BindingFlags.NonPublic) + }; + + private static string CachedConfiguration(CompetingRisks risks) => (string)Configuration.GetValue(risks); + + private static void AssertCanonicalInvalidation(CompetingRisks risks, Action mutate) + { + _ = risks.InverseCDF(0d); + string before = CachedConfiguration(risks); + mutate(); + string expected = DistributionNumerics.ConfigurationState(risks); + // Set sentinels after setters so this checks refresh's canonical decision independently of setter invalidation. + foreach (FieldInfo flag in DerivedFlags) flag.SetValue(risks, true); + _ = risks.InverseCDF(0d); + Assert.AreEqual(expected, CachedConfiguration(risks)); + foreach (FieldInfo flag in DerivedFlags) + Assert.AreEqual(expected == before, (bool)flag.GetValue(risks), flag.Name); + } + + private static CompetingRisks Create() => new CompetingRisks(new UnivariateDistributionBase[] + { new Weibull(5d, 1.4d), new Weibull(12d, 2.3d) }) { Dependency = Probability.DependencyType.PerfectlyPositive }; + +#if NETFRAMEWORK + private static CompetingRisks RoundTrip(CompetingRisks risks) + { + using var stream = new MemoryStream(); + var serializer = new BinaryFormatter(); + serializer.Serialize(stream, risks); + stream.Position = 0; + return (CompetingRisks)serializer.Deserialize(stream); + } +#endif + + private sealed class CountingWeibull : Weibull + { + internal int SerializationCalls; + internal string Token = "original"; + internal CountingWeibull(double scale, double shape) : base(scale, shape) { } + /// + public override XElement ToXElement() + { + SerializationCalls++; + XElement element = base.ToXElement(); + element.SetAttributeValue("CustomToken", Token); + return element; + } + /// + public override UnivariateDistributionBase Clone() => new CountingWeibull(Lambda, Kappa) { Token = Token }; + } + } +} diff --git a/Test_Numerics/Distributions/Univariate/Test_CompetingRisksDensityStepCache.cs b/Test_Numerics/Distributions/Univariate/Test_CompetingRisksDensityStepCache.cs new file mode 100644 index 00000000..16057128 --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Test_CompetingRisksDensityStepCache.cs @@ -0,0 +1,189 @@ +using System; +using System.Collections.Generic; +using System.IO; +#if NETFRAMEWORK +using System.Runtime.Serialization.Formatters.Binary; +#endif +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Data; +using Numerics.Data.Statistics; +using Numerics.Distributions; + +namespace Distributions.Univariate +{ + /// Guards reuse of the identical built-in Weibull derivative step without changing dependent densities. + [TestClass] + public class Test_CompetingRisksDensityStepCache + { + /// Live parameter, component, and configuration changes retain the generic derivative's exact results. + [TestMethod] + public void LogPDF_WeibullStepObservesLiveMutations() + { + var children = new UnivariateDistributionBase[] { new Weibull(5d, 1.4d), new Weibull(12d, 2.3d) }; + var risks = Create(children); + AssertGenericParity(risks); + ((Weibull)children[0]).Lambda = 7d; + AssertGenericParity(risks); + ((Weibull)children[1]).Kappa = 3d; + AssertGenericParity(risks); + risks.SetParameters(new[] { 8d, 1.5d, 11d, 2.5d }); + AssertGenericParity(risks); + children[0] = new Weibull(4d, 1d); + AssertGenericParity(risks); + risks.SetParameters(new UnivariateDistributionBase[] { new Weibull(11d, 2.5d), new Weibull(4d, 1d) }); + AssertGenericParity(risks); + risks.MinimumOfRandomVariables = false; + risks.PRNGSeed = 9876; + risks.XTransform = Transform.Logarithmic; + risks.ProbabilityTransform = Transform.None; + risks.CorrelationMatrix[0, 1] = risks.CorrelationMatrix[1, 0] = .6d; + AssertGenericParity(risks); + risks.Dependency = Probability.DependencyType.PerfectlyPositive; + AssertGenericParity(risks); + } + + /// Cached steps never bypass live validity checks and recover after invalid component parameters are repaired. + [TestMethod] + public void LogPDF_WeibullStepPreservesInvalidRecovery() + { + var risks = Create(new UnivariateDistributionBase[] { new Weibull(5d, 1.4d), new Weibull(12d, 2.3d) }); + AssertGenericParity(risks); + var child = (Weibull)risks.Distributions[0]; + child.Lambda = double.NaN; + Assert.ThrowsExactly(() => risks.LogPDF(3d)); + child.Lambda = 7d; + AssertGenericParity(risks); + child.Kappa = 0d; + Assert.ThrowsExactly(() => risks.LogPDF(3d)); + child.Kappa = 2d; + AssertGenericParity(risks); + } + + /// Unusable component widths keep the observation-dependent fallback, and unresolvable steps still throw. + [TestMethod] + public void LogPDF_WeibullStepPreservesFallbackAndExceptions() + { + var risks = Create(new UnivariateDistributionBase[] { new Weibull(1d, .0001d), new Weibull(2d, .0001d) }); + risks.Dependency = Probability.DependencyType.PerfectlyPositive; + AssertGenericParity(risks, new[] { 0d, .25d, 1d, 2d, 100d, 1E100d }); + // Both IQRs are finite, but their component-derived step underflows to zero. + risks.SetParameters(new[] { double.Epsilon, 1d, double.Epsilon, 1d }); + _ = risks.CDF(-1d); + for (int i = 0; i < 3; i++) + Assert.ThrowsExactly(() => risks.LogPDF(0d)); + risks.SetParameters(new[] { 5d, 1.4d, 12d, 2.3d }); + AssertGenericParity(risks); + } + + /// Derived Weibull callbacks retain their original order, repeated evaluation, and exception behavior. + [TestMethod] + public void LogPDF_DerivedWeibullsRetainQuartileCallbacks() + { + var calls = new List(); + var first = new ObservedWeibull(5d, 1.4d) { Name = "first", Calls = calls }; + var second = new ObservedWeibull(12d, 2.3d) { Name = "second", Calls = calls }; + var risks = Create(new UnivariateDistributionBase[] { new Weibull(5d, 1.4d), new Weibull(12d, 2.3d) }); + AssertGenericParity(risks); + risks.SetParameters(new UnivariateDistributionBase[] { first, second }); + for (int i = 0; i < 3; i++) + { + calls.Clear(); + _ = risks.LogPDF(3d); + CollectionAssert.AreEqual(new[] { "first:upper", "first:lower", "second:upper", "second:lower" }, calls); + } + first.ThrowOnUpper = true; + calls.Clear(); + var error = Assert.ThrowsExactly(() => risks.LogPDF(3d)); + Assert.AreEqual("quartile callback", error.Message); + CollectionAssert.AreEqual(new[] { "first:upper" }, calls); + first.ThrowOnUpper = false; + _ = risks.LogPDF(3d); + } + + /// Concurrent readers can lazily publish a step for an already initialized unchanged configuration. + [TestMethod] + public void LogPDF_WeibullStepInitializesForConcurrentReaders() + { + var risks = Create(new UnivariateDistributionBase[] { new Weibull(5d, 1.4d), new Weibull(12d, 2.3d) }); + risks.Dependency = Probability.DependencyType.PerfectlyPositive; + _ = risks.CDF(-1d); + var generic = GenericCopy(risks); + long expected = BitConverter.DoubleToInt64Bits(generic.LogPDF(3d)); + Parallel.For(0, 32, i => Assert.AreEqual(expected, BitConverter.DoubleToInt64Bits(risks.LogPDF(3d)))); + } + + /// Clone, XML, and actual legacy binary round trips preserve cold-cache evaluation and subsequent mutations. + [TestMethod] + public void LogPDF_WeibullStepSurvivesSerialization() + { + var original = Create(new UnivariateDistributionBase[] { new Weibull(5d, 1.4d), new Weibull(12d, 2.3d) }); + AssertGenericParity(original); + var copies = new List { (CompetingRisks)original.Clone(), + (CompetingRisks)UnivariateDistributionFactory.CreateDistribution(original.ToXElement()) }; +#if NETFRAMEWORK + using var stream = new MemoryStream(); + var serializer = new BinaryFormatter(); + serializer.Serialize(stream, original); + stream.Position = 0; + copies.Add((CompetingRisks)serializer.Deserialize(stream)); +#endif + foreach (CompetingRisks copy in copies) + { + AssertGenericParity(copy); + ((Weibull)copy.Distributions[0]).Lambda = 7d; + AssertGenericParity(copy); + } + } + + private static CompetingRisks Create(UnivariateDistributionBase[] children) => new CompetingRisks(children) + { + Dependency = Probability.DependencyType.CorrelationMatrix, + CorrelationMatrix = new[,] { { 1d, .35d }, { .35d, 1d } }, + PRNGSeed = 417 + }; + + private static CompetingRisks GenericCopy(CompetingRisks risks) + { + var children = new UnivariateDistributionBase[risks.Distributions.Count]; + for (int i = 0; i < children.Length; i++) + { + var child = (Weibull)risks.Distributions[i]; + children[i] = new ObservedWeibull(child.Lambda, child.Kappa); + } + return new CompetingRisks(children) + { + MinimumOfRandomVariables = risks.MinimumOfRandomVariables, + Dependency = risks.Dependency, + CorrelationMatrix = (double[,])risks.CorrelationMatrix.Clone(), + PRNGSeed = risks.PRNGSeed, + XTransform = risks.XTransform, + ProbabilityTransform = risks.ProbabilityTransform + }; + } + + private static void AssertGenericParity(CompetingRisks risks, double[] observations = null) + { + var generic = GenericCopy(risks); + observations ??= new[] { 0d, .5d, 1d, 3d, 8d, 15d }; + for (int repeat = 0; repeat < 3; repeat++) + foreach (double x in observations) + Assert.AreEqual(BitConverter.DoubleToInt64Bits(generic.LogPDF(x)), BitConverter.DoubleToInt64Bits(risks.LogPDF(x)), $"x={x:G17}"); + } + + private sealed class ObservedWeibull : Weibull + { + internal string Name; + internal List Calls; + internal bool ThrowOnUpper; + internal ObservedWeibull(double scale, double shape) : base(scale, shape) { } + /// + public override double InverseCDF(double probability) + { + Calls?.Add(Name + (probability == .75d ? ":upper" : ":lower")); + if (ThrowOnUpper && probability == .75d) throw new InvalidOperationException("quartile callback"); + return base.InverseCDF(probability); + } + } + } +} diff --git a/Test_Numerics/Distributions/Univariate/Test_CompetingRisksEvaluationAllocations.cs b/Test_Numerics/Distributions/Univariate/Test_CompetingRisksEvaluationAllocations.cs new file mode 100644 index 00000000..9296a2eb --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Test_CompetingRisksEvaluationAllocations.cs @@ -0,0 +1,81 @@ +using Numerics.Data.Statistics; +using Numerics.Distributions; + +namespace Test_Numerics.Distributions +{ + /// Protects live evaluation from temporary collection-wrapper allocations. + [TestClass] + public class Test_CompetingRisksEvaluationAllocations + { +#if NET6_0_OR_GREATER + /// Known out-of-support densities and CDFs allocate only the existing bounds enumeration. + [TestMethod] + public void OutsideSupport_AllocatesOnlyBoundsEnumeration() + { + var distribution = new CompetingRisks(new UnivariateDistributionBase[] + { + new Weibull(5d, 1.4d), new Weibull(12d, 2.3d) + }) + { + Dependency = Probability.DependencyType.CorrelationMatrix, + CorrelationMatrix = new[,] { { 1d, .35d }, { .35d, 1d } } + }; + for (int i = 0; i < 100; i++) { _ = distribution.LogPDF(-1d); _ = distribution.CDF(-1d); } + long before = GC.GetAllocatedBytesForCurrentThread(); + for (int i = 0; i < 200; i++) _ = distribution.Minimum; + long boundsAllocation = GC.GetAllocatedBytesForCurrentThread() - before; + before = GC.GetAllocatedBytesForCurrentThread(); + for (int i = 0; i < 100; i++) { _ = distribution.LogPDF(-1d); _ = distribution.CDF(-1d); } + long allocated = GC.GetAllocatedBytesForCurrentThread() - before; + Assert.AreEqual(boundsAllocation, allocated, "Live validation and collection access should add no allocations to the bounds enumeration."); + Assert.AreEqual(double.NegativeInfinity, distribution.LogPDF(-1d)); + Assert.AreEqual(0d, distribution.CDF(-1d)); + + // Allocation removal must retain validation before the support shortcut. + ((Weibull)distribution.Distributions[1]).Kappa = double.NaN; + Assert.ThrowsExactly(() => distribution.LogPDF(-1d)); + Assert.ThrowsExactly(() => distribution.CDF(-1d)); + } +#endif + + /// Derived owners retain every live support read even when their components are built-in Weibulls. + [TestMethod] + public void DerivedOwner_RetainsLiveSupportReads() + { + var distribution = new ObservedSupport(new UnivariateDistributionBase[] + { + new Weibull(5d, 1.4d), new Weibull(12d, 2.3d) + }) + { + Dependency = Probability.DependencyType.CorrelationMatrix, + CorrelationMatrix = new[,] { { 1d, .35d }, { .35d, 1d } } + }; + _ = distribution.LogPDF(3d); + _ = distribution.LogPDF(3d); + distribution.MinimumReads = distribution.MaximumReads = 0; + distribution.CdfCalls = 0; + _ = distribution.LogPDF(3d); + Assert.AreEqual(5, distribution.MinimumReads); + Assert.AreEqual(5, distribution.MaximumReads); + Assert.AreEqual(2, distribution.CdfCalls); + } + + /// Observes the virtual support extension points of a derived composite. + private sealed class ObservedSupport : CompetingRisks + { + internal int MinimumReads; + internal int MaximumReads; + internal int CdfCalls; + internal ObservedSupport(UnivariateDistributionBase[] distributions) : base(distributions) { } + + /// + public override double Minimum { get { MinimumReads++; return base.Minimum; } } + + /// + public override double Maximum { get { MaximumReads++; return base.Maximum; } } + + /// + public override double CDF(double x) { CdfCalls++; return base.CDF(x); } + } + } +} diff --git a/Test_Numerics/Distributions/Univariate/Test_DependentCompetingRisksRegressions.cs b/Test_Numerics/Distributions/Univariate/Test_DependentCompetingRisksRegressions.cs new file mode 100644 index 00000000..42c3a35f --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Test_DependentCompetingRisksRegressions.cs @@ -0,0 +1,57 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Data.Statistics; +using Numerics.Distributions; +using Numerics.Mathematics; + +namespace Distributions.Univariate +{ + /// Protects dependent candidate rejection and finite-support density behavior. + [TestClass] + public class Test_DependentCompetingRisksRegressions + { + /// A captured correlated-Weibull candidate remains rejectable without aborting estimation. + [TestMethod] + public void CorrelatedMinimum_UnresolvedCandidateReturnsNegativeInfinity() + { + var distribution = new CompetingRisks(new UnivariateDistributionBase[] + { + new Weibull(54.676968161948025d, 43.16940511169378d), + new Weibull(55.23342531523667d, 47.44412398606073d) + }) + { + MinimumOfRandomVariables = true, + Dependency = Probability.DependencyType.CorrelationMatrix, + CorrelationMatrix = new[,] { { 1d, .6d }, { .6d, 1d } } + }; + + // Captured from the unchanged BestFit correlated-minimum recovery fixture. + // The CDF derivative is negative at this rounded upper-tail plateau. + const double observation = 58.99784456776834d; + Assert.AreEqual(double.NegativeInfinity, distribution.LogPDF(observation)); + Assert.AreEqual(0d, distribution.PDF(observation)); + } + + /// A perfectly dependent unit-uniform composite retains unit density at and near its endpoints. + [TestMethod] + public void FiniteSupport_EndpointsAndAdjacentInteriorRetainOneSidedDensity() + { + var distribution = new CompetingRisks(new UnivariateDistributionBase[] + { + new Uniform(0d, 1d), + new Uniform(0d, 1d) + }) + { + Dependency = Probability.DependencyType.PerfectlyPositive + }; + + double lowerAdjacent = .25d * NumericalDerivative.CalculateStepSize(0d); + double upperAdjacent = 1d - .25d * NumericalDerivative.CalculateStepSize(1d); + foreach (double observation in new[] { 0d, lowerAdjacent, upperAdjacent, 1d }) + { + Assert.AreEqual(1d, distribution.PDF(observation), 0d, $"x={observation:G17}"); + Assert.AreEqual(0d, distribution.LogPDF(observation), 0d, $"x={observation:G17}"); + } + } + } +} diff --git a/Test_Numerics/Distributions/Univariate/Test_DistributionPerformanceRegressions.cs b/Test_Numerics/Distributions/Univariate/Test_DistributionPerformanceRegressions.cs new file mode 100644 index 00000000..3195d435 --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Test_DistributionPerformanceRegressions.cs @@ -0,0 +1,93 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Distributions; + +namespace Distributions.Univariate +{ + /// Allocation regressions for distribution numerical paths used in repeated fitting work. + [TestClass] + public class Test_DistributionPerformanceRegressions + { + /// Receives diagnostic allocation output from the focused tests. + public TestContext TestContext { get; set; } + +#if NET8_0_OR_GREATER + /// The small-shape gamma series reuses its immutable zeta constants. + [TestMethod] + public void GammaLogTails_RepeatedCallsDoNotAllocateZetaTables() + { + double checksum = 0d; + foreach (double shape in new[] { 0.25d, 0.5d, 2d }) + checksum += DistributionNumerics.GammaLogCDF(shape, 1d); + + long before = GC.GetAllocatedBytesForCurrentThread(); + for (int repetition = 0; repetition < 100; repetition++) + { + checksum += DistributionNumerics.GammaLogCDF(0.25d, 1d); + checksum += DistributionNumerics.GammaLogCDF(0.5d, 1d); + checksum += DistributionNumerics.GammaLogCDF(2d, 1d); + } + long allocated = GC.GetAllocatedBytesForCurrentThread() - before; + + Assert.IsTrue(double.IsFinite(checksum)); + Assert.AreEqual(0L, allocated, $"Repeated gamma log-tail evaluation allocated {allocated} bytes."); + } +#endif + + /// Preserves the original literals, ordered runtime sums, fallback boundary and invalid-index behavior. + [TestMethod] + public void ZetaInteger_PrecomputedRangeRetainsBitwiseValues() + { + double[] literals = { 1.6449340668482264365, 1.2020569031595942854, 1.0823232337111381915, + 1.0369277551433699263, 1.0173430619844491397, 1.0083492773819228268, + 1.0040773561979443394, 1.0020083928260822144, 1.0009945751278180853, + 1.0004941886041194646, 1.0002460865533080483, 1.0001227133475784891, + 1.0000612481350587048, 1.0000305882363070205, 1.0000152822594086519 }; + for (int n = 2; n <= 16; n++) + { + Assert.AreEqual(BitConverter.DoubleToInt64Bits(literals[n - 2]), + BitConverter.DoubleToInt64Bits(DistributionNumerics.ZetaInteger(n)), $"n={n}"); + } + for (int n = 17; n <= 60; n++) + { + double expected = 1d; + for (int k = 2; k <= 32; k++) expected += Math.Pow(k, -n); + Assert.AreEqual(BitConverter.DoubleToInt64Bits(expected), + BitConverter.DoubleToInt64Bits(DistributionNumerics.ZetaInteger(n)), $"n={n}"); + } + Assert.ThrowsExactly(() => DistributionNumerics.ZetaInteger(1)); + } + +#if NET8_0_OR_GREATER + /// LP3 gradients solve each standardized quantile once across skew, base, tail and scale cases. + [TestMethod] + public void LogPearsonQuantileGradient_RepeatedCallsAvoidDuplicateInverseWork() + { + var cases = new[] + { + (Distribution: new LogPearsonTypeIII(0.3d, 0.2d, -0.8d) { Base = Math.E }, Probability: 1E-10), + (Distribution: new LogPearsonTypeIII(0.3d, 0.2d, 0d) { Base = 2d }, Probability: 0.83d), + (Distribution: new LogPearsonTypeIII(3d, 0.35d, 0.8d) { Base = 10d }, Probability: 1d - 1E-10), + (Distribution: new LogPearsonTypeIII(-250d, 1E-6d, 4d) { Base = Math.E }, Probability: 0.5d) + }; + double checksum = 0d; + foreach (var item in cases) + foreach (double value in item.Distribution.QuantileGradient(item.Probability)) checksum += value; + + long before = GC.GetAllocatedBytesForCurrentThread(); + const int repetitions = 20; + for (int repetition = 0; repetition < repetitions; repetition++) + foreach (var item in cases) + foreach (double value in item.Distribution.QuantileGradient(item.Probability)) checksum += value; + long allocated = GC.GetAllocatedBytesForCurrentThread() - before; + + Assert.IsTrue(double.IsFinite(checksum)); + TestContext.WriteLine($"Measured LP3 gradient allocation: {allocated} bytes for {repetitions * cases.Length} calls."); + // Each repaired call retains one Pearson object and its returned gradient array (about 104 bytes). + // A 128-byte ceiling allows modest runtime layout variation but rejects the duplicate path's extra Pearson object. + Assert.IsLessThanOrEqualTo(128L * repetitions * cases.Length, allocated, + $"Repeated LP3 gradient evaluation allocated {allocated} bytes."); + } +#endif + } +} diff --git a/Test_Numerics/Distributions/Univariate/Test_ExtremePositiveRobustness.cs b/Test_Numerics/Distributions/Univariate/Test_ExtremePositiveRobustness.cs index ddc9c1b0..a4fc3707 100644 --- a/Test_Numerics/Distributions/Univariate/Test_ExtremePositiveRobustness.cs +++ b/Test_Numerics/Distributions/Univariate/Test_ExtremePositiveRobustness.cs @@ -170,7 +170,7 @@ public void InitializationHasFiniteOrderedFeasibleBounds(string family) foreach (var sample in new[] { new[] { 1d, 1d, 1d, 1d }, new[] { 1d, 2d, 3d, double.NaN }, new[] { 1d, 2d, 3d, double.PositiveInfinity }, new[] { 1d } }) Assert.Throws(() => distribution.GetParameterConstraints(sample), family); if (family == "Gamma" || family == "Weibull") - Assert.Throws(() => distribution.GetParameterConstraints(new[] { 0d, 1d, 2d, 3d }), family); + CheckConstraints(distribution.GetParameterConstraints(new[] { 0d, 1d, 2d, 3d }), family); foreach (double scale in new[] { 1E-200, 1d, 1E200 }) { var sample = new[] { 1d, 2d, 4d, 7d, 8d, 10d }.Select(x => x * scale).ToArray(); diff --git a/Test_Numerics/Distributions/Univariate/Test_GeneralizedBulkSetterRegressions.cs b/Test_Numerics/Distributions/Univariate/Test_GeneralizedBulkSetterRegressions.cs new file mode 100644 index 00000000..5554dc21 --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Test_GeneralizedBulkSetterRegressions.cs @@ -0,0 +1,110 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Distributions; + +namespace Distributions.Univariate +{ + /// Guards bulk parameter update allocations and state for GEV and generalized Pareto fitting. + [TestClass] + public class Test_GeneralizedBulkSetterRegressions + { +#if NET8_0_OR_GREATER + /// Valid repeated updates reuse their inputs without constructing temporary validation lists. + /// Whether to exercise generalized Pareto instead of GEV. + /// Whether to use the list overload instead of the scalar overload. + [TestMethod] + [DataRow(false, false)] + [DataRow(false, true)] + [DataRow(true, false)] + [DataRow(true, true)] + public void ValidBulkUpdates_DoNotAllocate(bool pareto, bool list) + { + UnivariateDistributionBase distribution = Create(pareto); + double[] parameters = { -2d, 1E-20, 0.25d }; + Action update = () => Set(distribution, parameters, list); + for (int i = 0; i < 1000; i++) update(); + + long before = GC.GetAllocatedBytesForCurrentThread(); + for (int i = 0; i < 1000; i++) update(); + long allocated = GC.GetAllocatedBytesForCurrentThread() - before; + + Assert.IsTrue(distribution.ParametersValid); + CollectionAssert.AreEqual(parameters, distribution.GetParameters); + Assert.AreEqual(0L, allocated, $"1000 bulk updates allocated {allocated} bytes ({allocated / 1000d} bytes/call)."); + } +#endif + + /// Bulk updates retain exact values, validation precedence, and recovery from every invalid parameter. + /// Whether to exercise generalized Pareto instead of GEV. + /// Whether to use the list overload instead of the scalar overload. + [TestMethod] + [DataRow(false, false)] + [DataRow(false, true)] + [DataRow(true, false)] + [DataRow(true, true)] + public void BulkUpdates_PreserveValuesValidityAndValidationExceptions(bool pareto, bool list) + { + UnivariateDistributionBase distribution = Create(pareto); + double[][] invalid = { + new[] { double.NaN, 2d, 0.25d }, + new[] { 1d, 0d, 0.25d }, + new[] { 1d, -2d, 0.25d }, + new[] { 1d, double.PositiveInfinity, 0.25d }, + new[] { 1d, 2d, double.NegativeInfinity }, + new[] { double.PositiveInfinity, -2d, double.NaN } + }; + foreach (double[] parameters in invalid) + { + var expected = distribution.ValidateParameters(parameters, false); + Assert.IsNotNull(expected); + Set(distribution, parameters, list); + Assert.IsFalse(distribution.ParametersValid); + CollectionAssert.AreEqual(parameters, distribution.GetParameters); + var actual = Assert.ThrowsExactly(() => distribution.LogPDF(3d)); + Assert.AreEqual(expected.ParamName, actual.ParamName); + Assert.AreEqual(expected.Message, actual.Message); + Assert.AreEqual(expected.ActualValue, actual.ActualValue); + + double[] valid = { -0d, 1E-20, -0.25d }; + Set(distribution, valid, list); + Assert.IsTrue(distribution.ParametersValid); + double[] retained = distribution.GetParameters; + for (int i = 0; i < valid.Length; i++) + Assert.AreEqual(BitConverter.DoubleToInt64Bits(valid[i]), BitConverter.DoubleToInt64Bits(retained[i])); + Assert.IsNull(distribution.ValidateParameters(retained, false)); + } + } + + /// Rejected list lengths retain the existing parameter exception and leave distribution state unchanged. + /// Whether to exercise generalized Pareto instead of GEV. + [TestMethod] + [DataRow(false)] + [DataRow(true)] + public void InvalidListLength_PreservesExceptionAndState(bool pareto) + { + UnivariateDistributionBase distribution = Create(pareto); + double[] original = distribution.GetParameters; + foreach (double[] parameters in new double[][] { null, Array.Empty(), new[] { 1d, 2d }, new[] { 1d, 2d, 3d, 4d } }) + { + var expected = distribution.ValidateParameters(parameters, false); + var actual = Assert.ThrowsExactly(() => distribution.SetParameters(parameters)); + Assert.AreEqual(expected.ParamName, actual.ParamName); + Assert.AreEqual(expected.Message, actual.Message); + CollectionAssert.AreEqual(original, distribution.GetParameters); + Assert.IsTrue(distribution.ParametersValid); + } + } + + private static UnivariateDistributionBase Create(bool pareto) + { + return pareto ? new GeneralizedPareto(1d, 2d, 0.25d) : new GeneralizedExtremeValue(1d, 2d, 0.25d); + } + + private static void Set(UnivariateDistributionBase distribution, double[] parameters, bool list) + { + if (list) distribution.SetParameters(parameters); + else if (distribution is GeneralizedPareto pareto) pareto.SetParameters(parameters[0], parameters[1], parameters[2]); + else ((GeneralizedExtremeValue)distribution).SetParameters(parameters[0], parameters[1], parameters[2]); + } + } +} diff --git a/Test_Numerics/Distributions/Univariate/Test_LegacyParameterConstraints.cs b/Test_Numerics/Distributions/Univariate/Test_LegacyParameterConstraints.cs new file mode 100644 index 00000000..c3665161 --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Test_LegacyParameterConstraints.cs @@ -0,0 +1,897 @@ +using Numerics.Distributions; + +namespace Test_Numerics.Distributions +{ + /// Freezes valid prior envelopes and initial values from the pre-hardening baseline. + [TestClass] + public class Test_LegacyParameterConstraints + { + // Captured from d80bfa8621c48a78cf4ebad7f01326841fac37aa on net8.0. + // These fixtures freeze valid initialization and family-specific prior envelopes. + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void Exponential_ordinary_PreservesBaselineConstraints() + { + var result = new Exponential().GetParameterConstraints(new double[] { 10d, 12d, 14d, 16d }); + AssertArray(new double[] { 9d, 4d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -1d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 10d, 100d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void Exponential_skewed_PreservesBaselineConstraints() + { + var result = new Exponential().GetParameterConstraints(new double[] { 1d, 2d, 4d, 8d, 16d, 32d }); + AssertArray(new double[] { -0.9d, 11.4d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -1.9d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1d, 1000d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void Exponential_nearUnity_PreservesBaselineConstraints() + { + var result = new Exponential().GetParameterConstraints(new double[] { 1.001d, 1.002d, 1.003d, 1.004d }); + AssertArray(new double[] { 1.0005d, 0.002000000000000076d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -8.9995d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1.001d, 0.1d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void Exponential_negative_PreservesBaselineConstraints() + { + var result = new Exponential().GetParameterConstraints(new double[] { -16d, -14d, -12d, -10d }); + AssertArray(new double[] { -17d, 4d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -117d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { -16d, 100d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void Exponential_zeroMean_PreservesBaselineConstraints() + { + var result = new Exponential().GetParameterConstraints(new double[] { -3d, -1d, 1d, 3d }); + AssertArray(new double[] { -4d, 4d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -14d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { -3d, 100d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void Exponential_subUnity_PreservesBaselineConstraints() + { + var result = new Exponential().GetParameterConstraints(new double[] { 0.1d, 0.2d, 0.4d, 0.8d }); + AssertArray(new double[] { 0.00833333333333334d, 0.3666666666666667d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -0.00166666666666666d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 0.1d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void GammaDistribution_ordinary_PreservesBaselineConstraints() + { + var result = new GammaDistribution().GetParameterConstraints(new double[] { 10d, 12d, 14d, 16d }); + AssertArray(new double[] { 0.5128205128205128d, 25.35d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { 1.11022302462516e-16d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 10d, 1000d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void GammaDistribution_skewed_PreservesBaselineConstraints() + { + var result = new GammaDistribution().GetParameterConstraints(new double[] { 1d, 2d, 4d, 8d, 16d, 32d }); + AssertArray(new double[] { 13.399999999999999d, 0.7835820895522388d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { 1.11022302462516e-16d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1000d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void GammaDistribution_nearUnity_PreservesBaselineConstraints() + { + var result = new GammaDistribution().GetParameterConstraints(new double[] { 1.001d, 1.002d, 1.003d, 1.004d }); + AssertArray(new double[] { 1.662510390690019e-06d, 603003.749999972d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { 1.11022302462516e-16d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 0.0001d, 10000000d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void GammaDistribution_subUnity_PreservesBaselineConstraints() + { + var result = new GammaDistribution().GetParameterConstraints(new double[] { 0.1d, 0.2d, 0.4d, 0.8d }); + AssertArray(new double[] { 0.2555555555555556d, 1.467391304347826d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { 1.11022302462516e-16d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 10d, 100d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void GeneralizedExtremeValue_ordinary_PreservesBaselineConstraints() + { + var result = new GeneralizedExtremeValue().GetParameterConstraints(new double[] { 10d, 12d, 14d, 16d }); + AssertArray(new double[] { 11.964905241795506d, 2.944098222941484d, 0.2846308210256667d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -1000d, 1.11022302462516e-16d, -10d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1000d, 100d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void GeneralizedExtremeValue_skewed_PreservesBaselineConstraints() + { + var result = new GeneralizedExtremeValue().GetParameterConstraints(new double[] { 1d, 2d, 4d, 8d, 16d, 32d }); + AssertArray(new double[] { 3.5251212632019016d, 5.1182032350575986d, -0.44798569084540746d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -100d, 1.11022302462516e-16d, -10d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 100d, 100d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void GeneralizedExtremeValue_nearUnity_PreservesBaselineConstraints() + { + var result = new GeneralizedExtremeValue().GetParameterConstraints(new double[] { 1.001d, 1.002d, 1.003d, 1.004d }); + AssertArray(new double[] { 1.0019824526208967d, 0.0014720491114694771d, 0.28463082102423143d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -100d, 1.11022302462516e-16d, -10d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 100d, 0.1d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void GeneralizedExtremeValue_negative_PreservesBaselineConstraints() + { + var result = new GeneralizedExtremeValue().GetParameterConstraints(new double[] { -16d, -14d, -12d, -10d }); + AssertArray(new double[] { -14.035094758204492d, 2.9440982229414914d, 0.28463082102566944d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -1000d, 1.11022302462516e-16d, -10d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1000d, 100d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void GeneralizedExtremeValue_zeroMean_PreservesBaselineConstraints() + { + var result = new GeneralizedExtremeValue().GetParameterConstraints(new double[] { -3d, -1d, 1d, 3d }); + AssertArray(new double[] { -1.035094758204496d, 2.9440982229414825d, 0.284630821025665d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -100d, 1.11022302462516e-16d, -10d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 100d, 100d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void GeneralizedExtremeValue_subUnity_PreservesBaselineConstraints() + { + var result = new GeneralizedExtremeValue().GetParameterConstraints(new double[] { 0.1d, 0.2d, 0.4d, 0.8d }); + AssertArray(new double[] { 0.1832069588359371d, 0.18598234830157992d, -0.31866881623469623d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -10d, 1.11022302462516e-16d, -10d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 10d, 10d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void GeneralizedLogistic_ordinary_PreservesBaselineConstraints() + { + var result = new GeneralizedLogistic().GetParameterConstraints(new double[] { 10d, 12d, 14d, 16d }); + AssertArray(new double[] { 13.000000000000002d, 1.666666666666666d, 8.881784197001252e-16d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -1000d, 1.11022302462516e-16d, -10d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1000d, 100d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void GeneralizedLogistic_skewed_PreservesBaselineConstraints() + { + var result = new GeneralizedLogistic().GetParameterConstraints(new double[] { 1d, 2d, 4d, 8d, 16d, 32d }); + AssertArray(new double[] { 5.685723293384427d, 4.328789084801283d, -0.49253731343283613d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -100d, 1.11022302462516e-16d, -10d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 100d, 100d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void GeneralizedLogistic_nearUnity_PreservesBaselineConstraints() + { + var result = new GeneralizedLogistic().GetParameterConstraints(new double[] { 1.001d, 1.002d, 1.003d, 1.004d }); + AssertArray(new double[] { 1.0024999999999988d, 0.0008333333333332416d, -7.993605777301127e-13d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -100d, 1.11022302462516e-16d, -10d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 100d, 0.01d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void GeneralizedLogistic_negative_PreservesBaselineConstraints() + { + var result = new GeneralizedLogistic().GetParameterConstraints(new double[] { -16d, -14d, -12d, -10d }); + AssertArray(new double[] { -12.999999999999995d, 1.6666666666666679d, 2.220446049250313e-15d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -1000d, 1.11022302462516e-16d, -10d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1000d, 100d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void GeneralizedLogistic_zeroMean_PreservesBaselineConstraints() + { + var result = new GeneralizedLogistic().GetParameterConstraints(new double[] { -3d, -1d, 1d, 3d }); + AssertArray(new double[] { 1.11022302462516e-16d, 1.6666666666666667d, 0d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -1e-14d, 1.11022302462516e-16d, -10d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1e-14d, 100d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void GeneralizedLogistic_subUnity_PreservesBaselineConstraints() + { + var result = new GeneralizedLogistic().GetParameterConstraints(new double[] { 0.1d, 0.2d, 0.4d, 0.8d }); + AssertArray(new double[] { 0.26062346440010725d, 0.1469106309971434d, -0.3913043478260869d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -10d, 1.11022302462516e-16d, -10d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 10d, 10d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void GeneralizedNormal_ordinary_PreservesBaselineConstraints() + { + var result = new GeneralizedNormal().GetParameterConstraints(new double[] { 10d, 12d, 14d, 16d }); + AssertArray(new double[] { 13d, 3.03207911111111d, 1.817793382485888e-15d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -1000d, 1.11022302462516e-16d, -10d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1000d, 100d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void GeneralizedNormal_skewed_PreservesBaselineConstraints() + { + var result = new GeneralizedNormal().GetParameterConstraints(new double[] { 1d, 2d, 4d, 8d, 16d, 32d }); + AssertArray(new double[] { 5.1845392376947785d, 7.317708020900799d, -1.0739272660014685d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -100d, 1.11022302462516e-16d, -10d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 100d, 100d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void GeneralizedNormal_nearUnity_PreservesBaselineConstraints() + { + var result = new GeneralizedNormal().GetParameterConstraints(new double[] { 1.001d, 1.002d, 1.003d, 1.004d }); + AssertArray(new double[] { 1.0025d, 0.001477372521655275d, -1.6360140442372994e-12d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -100d, 1.11022302462516e-16d, -10d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 100d, 0.1d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void GeneralizedNormal_negative_PreservesBaselineConstraints() + { + var result = new GeneralizedNormal().GetParameterConstraints(new double[] { -16d, -14d, -12d, -10d }); + AssertArray(new double[] { -13d, 3.1009900000000026d, 4.5444834562147206e-15d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -1000d, 1.11022302462516e-16d, -10d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1000d, 100d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void GeneralizedNormal_subUnity_PreservesBaselineConstraints() + { + var result = new GeneralizedNormal().GetParameterConstraints(new double[] { 0.1d, 0.2d, 0.4d, 0.8d }); + AssertArray(new double[] { 0.24860780884509534d, 0.25425864998736986d, -0.8320397358919761d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -10d, 1.11022302462516e-16d, -10d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 10d, 10d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void GeneralizedPareto_ordinary_PreservesBaselineConstraints() + { + var result = new GeneralizedPareto().GetParameterConstraints(new double[] { 10d, 12d, 14d, 16d }); + AssertArray(new double[] { 7.999999999999996d, 10.000000000000027d, 1.0000000000000036d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -2.0000000000000044d, 1.11022302462516e-16d, -10d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 10d, 1000d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void GeneralizedPareto_skewed_PreservesBaselineConstraints() + { + var result = new GeneralizedPareto().GetParameterConstraints(new double[] { 1d, 2d, 4d, 8d, 16d, 32d }); + AssertArray(new double[] { -0.7559999999999949d, 7.654079999999991d, -0.32000000000000056d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -1.755999999999995d, 1.11022302462516e-16d, -10d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1.0000000000000002d, 100d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void GeneralizedPareto_nearUnity_PreservesBaselineConstraints() + { + var result = new GeneralizedPareto().GetParameterConstraints(new double[] { 1.001d, 1.002d, 1.003d, 1.004d }); + AssertArray(new double[] { 1.0000000000000029d, 0.004999999999986127d, 0.9999999999968026d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -8.999999999999996d, 1.11022302462516e-16d, -10d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1.0010000000000001d, 0.1d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void GeneralizedPareto_negative_PreservesBaselineConstraints() + { + var result = new GeneralizedPareto().GetParameterConstraints(new double[] { -16d, -14d, -12d, -10d }); + AssertArray(new double[] { -18.000000000000018d, 10.000000000000082d, 1.0000000000000089d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -118.00000000000001d, 1.11022302462516e-16d, -10d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { -16d, 1000d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void GeneralizedPareto_zeroMean_PreservesBaselineConstraints() + { + var result = new GeneralizedPareto().GetParameterConstraints(new double[] { -3d, -1d, 1d, 3d }); + AssertArray(new double[] { -5d, 10d, 1d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -15d, 1.11022302462516e-16d, -10d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { -3d, 100d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void GeneralizedPareto_subUnity_PreservesBaselineConstraints() + { + var result = new GeneralizedPareto().GetParameterConstraints(new double[] { 0.1d, 0.2d, 0.4d, 0.8d }); + AssertArray(new double[] { 0.015625d, 0.314453125d, -0.12499999999999992d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -0.084375d, 1.11022302462516e-16d, -10d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 0.10000000000000012d, 10d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void Gumbel_ordinary_PreservesBaselineConstraints() + { + var result = new Gumbel().GetParameterConstraints(new double[] { 10d, 12d, 14d, 16d }); + AssertArray(new double[] { 11.612089704538555d, 2.4044917348149384d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -1000d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1000d, 100d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void Gumbel_skewed_PreservesBaselineConstraints() + { + var result = new Gumbel().GetParameterConstraints(new double[] { 1d, 2d, 4d, 8d, 16d, 32d }); + AssertArray(new double[] { 4.92060061224499d, 9.666056773956054d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -100d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 100d, 100d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void Gumbel_nearUnity_PreservesBaselineConstraints() + { + var result = new Gumbel().GetParameterConstraints(new double[] { 1.001d, 1.002d, 1.003d, 1.004d }); + AssertArray(new double[] { 1.0018060448522692d, 0.0012022458674073372d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -100d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 100d, 0.1d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void Gumbel_negative_PreservesBaselineConstraints() + { + var result = new Gumbel().GetParameterConstraints(new double[] { -16d, -14d, -12d, -10d }); + AssertArray(new double[] { -14.387910295461445d, 2.4044917348149406d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -1000d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1000d, 100d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void Gumbel_zeroMean_PreservesBaselineConstraints() + { + var result = new Gumbel().GetParameterConstraints(new double[] { -3d, -1d, 1d, 3d }); + AssertArray(new double[] { -1.3879102954614455d, 2.4044917348149393d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -100d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 100d, 100d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void Gumbel_subUnity_PreservesBaselineConstraints() + { + var result = new Gumbel().GetParameterConstraints(new double[] { 0.1d, 0.2d, 0.4d, 0.8d }); + AssertArray(new double[] { 0.21539031602193381d, 0.27651654950371796d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -10d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 10d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void KappaFour_ordinary_PreservesBaselineConstraints() + { + var result = new KappaFour().GetParameterConstraints(new double[] { 10d, 12d, 14d, 16d }); + AssertArray(new double[] { 8.000006666483934d, 9.999987917531367d, 0.9999994001012955d, 0.9999991500014682d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -100d, 1.11022302462516e-16d, -10d, -2d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 100d, 100d, 10d, 2d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void KappaFour_skewed_PreservesBaselineConstraints() + { + var result = new KappaFour().GetParameterConstraints(new double[] { 1d, 2d, 4d, 8d, 16d, 32d }); + AssertArray(new double[] { -41.20730147684273d, 41.160111238748186d, 0.2828390688884041d, 0d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -1000d, 1.11022302462516e-16d, -10d, -2d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1000d, 1000d, 10d, 2d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void KappaFour_nearUnity_PreservesBaselineConstraints() + { + var result = new KappaFour().GetParameterConstraints(new double[] { 1.001d, 1.002d, 1.003d, 1.004d }); + AssertArray(new double[] { 1.0000000033331053d, 0.004999993959029831d, 0.9999994001312362d, 0.9999991500350589d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -100d, 1.11022302462516e-16d, -10d, -2d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 100d, 0.1d, 10d, 2d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void KappaFour_negative_PreservesBaselineConstraints() + { + var result = new KappaFour().GetParameterConstraints(new double[] { -16d, -14d, -12d, -10d }); + AssertArray(new double[] { -17.99999333350232d, 9.999987917504608d, 0.9999994000997355d, 0.9999991499997695d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -1000d, 1.11022302462516e-16d, -10d, -2d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1000d, 100d, 10d, 2d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void KappaFour_zeroMean_PreservesBaselineConstraints() + { + var result = new KappaFour().GetParameterConstraints(new double[] { -3d, -1d, 1d, 3d }); + AssertArray(new double[] { -4.999993333505345d, 9.999987917511882d, 0.9999994001003065d, 0.9999991500001029d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -100d, 1.11022302462516e-16d, -10d, -2d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 100d, 100d, 10d, 2d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void KappaFour_subUnity_PreservesBaselineConstraints() + { + var result = new KappaFour().GetParameterConstraints(new double[] { 0.1d, 0.2d, 0.4d, 0.8d }); + AssertArray(new double[] { -0.739635718854716d, 1.0608365097007928d, 0.36224410221013503d, 0d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -10d, 1.11022302462516e-16d, -10d, -2d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 10d, 100d, 10d, 2d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void LnNormal_ordinary_PreservesBaselineConstraints() + { + var result = new LnNormal().GetParameterConstraints(new double[] { 10d, 12d, 14d, 16d }); + AssertArray(new double[] { 13d, 2.581988897471611d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { 1.11022302462516e-16d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1000d, 100d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void LnNormal_skewed_PreservesBaselineConstraints() + { + var result = new LnNormal().GetParameterConstraints(new double[] { 1d, 2d, 4d, 8d, 16d, 32d }); + AssertArray(new double[] { 10.5d, 11.861703081766969d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { 1.11022302462516e-16d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1000d, 1000d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void LnNormal_nearUnity_PreservesBaselineConstraints() + { + var result = new LnNormal().GetParameterConstraints(new double[] { 1.001d, 1.002d, 1.003d, 1.004d }); + AssertArray(new double[] { 1.0025d, 0.0012909944487358356d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { 1.11022302462516e-16d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 100d, 0.1d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void LnNormal_subUnity_PreservesBaselineConstraints() + { + var result = new LnNormal().GetParameterConstraints(new double[] { 0.1d, 0.2d, 0.4d, 0.8d }); + AssertArray(new double[] { 0.375d, 0.30956959368344517d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { 1.11022302462516e-16d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 10d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void Logistic_ordinary_PreservesBaselineConstraints() + { + var result = new Logistic().GetParameterConstraints(new double[] { 10d, 12d, 14d, 16d }); + AssertArray(new double[] { 13d, 1.4235250868343539d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -1000d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1000d, 100d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void Logistic_skewed_PreservesBaselineConstraints() + { + var result = new Logistic().GetParameterConstraints(new double[] { 1d, 2d, 4d, 8d, 16d, 32d }); + AssertArray(new double[] { 10.5d, 6.539699657891849d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -1000d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1000d, 100d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void Logistic_nearUnity_PreservesBaselineConstraints() + { + var result = new Logistic().GetParameterConstraints(new double[] { 1.001d, 1.002d, 1.003d, 1.004d }); + AssertArray(new double[] { 1.0025d, 0.0007117625434171935d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -100d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 100d, 0.01d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void Logistic_subUnity_PreservesBaselineConstraints() + { + var result = new Logistic().GetParameterConstraints(new double[] { 0.1d, 0.2d, 0.4d, 0.8d }); + AssertArray(new double[] { 0.375d, 0.17067466214166682d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -10d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 10d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void LogNormal_ordinary_PreservesBaselineConstraints() + { + var result = new LogNormal().GetParameterConstraints(new double[] { 10d, 12d, 14d, 16d }); + AssertArray(new double[] { 1.1073573160954466d, 0.08791220351278327d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -100d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 100d, 2d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void LogNormal_skewed_PreservesBaselineConstraints() + { + var result = new LogNormal().GetParameterConstraints(new double[] { 1d, 2d, 4d, 8d, 16d, 32d }); + AssertArray(new double[] { 0.7525749891599528d, 0.5631755534583315d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -10d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 10d, 2d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void LogNormal_nearUnity_PreservesBaselineConstraints() + { + var result = new LogNormal().GetParameterConstraints(new double[] { 1.001d, 1.002d, 1.003d, 1.004d }); + AssertArray(new double[] { 0.0010841112099910272d, 0.0005592740172164043d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -0.1d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 0.1d, 2d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void LogNormal_subUnity_PreservesBaselineConstraints() + { + var result = new LogNormal().GetParameterConstraints(new double[] { 0.1d, 0.2d, 0.4d, 0.8d }); + AssertArray(new double[] { -0.5484550065040281d, 0.38862805330516337d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -10d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 10d, 2d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void LogPearsonTypeIII_ordinary_PreservesBaselineConstraints() + { + var result = new LogPearsonTypeIII().GetParameterConstraints(new double[] { 10d, 12d, 14d, 16d }); + AssertArray(new double[] { 1.1073573160954466d, 0.08791220351278327d, -0.2899042849970034d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -100d, 1.11022302462516e-16d, -6d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 100d, 2d, 6d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void LogPearsonTypeIII_skewed_PreservesBaselineConstraints() + { + var result = new LogPearsonTypeIII().GetParameterConstraints(new double[] { 1d, 2d, 4d, 8d, 16d, 32d }); + AssertArray(new double[] { 0.7525749891599528d, 0.5631755534583315d, -1.1187971499007316e-15d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -10d, 1.11022302462516e-16d, -6d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 10d, 2d, 6d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void LogPearsonTypeIII_nearUnity_PreservesBaselineConstraints() + { + var result = new LogPearsonTypeIII().GetParameterConstraints(new double[] { 1.001d, 1.002d, 1.003d, 1.004d }); + AssertArray(new double[] { 0.0010841112099910272d, 0.0005592740172164043d, -0.0018543970617275146d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -0.1d, 1.11022302462516e-16d, -6d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 0.1d, 2d, 6d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void LogPearsonTypeIII_subUnity_PreservesBaselineConstraints() + { + var result = new LogPearsonTypeIII().GetParameterConstraints(new double[] { 0.1d, 0.2d, 0.4d, 0.8d }); + AssertArray(new double[] { -0.5484550065040281d, 0.38862805330516337d, -1.2610041890269048e-15d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -10d, 1.11022302462516e-16d, -6d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 10d, 2d, 6d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void Normal_ordinary_PreservesBaselineConstraints() + { + var result = new Normal().GetParameterConstraints(new double[] { 10d, 12d, 14d, 16d }); + AssertArray(new double[] { 13d, 2.581988897471611d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -1000d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1000d, 100d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void Normal_skewed_PreservesBaselineConstraints() + { + var result = new Normal().GetParameterConstraints(new double[] { 1d, 2d, 4d, 8d, 16d, 32d }); + AssertArray(new double[] { 10.5d, 11.861703081766969d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -1000d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1000d, 1000d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void Normal_nearUnity_PreservesBaselineConstraints() + { + var result = new Normal().GetParameterConstraints(new double[] { 1.001d, 1.002d, 1.003d, 1.004d }); + AssertArray(new double[] { 1.0025d, 0.0012909944487358356d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -100d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 100d, 0.1d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void Normal_negative_PreservesBaselineConstraints() + { + var result = new Normal().GetParameterConstraints(new double[] { -16d, -14d, -12d, -10d }); + AssertArray(new double[] { -13d, 2.581988897471611d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -1000d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1000d, 100d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void Normal_zeroMean_PreservesBaselineConstraints() + { + var result = new Normal().GetParameterConstraints(new double[] { -3d, -1d, 1d, 3d }); + AssertArray(new double[] { 1.11022302462516e-16d, 2.581988897471611d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -1e-14d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1e-14d, 100d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void Normal_subUnity_PreservesBaselineConstraints() + { + var result = new Normal().GetParameterConstraints(new double[] { 0.1d, 0.2d, 0.4d, 0.8d }); + AssertArray(new double[] { 0.375d, 0.30956959368344517d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -10d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 10d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void PearsonTypeIII_ordinary_PreservesBaselineConstraints() + { + var result = new PearsonTypeIII().GetParameterConstraints(new double[] { 10d, 12d, 14d, 16d }); + AssertArray(new double[] { 13d, 2.581988897471611d, 0d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -1000d, 1.11022302462516e-16d, -6d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1000d, 100d, 6d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void PearsonTypeIII_skewed_PreservesBaselineConstraints() + { + var result = new PearsonTypeIII().GetParameterConstraints(new double[] { 1d, 2d, 4d, 8d, 16d, 32d }); + AssertArray(new double[] { 10.5d, 11.861703081766969d, 1.4996929578604363d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -1000d, 1.11022302462516e-16d, -6d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1000d, 1000d, 6d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void PearsonTypeIII_nearUnity_PreservesBaselineConstraints() + { + var result = new PearsonTypeIII().GetParameterConstraints(new double[] { 1.001d, 1.002d, 1.003d, 1.004d }); + AssertArray(new double[] { 1.0025d, 0.0012909944487358356d, -1.025170155751457e-15d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -100d, 1.11022302462516e-16d, -6d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 100d, 0.1d, 6d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void PearsonTypeIII_subUnity_PreservesBaselineConstraints() + { + var result = new PearsonTypeIII().GetParameterConstraints(new double[] { 0.1d, 0.2d, 0.4d, 0.8d }); + AssertArray(new double[] { 0.375d, 0.30956959368344517d, 1.1376243669576893d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -10d, 1.11022302462516e-16d, -6d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 10d, 10d, 6d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void Weibull_ordinary_PreservesBaselineConstraints() + { + var result = new Weibull().GetParameterConstraints(new double[] { 10d, 12d, 14d, 16d }); +#if NETFRAMEWORK + // Captured independently from d80bfa8 on CLR 4.0.30319.42000; retain exact equality. + AssertArray(new double[] { 13.949101633065432d, 6.6844918509346618d }, result.Item1, "initial"); +#else + AssertArray(new double[] { 13.949101633065432d, 6.684491850934669d }, result.Item1, "initial"); +#endif + CollectionAssert.AreEqual(new double[] { 1.11022302462516e-16d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1000d, 100d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void Weibull_skewed_PreservesBaselineConstraints() + { + var result = new Weibull().GetParameterConstraints(new double[] { 1d, 2d, 4d, 8d, 16d, 32d }); + AssertArray(new double[] { 10.19351618226867d, 0.9402756427497648d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { 1.11022302462516e-16d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1000d, 10d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void Weibull_nearUnity_PreservesBaselineConstraints() + { + var result = new Weibull().GetParameterConstraints(new double[] { 1.001d, 1.002d, 1.003d, 1.004d }); + AssertArray(new double[] { 1.0030553641004294d, 1007.3379396499552d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { 1.11022302462516e-16d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 100d, 100000d }, result.Item3, "upper"); + } + + /// Checks initialization and bounds against the literal baseline fixture. + [TestMethod] + public void Weibull_subUnity_PreservesBaselineConstraints() + { + var result = new Weibull().GetParameterConstraints(new double[] { 0.1d, 0.2d, 0.4d, 0.8d }); + AssertArray(new double[] { 0.4158025497714148d, 1.4492910264321852d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { 1.11022302462516e-16d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 10d, 100d }, result.Item3, "upper"); + } + + /// Preserves physical-scale decade rounding before conversion to the configured log base. + [TestMethod] + public void LogNormal_nearUnity_BaseTwo_PreservesBaselineConstraints() + { + var result = new LogNormal { Base = 2d }.GetParameterConstraints(new double[] { 1.001d, 1.002d, 1.003d, 1.004d }); + AssertArray(new double[] { 0.003601339486451527d, 0.0018578680705316926d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -0.1d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 0.1d, 7d }, result.Item3, "upper"); + } + + /// Preserves physical-scale decade rounding before conversion to the configured log base. + [TestMethod] + public void LogNormal_subUnity_BaseTwo_PreservesBaselineConstraints() + { + var result = new LogNormal { Base = 2d }.GetParameterConstraints(new double[] { 0.1d, 0.2d, 0.4d, 0.8d }); + AssertArray(new double[] { -1.8219280948873622d, 1.2909944487358056d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -100d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 100d, 7d }, result.Item3, "upper"); + } + + /// Preserves physical-scale decade rounding before conversion to the configured log base. + [TestMethod] + public void LogNormal_nearUnity_NaturalBase_PreservesBaselineConstraints() + { + var result = new LogNormal { Base = 2.718281828459045d }.GetParameterConstraints(new double[] { 1.001d, 1.002d, 1.003d, 1.004d }); + AssertArray(new double[] { 0.002496258311273077d, 0.0012877760149413882d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -0.1d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 0.1d, 5d }, result.Item3, "upper"); + } + + /// Preserves physical-scale decade rounding before conversion to the configured log base. + [TestMethod] + public void LogNormal_subUnity_NaturalBase_PreservesBaselineConstraints() + { + var result = new LogNormal { Base = 2.718281828459045d }.GetParameterConstraints(new double[] { 0.1d, 0.2d, 0.4d, 0.8d }); + AssertArray(new double[] { -1.2628643221541278d, 0.8948491622597645d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -100d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 100d, 5d }, result.Item3, "upper"); + } + + /// Preserves physical-scale decade rounding before conversion to the configured log base. + [TestMethod] + public void LogPearsonTypeIII_nearUnity_BaseTwo_PreservesBaselineConstraints() + { + var result = new LogPearsonTypeIII { Base = 2d }.GetParameterConstraints(new double[] { 1.001d, 1.002d, 1.003d, 1.004d }); + AssertArray(new double[] { 0.003601339486451527d, 0.0018578680705316926d, -0.0018543970617339045d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -0.1d, 1.11022302462516e-16d, -6d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 0.1d, 7d, 6d }, result.Item3, "upper"); + } + + /// Preserves physical-scale decade rounding before conversion to the configured log base. + [TestMethod] + public void LogPearsonTypeIII_subUnity_BaseTwo_PreservesBaselineConstraints() + { + var result = new LogPearsonTypeIII { Base = 2d }.GetParameterConstraints(new double[] { 0.1d, 0.2d, 0.4d, 0.8d }); + AssertArray(new double[] { -1.8219280948873622d, 1.2909944487358056d, 0d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -100d, 1.11022302462516e-16d, -6d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 100d, 7d, 6d }, result.Item3, "upper"); + } + + /// Preserves physical-scale decade rounding before conversion to the configured log base. + [TestMethod] + public void LogPearsonTypeIII_nearUnity_NaturalBase_PreservesBaselineConstraints() + { + var result = new LogPearsonTypeIII { Base = 2.718281828459045d }.GetParameterConstraints(new double[] { 1.001d, 1.002d, 1.003d, 1.004d }); + AssertArray(new double[] { 0.002496258311273077d, 0.0012877760149413882d, -0.001854397061727415d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -0.1d, 1.11022302462516e-16d, -6d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 0.1d, 5d, 6d }, result.Item3, "upper"); + } + + /// Preserves physical-scale decade rounding before conversion to the configured log base. + [TestMethod] + public void LogPearsonTypeIII_subUnity_NaturalBase_PreservesBaselineConstraints() + { + var result = new LogPearsonTypeIII { Base = 2.718281828459045d }.GetParameterConstraints(new double[] { 0.1d, 0.2d, 0.4d, 0.8d }); + AssertArray(new double[] { -1.2628643221541278d, 0.8948491622597645d, 0d }, result.Item1, "initial"); + CollectionAssert.AreEqual(new double[] { -100d, 1.11022302462516e-16d, -6d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 100d, 5d, 6d }, result.Item3, "upper"); + } + + /// Retains hardened zero-center bounds when this family's old location envelope was invalid. + [TestMethod] + public void Logistic_ZeroMean_RetainsExceptionalFallback() + { + var result = new Logistic().GetParameterConstraints(new double[] { -3d, -1d, 1d, 3d }); + Assert.AreEqual(0d, result.Item1[0]); + Assert.AreEqual(-100d, result.Item2[0]); + Assert.AreEqual(100d, result.Item3[0]); + } + + /// Retains hardened zero-center bounds when this family's old location envelope was invalid. + [TestMethod] + public void PearsonTypeIII_ZeroMean_RetainsExceptionalFallback() + { + var result = new PearsonTypeIII().GetParameterConstraints(new double[] { -3d, -1d, 1d, 3d }); + Assert.AreEqual(0d, result.Item1[0]); + Assert.AreEqual(-100d, result.Item2[0]); + Assert.AreEqual(100d, result.Item3[0]); + } + + /// Preserves a finite ordered legacy envelope whose initializer equals the scale lower bound. + [TestMethod] + public void Normal_ScaleAtLegacyLowerBound_PreservesBoundaryInitializer() + { + double epsilon = Numerics.Tools.DoubleMachineEpsilon; + var result = new Normal().GetParameterConstraints(new double[] { 0d, 0d, 0d, 2d * epsilon }); + Assert.AreEqual(epsilon, result.Item1[1]); + Assert.AreEqual(epsilon, result.Item2[1]); + Assert.AreEqual(1E-14, result.Item3[1]); + } + + /// Preserves the fallback exception identity and sample parameter expected by fitting callers. + [TestMethod] + public void FailedInitializers_PreserveFallbackSampleException() + { + var legacyFailure = new ArithmeticException("Legacy moments overflowed."); + var fallbackFailure = new ArgumentException("No finite bounds exist.", "sample"); + var actual = Assert.ThrowsExactly(() => DistributionNumerics.PreferLegacyConstraints( + () => throw legacyFailure, () => throw fallbackFailure)); + Assert.AreSame(fallbackFailure, actual); + Assert.AreEqual("sample", actual.ParamName); + Assert.AreSame(legacyFailure, actual.Data["LegacyParameterConstraintsFailure"]); + } + + private static void AssertArray(double[] expected, double[] actual, string label) + { + Assert.HasCount(expected.Length, actual, label); + for (int i = 0; i < expected.Length; i++) + Assert.AreEqual(expected[i], actual[i], $"{label}[{i}]"); + } + } +} diff --git a/Test_Numerics/Distributions/Univariate/Test_LogPearsonPerformanceRegressions.cs b/Test_Numerics/Distributions/Univariate/Test_LogPearsonPerformanceRegressions.cs new file mode 100644 index 00000000..5829c482 --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Test_LogPearsonPerformanceRegressions.cs @@ -0,0 +1,43 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Distributions; + +namespace Distributions.Univariate +{ + /// Regression coverage for allocation-free LP3 log-density delegation. + [TestClass] + public class Test_LogPearsonPerformanceRegressions + { + /// Receives diagnostic allocation output from the focused tests. + public TestContext TestContext { get; set; } + +#if NET8_0_OR_GREATER + /// Repeated valid LP3 log-density evaluation does not allocate Pearson wrappers. + [TestMethod] + public void LogPDF_RepeatedCallsDoNotAllocatePearsonWrappers() + { + var cases = new[] + { + (Distribution: new LogPearsonTypeIII(3d, 0.35d, 0d) { Base = 10d }, X: 700d), + (Distribution: new LogPearsonTypeIII(3d, 0.35d, 1E-5d) { Base = 2d }, X: 8d), + (Distribution: new LogPearsonTypeIII(3d, 0.35d, -0.5d) { Base = 10d }, X: 1200d), + (Distribution: new LogPearsonTypeIII(0.3d, 0.2d, 1.5d) { Base = Math.E }, X: 2d), + (Distribution: new LogPearsonTypeIII(3d, 0.35d, 4d) { Base = 10d }, X: 900d) + }; + long checksum = 0L; + foreach (var item in cases) checksum ^= BitConverter.DoubleToInt64Bits(item.Distribution.LogPDF(item.X)); + + long before = GC.GetAllocatedBytesForCurrentThread(); + const int repetitions = 100; + for (int repetition = 0; repetition < repetitions; repetition++) + foreach (var item in cases) + checksum ^= BitConverter.DoubleToInt64Bits(item.Distribution.LogPDF(item.X)); + long allocated = GC.GetAllocatedBytesForCurrentThread() - before; + + TestContext.WriteLine($"Measured LP3 LogPDF allocation: {allocated} bytes for {repetitions * cases.Length} calls; checksum={checksum}."); + Assert.AreEqual(0L, allocated, $"Repeated LP3 LogPDF evaluation allocated {allocated} bytes."); + } +#endif + + } +} diff --git a/Test_Numerics/Distributions/Univariate/Test_MixturePerformanceRegressions.cs b/Test_Numerics/Distributions/Univariate/Test_MixturePerformanceRegressions.cs new file mode 100644 index 00000000..9c6e6df3 --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Test_MixturePerformanceRegressions.cs @@ -0,0 +1,108 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Distributions; + +namespace Distributions.Univariate +{ + /// Regression coverage for allocation-conscious validation of mutable mixtures. + [TestClass] + public class Test_MixturePerformanceRegressions + { + /// Receives diagnostic allocation output from the focused tests. + public TestContext TestContext { get; set; } + +#if NET8_0_OR_GREATER + /// Repeated density evaluation validates live state without rebuilding flattened candidates. + [TestMethod] + public void LogPDF_RepeatedLiveValidationAvoidsFlattenedCandidateAllocations() + { + var mixtures = new[] + { + new Mixture(new[] { 0.4d, 0.6d }, new UnivariateDistributionBase[] + { new Normal(2d, 0.8d), new Normal(6d, 1.4d) }), + new Mixture(new[] { 0.25d, 0.45d, 0.3d }, new UnivariateDistributionBase[] + { new Normal(2d, 0.8d), new Normal(5d, 1.2d), new Normal(9d, 1.5d) }), + new Mixture(new[] { 0.4d, 0.6d }, new UnivariateDistributionBase[] + { new Normal(2d, 0.8d), new Normal(6d, 1.4d) }) { IsZeroInflated = true, ZeroWeight = 0.2d } + }; + double checksum = 0d; + foreach (Mixture mixture in mixtures) checksum += mixture.LogPDF(3d); + + long before = GC.GetAllocatedBytesForCurrentThread(); + const int repetitions = 100; + for (int repetition = 0; repetition < repetitions; repetition++) + foreach (Mixture mixture in mixtures) checksum += mixture.LogPDF(3d); + long allocated = GC.GetAllocatedBytesForCurrentThread() - before; + + Assert.IsTrue(double.IsFinite(checksum)); + TestContext.WriteLine($"Measured mixture LogPDF allocation: {allocated} bytes for {repetitions * mixtures.Length} calls."); + Assert.IsLessThanOrEqualTo(256L * repetitions * mixtures.Length, allocated, + $"Repeated mixture LogPDF evaluation allocated {allocated} bytes."); + } +#endif + + /// Evaluation observes public weight, component and nested-parameter mutations on every call. + [TestMethod] + public void LogPDF_DirectValidationDetectsEveryLiveMutation() + { + var mixture = CreateTwoNormalMixture(new[] { 0.4d, 0.6d }); + mixture.Weights[0] = double.NaN; + ArgumentOutOfRangeException weightError = + Assert.ThrowsExactly(() => mixture.LogPDF(3d)); + Assert.AreEqual("Weights", weightError.ParamName); + + mixture = CreateTwoNormalMixture(new[] { 0.4d, 0.6d }); + mixture.Distributions[0].SetParameters(new[] { 2d, -0.8d }); + Assert.ThrowsExactly(() => mixture.LogPDF(3d)); + + mixture = CreateTwoNormalMixture(new[] { 0.4d, 0.6d }); + mixture.Distributions[0] = new Normal(double.NaN, 0.8d); + Assert.ThrowsExactly(() => mixture.LogPDF(3d)); + } + + /// Zero-weight components remain parameter-valid while positive-mass checks remain active-only. + [TestMethod] + public void LogPDF_DirectValidationPreservesInactiveAndZeroInflatedRules() + { + var invalidInactive = CreateTwoNormalMixture(new[] { 1d, 0d }); + invalidInactive.Distributions[1].SetParameters(new[] { 6d, -1.4d }); + Assert.ThrowsExactly(() => invalidInactive.LogPDF(3d)); + + var validInactive = new Mixture(new[] { 1d, 0d }, new UnivariateDistributionBase[] + { new Normal(2d, 0.8d), new Deterministic(0d) }) + { + IsZeroInflated = true, + ZeroWeight = 0.2d + }; + Assert.IsTrue(Numerics.Tools.IsFinite(validInactive.LogPDF(3d))); + + validInactive.Weights[0] = 0d; + validInactive.Weights[1] = 0.8d; + ArgumentOutOfRangeException positiveMassError = + Assert.ThrowsExactly(() => validInactive.LogPDF(3d)); + StringAssert.Contains(positiveMassError.Message, "positive probability above zero"); + } + + /// The public validator continues to evaluate supplied hypothetical flattened candidates. + [TestMethod] + public void ValidateParameters_PreservesHypotheticalCandidateContract() + { + var mixture = CreateTwoNormalMixture(new[] { 0.4d, 0.6d }); + double[] valid = mixture.GetParameters; + Assert.IsNull(mixture.ValidateParameters(valid, false)); + + double[] invalidWeight = (double[])valid.Clone(); + invalidWeight[0] = double.NaN; + ArgumentOutOfRangeException weightError = + Assert.ThrowsExactly(() => mixture.ValidateParameters(invalidWeight, true)); + Assert.AreEqual("Weights", weightError.ParamName); + + double[] invalidComponent = (double[])valid.Clone(); + invalidComponent[3] = -0.8d; + Assert.IsNotNull(mixture.ValidateParameters(invalidComponent, false)); + } + + private static Mixture CreateTwoNormalMixture(double[] weights) => new Mixture(weights, + new UnivariateDistributionBase[] { new Normal(2d, 0.8d), new Normal(6d, 1.4d) }); + } +} diff --git a/Test_Numerics/Distributions/Univariate/Test_MixtureWeightLogCache.cs b/Test_Numerics/Distributions/Univariate/Test_MixtureWeightLogCache.cs new file mode 100644 index 00000000..a6bbf4e6 --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Test_MixtureWeightLogCache.cs @@ -0,0 +1,224 @@ +using System; +using System.Collections.Generic; +using System.IO; +#if NET8_0_OR_GREATER +using System.Runtime.Serialization; +#else +using System.Runtime.Serialization.Formatters.Binary; +#endif +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Distributions; + +namespace Distributions.Univariate +{ + /// Guards live weight reads, callbacks, and transient caches in mixture density evaluation. + [TestClass] + public class Test_MixtureWeightLogCache + { +#if NET8_0_OR_GREATER + /// Repeated evaluations with unchanged weights do not allocate after initial use. + /// The number of Normal components. + /// Whether to include the hurdle normalization path. + [TestMethod] + [DataRow(2, false)] + [DataRow(3, false)] + [DataRow(2, true)] + public void LogPDF_RepeatedStableWeightsAllocateZeroBytes(int count, bool zeroInflated) + { + var mixture = count == 2 ? Create() : new Mixture(new[] { 0.25d, 0.45d, 0.3d }, + new UnivariateDistributionBase[] { new Normal(2d, 0.8d), new Normal(5d, 1.2d), new Normal(9d, 1.5d) }); + if (zeroInflated) { mixture.IsZeroInflated = true; mixture.ZeroWeight = 0.2d; } + double checksum = 0d; + for (int i = 0; i < 1000; i++) checksum += mixture.LogPDF(3d); + long before = GC.GetAllocatedBytesForCurrentThread(); + for (int i = 0; i < 1000; i++) checksum += mixture.LogPDF(3d); + long allocated = GC.GetAllocatedBytesForCurrentThread() - before; + Assert.IsTrue(Numerics.Tools.IsFinite(checksum)); + Assert.AreEqual(0L, allocated); + } +#endif + + /// In-place, bulk, replacement, and zero-inflation weight changes match a freshly constructed mixture exactly. + [TestMethod] + public void LogPDF_ObservesEveryWeightUpdateExactly() + { + Mixture mixture = Create(); + AssertMatchesFresh(mixture); + mixture.Weights[0] = 0.7d; + mixture.Weights[1] = 0.3d; + AssertMatchesFresh(mixture); + double[] parameters = mixture.GetParameters; + parameters[0] = 0.25d; + parameters[1] = 0.75d; + mixture.SetParameters(parameters); + AssertMatchesFresh(mixture); + mixture.SetParameters(new[] { 0.2d, 0.3d, 0.5d }, + new UnivariateDistributionBase[] { new Normal(1d, 0.8d), new Normal(5d, 1.2d), new Normal(9d, 1.5d) }); + AssertMatchesFresh(mixture); + mixture.SetParameters(new[] { 0.6d, 0.4d }, + new UnivariateDistributionBase[] { new Normal(2d, 0.8d), new Normal(6d, 1.4d) }); + AssertMatchesFresh(mixture); + mixture.IsZeroInflated = true; + mixture.ZeroWeight = 0.2d; + AssertMatchesFresh(mixture); + mixture.ZeroWeight = 0.35d; + AssertMatchesFresh(mixture); + } + + /// Warmed caches do not bypass invalid live weights or change weight-first validation and recovery. + [TestMethod] + public void LogPDF_PreservesInvalidWeightPrecedenceAndRecovery() + { + Mixture mixture = Create(); + AssertMatchesFresh(mixture); + var normal = (Normal)mixture.Distributions[0]; + normal.Mu = double.NaN; + mixture.Weights[0] = double.NaN; + var error = Assert.ThrowsExactly(() => mixture.LogPDF(3d)); + Assert.AreEqual(nameof(Mixture.Weights), error.ParamName); + mixture.Weights[0] = 0.4d; + error = Assert.ThrowsExactly(() => mixture.LogPDF(3d)); + Assert.AreEqual(nameof(Normal.Mu), error.ParamName); + normal.Mu = 2d; + AssertMatchesFresh(mixture); + mixture.Weights[0] = 0d; + mixture.Weights[1] = 1d; + AssertMatchesFresh(mixture); + mixture.Weights[0] = 0.4d; + mixture.Weights[1] = 0.6d; + AssertMatchesFresh(mixture); + } + + /// Each weight is read after its component callback, without anticipating a later component's mutation. + [TestMethod] + public void LogPDF_ReadsWeightAfterEachComponentCallback() + { + var order = new List(); + Mixture mixture = null; + var first = new CallbackDistribution(() => + { order.Add(1); mixture.Weights[0] = 0.25d; mixture.Weights[1] = 0.75d; }); + var second = new CallbackDistribution(() => + { order.Add(2); mixture.Weights[0] = 0.5d; mixture.Weights[1] = 0.5d; }); + mixture = new Mixture(new[] { 0.4d, 0.6d }, new UnivariateDistributionBase[] { first, second }); + for (int i = 0; i < 3; i++) + { + order.Clear(); + Assert.AreEqual(Math.Log(0.75d), mixture.LogPDF(3d), 2E-15); + CollectionAssert.AreEqual(new[] { 1, 2 }, order); + } + } + + /// Weights changed by a callback retain the original logarithm's signed-zero and nonfinite semantics. + [TestMethod] + public void LogPDF_CallbackWeightChangesRetainOriginalLogSemantics() + { + double target = 1d; + Mixture mixture = null; + mixture = new Mixture(new[] { 1d }, new UnivariateDistributionBase[] + { new CallbackDistribution(() => mixture.Weights[0] = target) }); + foreach (double value in new[] { 0.25d, 0d, -0d, -0.5d, double.NaN, + BitConverter.Int64BitsToDouble(0x7FF8000000000123L), double.PositiveInfinity, double.NegativeInfinity, 1d }) + { + target = value; + mixture.Weights[0] = 1d; + double expected = DistributionNumerics.LogSum(double.NegativeInfinity, Math.Log(value) + 0d); + Assert.AreEqual(BitConverter.DoubleToInt64Bits(expected), BitConverter.DoubleToInt64Bits(mixture.LogPDF(3d))); + } + } + + /// A prior callback can activate or deactivate a later component before that component's existing weight check. + [TestMethod] + public void LogPDF_PreservesLiveActivationAndCallbackOrder() + { + var order = new List(); + bool activateSecond = false; + Mixture mixture = null; + var first = new CallbackDistribution(() => + { + order.Add(1); + mixture.Weights[0] = activateSecond ? 0.5d : 1d; + mixture.Weights[1] = activateSecond ? 0.5d : 0d; + }); + var second = new CallbackDistribution(() => order.Add(2)); + mixture = new Mixture(new[] { 0.5d, 0.5d }, new UnivariateDistributionBase[] { first, second }); + Assert.AreEqual(0d, mixture.LogPDF(3d), 0d); + CollectionAssert.AreEqual(new[] { 1 }, order); + activateSecond = true; + order.Clear(); + Assert.AreEqual(0d, mixture.LogPDF(3d), 2E-15); + CollectionAssert.AreEqual(new[] { 1, 2 }, order); + } + + /// Concurrent first use on an unchanged mixture publishes complete weight/log pairs. + [TestMethod] + public void LogPDF_ConcurrentReadOnlyInitializationPreservesExactValues() + { + Mixture mixture = Create(); + long expected = BitConverter.DoubleToInt64Bits(mixture.Clone().LogPDF(3d)); + var results = new double[32]; + Parallel.For(0, results.Length, i => results[i] = mixture.LogPDF(3d)); + foreach (double result in results) Assert.AreEqual(expected, BitConverter.DoubleToInt64Bits(result)); + } + + /// Clone, XML, and field-based deserialization restore transient weight caches and retain later mutations. + [TestMethod] + public void LogPDF_CloneAndSerializationRetainMutableWeightState() + { + Mixture mixture = Create(); + AssertMatchesFresh(mixture); + long expected = BitConverter.DoubleToInt64Bits(mixture.LogPDF(3d)); + var copies = new[] { (Mixture)mixture.Clone(), + (Mixture)UnivariateDistributionFactory.CreateDistribution(mixture.ToXElement()), RoundTrip(mixture) }; + foreach (Mixture copy in copies) + { + Assert.AreEqual(expected, BitConverter.DoubleToInt64Bits(copy.LogPDF(3d))); + copy.Weights[0] = 0.7d; + copy.Weights[1] = 0.3d; + AssertMatchesFresh(copy); + } + } + + private static void AssertMatchesFresh(Mixture mixture) + { + long expected = BitConverter.DoubleToInt64Bits(mixture.Clone().LogPDF(3d)); + for (int i = 0; i < 3; i++) Assert.AreEqual(expected, BitConverter.DoubleToInt64Bits(mixture.LogPDF(3d))); + } + + private static Mixture Create() => new Mixture(new[] { 0.4d, 0.6d }, + new UnivariateDistributionBase[] { new Normal(2d, 0.8d), new Normal(6d, 1.4d) }); + + private static Mixture RoundTrip(Mixture mixture) + { + using var stream = new MemoryStream(); +#if NET8_0_OR_GREATER + var serializer = new DataContractSerializer(typeof(Mixture), new[] { typeof(Normal) }); + serializer.WriteObject(stream, mixture); + stream.Position = 0; + return (Mixture)serializer.ReadObject(stream); +#else + var serializer = new BinaryFormatter(); + serializer.Serialize(stream, mixture); + stream.Position = 0; + return (Mixture)serializer.Deserialize(stream); +#endif + } + + private sealed class CallbackDistribution : Cauchy + { + private readonly Action _callback; + + internal CallbackDistribution(Action callback) => _callback = callback; + + /// + public override double LogPDF(double x) + { + _callback(); + return 0d; + } + + /// + public override UnivariateDistributionBase Clone() => new CallbackDistribution(_callback); + } + } +} diff --git a/Test_Numerics/Distributions/Univariate/Test_NonpositiveConstraintRegressions.cs b/Test_Numerics/Distributions/Univariate/Test_NonpositiveConstraintRegressions.cs new file mode 100644 index 00000000..9fe015d3 --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Test_NonpositiveConstraintRegressions.cs @@ -0,0 +1,67 @@ +using Numerics.Distributions; + +namespace Test_Numerics.Distributions +{ + /// Preserves usable constraint envelopes for nonpositive observations from the pre-hardening baseline. + [TestClass] + public class Test_NonpositiveConstraintRegressions + { + /// Checks literal d80bfa8 initialization and rounded bounds without changing estimation data. + [TestMethod] + [DataRow("GammaDistribution", 0d)] + [DataRow("GammaDistribution", -1d)] + [DataRow("LnNormal", 0d)] + [DataRow("LnNormal", -1d)] + [DataRow("LogNormal", 0d)] + [DataRow("LogNormal", -1d)] + [DataRow("LogPearsonTypeIII", 0d)] + [DataRow("LogPearsonTypeIII", -1d)] + [DataRow("Weibull", 0d)] + [DataRow("Weibull", -1d)] + public void ConstraintsPreserveUsableLegacyNonpositiveSamples(string family, double first) + { + var distribution = (IMaximumLikelihoodEstimation)System.Activator.CreateInstance( + typeof(Normal).Assembly.GetType("Numerics.Distributions." + family))!; + double[] sample = { first, 1d, 10d, 100d }; + var actual = distribution.GetParameterConstraints(sample); + double epsilon = Numerics.Tools.DoubleMachineEpsilon; + double[] initial; + double[] lower; + double[] upper; + switch (family) + { + case "GammaDistribution": + initial = first == 0d ? new[] { 84.33333333333334, 0.3290513833992095 } + : new[] { 85.78181818181818, 0.3205807545570157 }; + lower = new[] { epsilon, epsilon }; + upper = new[] { 1000d, 10d }; + break; + case "LnNormal": + initial = first == 0d ? new[] { 27.75, 48.376130477746976 } + : new[] { 27.5, 48.569537778323564 }; + lower = new[] { epsilon, epsilon }; + upper = new[] { 1000d, 1000d }; + break; + case "LogNormal": + initial = new[] { 0.5000000000000002, 1.2909944487358054 }; + lower = new[] { -10d, epsilon }; + upper = new[] { 10d, 3d }; + break; + case "LogPearsonTypeIII": + initial = new[] { 0.25, 1.707825127659933, -0.7528371991317255 }; + lower = new[] { -10d, epsilon, -6d }; + upper = new[] { 10d, 3d, 6d }; + break; + default: + initial = new[] { 12.336441557126482, 0.493577181580963 }; + lower = new[] { epsilon, epsilon }; + upper = new[] { 1000d, 10d }; + break; + } + CollectionAssert.AreEqual(initial, actual.Item1, "Legacy initialization"); + CollectionAssert.AreEqual(lower, actual.Item2, "Legacy lower bounds"); + CollectionAssert.AreEqual(upper, actual.Item3, "Legacy upper bounds"); + CollectionAssert.AreEqual(new[] { first, 1d, 10d, 100d }, sample, "Observations are never modified"); + } + } +} diff --git a/Test_Numerics/Distributions/Univariate/Test_NormalLogDensityCache.cs b/Test_Numerics/Distributions/Univariate/Test_NormalLogDensityCache.cs new file mode 100644 index 00000000..d00ec31d --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Test_NormalLogDensityCache.cs @@ -0,0 +1,294 @@ +using System; +using System.IO; +#if NET8_0_OR_GREATER +using System.Runtime.Serialization; +#else +using System.Runtime.Serialization.Formatters.Binary; +#endif +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Distributions; + +namespace Distributions.Univariate +{ + /// Guards density and positive-mass normalization when parameter-dependent caches must be refreshed or restored. + [TestClass] + public class Test_NormalLogDensityCache + { + /// Direct and bulk parameter changes refresh the density's scale-dependent value. + [TestMethod] + public void LogPDF_ObservesDirectAndBulkScaleChanges() + { + var normal = new Normal(2d, 0.8d); + Assert.AreEqual(-1.477044981890463d, normal.LogPDF(3d), 2E-15); + normal.Sigma = 1.4d; + Assert.AreEqual(-1.5105128106422123d, normal.LogPDF(3d), 2E-15); + normal.SetParameters(-4d, 2.5d); + Assert.AreEqual(-5.755229265078827d, normal.LogPDF(3d), 8E-15); + normal.SetParameters(new[] { 1d, 4d }); + Assert.AreEqual(-2.4302328943245635d, normal.LogPDF(3d), 4E-15); + } + + /// Invalid scales retain validation behavior and can subsequently be replaced by valid scales. + [TestMethod] + public void LogPDF_RecoversAfterInvalidScaleAndLocationChanges() + { + var normal = new Normal(2d, 0.8d); + foreach (double invalid in new[] { -1d, double.NaN, double.NegativeInfinity, double.PositiveInfinity }) + { + normal.Sigma = invalid; + Assert.IsFalse(normal.ParametersValid); + var error = Assert.ThrowsExactly(() => normal.LogPDF(3d)); + Assert.AreEqual(nameof(Normal.Sigma), error.ParamName); + normal.Sigma = 1.4d; + Assert.IsTrue(normal.ParametersValid); + Assert.AreEqual(-1.5105128106422123d, normal.LogPDF(3d), 2E-15); + } + normal.Mu = double.NaN; + normal.Sigma = 0.8d; + var locationError = Assert.ThrowsExactly(() => normal.LogPDF(3d)); + Assert.AreEqual(nameof(Normal.Mu), locationError.ParamName); + normal.Mu = 2d; + Assert.AreEqual(-1.477044981890463d, normal.LogPDF(3d), 2E-15); + } + + /// The cached value uses the existing normalized scale, including signed zero and tiny positive inputs. + [TestMethod] + public void LogPDF_UsesNormalizedScaleAndRetainsExtremeDensity() + { + var normal = new Normal(2d, 0.8d); + foreach (double scale in new[] { 0d, -0d, double.Epsilon, 1E-20, 1E-16 }) + { + normal.Sigma = scale; + Assert.AreEqual(1E-16, normal.Sigma); + Assert.AreEqual(35.92242295470006d, normal.LogPDF(2d), 2E-14); + } + normal.SetParameters(0d, 1E308); + Assert.AreEqual(-710.1151471753708d, normal.LogPDF(0d), 2E-12); + } + + /// Clones initialize their own scale-dependent density state and remain independent after mutation. + [TestMethod] + public void LogPDF_CloneRetainsIndependentScaleState() + { + var original = new Normal(2d, 0.8d); + _ = original.LogPDF(3d); + var clone = (Normal)original.Clone(); + Assert.AreEqual(-1.477044981890463d, clone.LogPDF(3d), 2E-15); + clone.Sigma = 1.4d; + Assert.AreEqual(-1.5105128106422123d, clone.LogPDF(3d), 2E-15); + Assert.AreEqual(-1.477044981890463d, original.LogPDF(3d), 2E-15); + } + + /// XML round trips preserve parameters and density before and after a subsequent scale change. + [TestMethod] + public void LogPDF_XmlRoundTripRetainsMutableScaleState() + { + var original = new Normal(2d, 0.8d); + _ = original.LogPDF(3d); + var element = original.ToXElement(); + var restored = (Normal)UnivariateDistributionFactory.CreateDistribution(element); + Assert.AreEqual(element.ToString(), restored.ToXElement().ToString()); + Assert.AreEqual(-1.477044981890463d, restored.LogPDF(3d), 2E-15); + restored.Sigma = 1.4d; + Assert.AreEqual(-1.5105128106422123d, restored.LogPDF(3d), 2E-15); + } + + /// Field-based deserialization restores density even when constructors and scale setters do not run. + [TestMethod] + public void LogPDF_SerializableRoundTripInitializesTransientScaleState() + { + var original = new Normal(2d, 0.8d); + _ = original.LogPDF(3d); + var restored = RoundTrip(original); + Assert.AreEqual(-1.477044981890463d, restored.LogPDF(3d), 2E-15); + Assert.AreEqual(-1.477044981890463d, restored.LogPDF(3d), 2E-15); + restored.Sigma = 1.4d; + Assert.AreEqual(-1.5105128106422123d, restored.LogPDF(3d), 2E-15); + + original.Sigma = -1d; + restored = RoundTrip(original); + var error = Assert.ThrowsExactly(() => restored.LogPDF(3d)); + Assert.AreEqual(nameof(Normal.Sigma), error.ParamName); + restored.Sigma = 1.4d; + Assert.AreEqual(-1.5105128106422123d, restored.LogPDF(3d), 2E-15); + } + + /// Repeated zero-survival normalization matches the uncached public method after either parameter changes. + [TestMethod] + public void LogCCDFAtZero_ObservesDirectAndBulkParameterChangesExactly() + { + var normal = new Normal(2d, 0.8d); + AssertZeroSurvivalMatchesUncached(normal); + normal.Mu = -2d; + AssertZeroSurvivalMatchesUncached(normal); + normal.Sigma = 1.4d; + AssertZeroSurvivalMatchesUncached(normal); + normal.SetParameters(-4d, 2.5d); + AssertZeroSurvivalMatchesUncached(normal); + normal.SetParameters(new[] { 1d, 4d }); + AssertZeroSurvivalMatchesUncached(normal); + normal.SetParameters(0d, 0d); + Assert.AreEqual(1E-16, normal.Sigma); + AssertZeroSurvivalMatchesUncached(normal); + normal.SetParameters(-40d, 1d); + Assert.AreEqual(-804.6084420137538d, normal.LogCCDFAtZero(), 2E-12); + AssertZeroSurvivalMatchesUncached(normal); + } + + /// Invalid parameters are rejected before a cached value can be returned and repaired values are observed. + [TestMethod] + public void LogCCDFAtZero_PreservesValidationOrderAndRecovery() + { + var normal = new Normal(2d, 0.8d); + AssertZeroSurvivalMatchesUncached(normal); + normal.Mu = double.NaN; + normal.Sigma = -1d; + var error = Assert.ThrowsExactly(() => normal.LogCCDFAtZero()); + Assert.AreEqual(nameof(Normal.Mu), error.ParamName); + normal.Mu = -2d; + error = Assert.ThrowsExactly(() => normal.LogCCDFAtZero()); + Assert.AreEqual(nameof(Normal.Sigma), error.ParamName); + normal.Sigma = 1.4d; + AssertZeroSurvivalMatchesUncached(normal); + normal.Sigma = double.PositiveInfinity; + Assert.ThrowsExactly(() => normal.LogCCDFAtZero()); + normal.SetParameters(1d, 4d); + AssertZeroSurvivalMatchesUncached(normal); + } + + /// Clone, XML, and field-based serialization retain zero-survival values and subsequent parameter mutation. + [TestMethod] + public void LogCCDFAtZero_CloneAndSerializationRestoreTransientState() + { + var original = new Normal(-2d, 1.4d); + AssertZeroSurvivalMatchesUncached(original); + var copies = new[] + { + (Normal)original.Clone(), + (Normal)UnivariateDistributionFactory.CreateDistribution(original.ToXElement()), + RoundTrip(original) + }; + foreach (Normal copy in copies) + { + Assert.AreEqual(BitConverter.DoubleToInt64Bits(original.LogCCDF(0d)), + BitConverter.DoubleToInt64Bits(copy.LogCCDFAtZero())); + AssertZeroSurvivalMatchesUncached(copy); + copy.Mu = 2d; + AssertZeroSurvivalMatchesUncached(copy); + copy.Sigma = 0.8d; + AssertZeroSurvivalMatchesUncached(copy); + } + original.Mu = double.NaN; + Normal invalid = RoundTrip(original); + Assert.ThrowsExactly(() => invalid.LogCCDFAtZero()); + invalid.Mu = 2d; + AssertZeroSurvivalMatchesUncached(invalid); + } + + /// Zero-inflated density retains exact uncached normalization after live Normal parameter changes. + [TestMethod] + public void ZeroInflatedLogPDF_ObservesNormalParameterChangesExactly() + { + var mixture = new Mixture(new[] { 1d }, new UnivariateDistributionBase[] { new Normal(2d, 0.8d) }) + { IsZeroInflated = true, ZeroWeight = 0.2d }; + var normal = (Normal)mixture.Distributions[0]; + AssertSingleComponentNormalization(mixture); + normal.Mu = -2d; + AssertSingleComponentNormalization(mixture); + normal.Sigma = 1.4d; + AssertSingleComponentNormalization(mixture); + normal.SetParameters(-4d, 2.5d); + AssertSingleComponentNormalization(mixture); + normal.SetParameters(new[] { 1d, 4d }); + AssertSingleComponentNormalization(mixture); + normal.Mu = double.NaN; + Assert.ThrowsExactly(() => mixture.LogPDF(0d)); + normal.Mu = 2d; + AssertSingleComponentNormalization(mixture); + } + + /// Cached positive mass cannot bypass weight, inactive-parameter, or component-order validation. + [TestMethod] + public void ZeroInflatedLogPDF_PreservesLiveValidationPrecedence() + { + var mixture = new Mixture(new[] { 0.4d, 0.6d }, + new UnivariateDistributionBase[] { new Normal(2d, 0.8d), new Normal(6d, 1.4d) }) + { IsZeroInflated = true, ZeroWeight = 0.2d }; + var first = (Normal)mixture.Distributions[0]; + var second = (Normal)mixture.Distributions[1]; + _ = mixture.LogPDF(3d); + first.SetParameters(-1E308, 1E-16); + second.Sigma = -1d; + mixture.Weights[0] = double.NaN; + var error = Assert.ThrowsExactly(() => mixture.LogPDF(3d)); + Assert.AreEqual(nameof(Mixture.Weights), error.ParamName); + mixture.Weights[0] = 0.32d; + error = Assert.ThrowsExactly(() => mixture.LogPDF(3d)); + Assert.AreEqual(nameof(Mixture.Distributions), error.ParamName); + StringAssert.Contains(error.Message, "positive probability above zero"); + mixture.Weights[0] = 0d; + mixture.Weights[1] = 0.8d; + first.Mu = double.NaN; + error = Assert.ThrowsExactly(() => mixture.LogPDF(3d)); + Assert.AreEqual(nameof(Normal.Mu), error.ParamName); + first.Mu = -1E308; + error = Assert.ThrowsExactly(() => mixture.LogPDF(3d)); + Assert.AreEqual(nameof(Normal.Sigma), error.ParamName); + second.Sigma = 1.4d; + Assert.IsTrue(Numerics.Tools.IsFinite(mixture.LogPDF(3d))); + } + + /// Non-Normal mixture components continue to evaluate their live zero-survival call after nested mutation. + [TestMethod] + public void ZeroInflatedLogPDF_PreservesNestedComponentNormalization() + { + var inner = new Mixture(new[] { 1d }, new UnivariateDistributionBase[] { new Normal(-2d, 1.4d) }); + var outer = new Mixture(new[] { 1d }, new UnivariateDistributionBase[] { inner }) + { IsZeroInflated = true, ZeroWeight = 0.2d }; + var nested = (Mixture)outer.Distributions[0]; + var normal = (Normal)nested.Distributions[0]; + AssertSingleComponentNormalization(outer); + normal.Mu = 2d; + AssertSingleComponentNormalization(outer); + normal.Sigma = 0.8d; + AssertSingleComponentNormalization(outer); + normal.Sigma = -1d; + Assert.ThrowsExactly(() => outer.LogPDF(3d)); + normal.Sigma = 1.4d; + AssertSingleComponentNormalization(outer); + } + + private static void AssertZeroSurvivalMatchesUncached(Normal normal) + { + long expected = BitConverter.DoubleToInt64Bits(normal.LogCCDF(0d)); + for (int i = 0; i < 3; i++) + Assert.AreEqual(expected, BitConverter.DoubleToInt64Bits(normal.LogCCDFAtZero())); + } + + private static void AssertSingleComponentNormalization(Mixture mixture) + { + var component = mixture.Distributions[0]; + double expected = Math.Log(mixture.Weights[0]) + (component.LogPDF(3d) - component.LogCCDF(0d)); + for (int i = 0; i < 3; i++) + Assert.AreEqual(BitConverter.DoubleToInt64Bits(expected), BitConverter.DoubleToInt64Bits(mixture.LogPDF(3d))); + } + + private static Normal RoundTrip(Normal original) + { + using var stream = new MemoryStream(); +#if NET8_0_OR_GREATER + // Honors Serializable/NonSerialized field contracts without enabling the obsolete BinaryFormatter. + var serializer = new DataContractSerializer(typeof(Normal)); + serializer.WriteObject(stream, original); + stream.Position = 0; + return (Normal)serializer.ReadObject(stream); +#else + // The legacy target still supports the actual existing binary serialization contract. + var serializer = new BinaryFormatter(); + serializer.Serialize(stream, original); + stream.Position = 0; + return (Normal)serializer.Deserialize(stream); +#endif + } + } +} diff --git a/Test_Numerics/Distributions/Univariate/Test_NormalMixtureValidationAllocations.cs b/Test_Numerics/Distributions/Univariate/Test_NormalMixtureValidationAllocations.cs new file mode 100644 index 00000000..7eb02fac --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Test_NormalMixtureValidationAllocations.cs @@ -0,0 +1,114 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Distributions; + +namespace Distributions.Univariate +{ + /// Guards allocation-free live Normal validation without weakening mixture validation contracts. + [TestClass] + public class Test_NormalMixtureValidationAllocations + { +#if NET8_0_OR_GREATER + /// Repeated Normal mixture densities validate live parameters without allocating parameter arrays. + /// Whether to exercise the positive-conditional mixture path. + [TestMethod] + [DataRow(false)] + [DataRow(true)] + public void LogPDF_LiveNormalValidationAllocatesZeroBytes(bool zeroInflated) + { + Mixture mixture = Create(); + if (zeroInflated) { mixture.IsZeroInflated = true; mixture.ZeroWeight = 0.2d; } + double checksum = 0d; + for (int i = 0; i < 1000; i++) checksum += mixture.LogPDF(3d); + + long before = GC.GetAllocatedBytesForCurrentThread(); + for (int i = 0; i < 1000; i++) checksum += mixture.LogPDF(3d); + long allocated = GC.GetAllocatedBytesForCurrentThread() - before; + + Assert.IsTrue(double.IsFinite(checksum)); + Assert.AreEqual(0L, allocated, $"1000 evaluations allocated {allocated} bytes ({allocated / 1000d} bytes/call)."); + } +#endif + + /// Live scalar validation preserves weight-first and component-order errors and observes repaired values. + [TestMethod] + public void LogPDF_PreservesErrorOrderAndLiveRecovery() + { + Mixture mixture = Create(); + var first = (Normal)mixture.Distributions[0]; + var second = (Normal)mixture.Distributions[1]; + first.Mu = double.NaN; + first.Sigma = -1d; + second.Sigma = -2d; + mixture.Weights[0] = double.NaN; + var weightError = Assert.ThrowsExactly(() => mixture.LogPDF(3d)); + Assert.AreEqual("Weights", weightError.ParamName); + + mixture.Weights[0] = 0.4d; + AssertScalarError(mixture, first); + first.Mu = 2d; + AssertScalarError(mixture, first); + first.Sigma = 0.8d; + AssertScalarError(mixture, second); + second.Sigma = 1.4d; + Assert.IsFalse(double.IsNaN(mixture.LogPDF(3d))); + mixture.Distributions[0] = new Normal(double.PositiveInfinity, 1d); + AssertScalarError(mixture, (Normal)mixture.Distributions[0]); + } + + /// Zero-weight Normal components remain validated even when their density will not contribute. + [TestMethod] + public void LogPDF_ValidatesInactiveNormalComponents() + { + Mixture mixture = Create(); + mixture.Weights[0] = 1d; + mixture.Weights[1] = 0d; + var inactive = (Normal)mixture.Distributions[1]; + inactive.Mu = double.NaN; + AssertScalarError(mixture, inactive); + } + + /// The public candidate validator uses supplied parameters independently of current Normal state. + [TestMethod] + public void ValidateParameters_PreservesCandidateIndependence() + { + Mixture mixture = Create(); + double[] validCandidate = mixture.GetParameters; + double[] invalidCandidate = (double[])validCandidate.Clone(); + invalidCandidate[3] = -0.8d; + var candidateError = mixture.ValidateParameters(invalidCandidate, false); + Assert.IsNotNull(candidateError); + Assert.AreEqual("Sigma", candidateError.ParamName); + Assert.IsNull(mixture.ValidateParameters(validCandidate, false)); + + ((Normal)mixture.Distributions[0]).Mu = double.NaN; + Assert.IsNull(mixture.ValidateParameters(validCandidate, false)); + Assert.ThrowsExactly(() => mixture.LogPDF(3d)); + } + + /// Non-Normal component delegation still detects a directly mutated nested Normal. + [TestMethod] + public void LogPDF_PreservesNestedMutationValidation() + { + Mixture inner = Create(); + var outer = new Mixture(new[] { 0.5d, 0.5d }, new UnivariateDistributionBase[] { inner, new Normal(10d, 2d) }); + var nested = (Mixture)outer.Distributions[0]; + ((Normal)nested.Distributions[1]).Sigma = -1d; + var error = Assert.ThrowsExactly(() => outer.LogPDF(3d)); + Assert.AreEqual("Sigma", error.ParamName); + } + + private static void AssertScalarError(Mixture mixture, Normal normal) + { + var expected = normal.ValidateParameters(normal.Mu, normal.Sigma, false); + Assert.IsNotNull(expected); + var actual = Assert.ThrowsExactly(() => mixture.LogPDF(3d)); + Assert.AreEqual(expected.ParamName, actual.ParamName); + Assert.AreEqual(expected.Message, actual.Message); + Assert.AreEqual(expected.ActualValue, actual.ActualValue); + } + + private static Mixture Create() => new Mixture(new[] { 0.4d, 0.6d }, + new UnivariateDistributionBase[] { new Normal(2d, 0.8d), new Normal(6d, 1.4d) }); + } +} diff --git a/docs/distributions/bestfit-regression-inventory.json b/docs/distributions/bestfit-regression-inventory.json new file mode 100644 index 00000000..5821ffbe --- /dev/null +++ b/docs/distributions/bestfit-regression-inventory.json @@ -0,0 +1,5251 @@ +{ + "approved": "2026-09-08", + "numericsBaseline": "d80bfa8621c48a78cf4ebad7f01326841fac37aa", + "bestFitBaseline": "fbe0989a87a0bd38a297f8a42e8d979585d0f4ad", + "numericsStart": "a3bd2afd64286e1b8df3ada8be711d55180cbb11", + "bestFitStart": "3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a", + "methods": [ + { + "method": "RMC.BestFit.Verification.Bivariate.BivariateAnalysisParameterRecoveryTests.RecoverAMHCopulaParameters", + "source": "src/RMC.BestFit.Verification/Bivariate/BivariateAnalysisParameterRecoveryTests.cs", + "model": "AMH copula with two Normal marginals", + "analysis": "BivariateAnalysis", + "area": "Bivariate", + "declaredDependencies": [ + "Gumbel", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Bivariate.BivariateAnalysisParameterRecoveryTests.RecoverClaytonCopulaParameters", + "source": "src/RMC.BestFit.Verification/Bivariate/BivariateAnalysisParameterRecoveryTests.cs", + "model": "Clayton copula with two Normal marginals", + "analysis": "BivariateAnalysis", + "area": "Bivariate", + "declaredDependencies": [ + "Gumbel", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Bivariate.BivariateAnalysisParameterRecoveryTests.RecoverFrankCopulaParameters", + "source": "src/RMC.BestFit.Verification/Bivariate/BivariateAnalysisParameterRecoveryTests.cs", + "model": "Frank copula with two Normal marginals", + "analysis": "BivariateAnalysis", + "area": "Bivariate", + "declaredDependencies": [ + "Gumbel", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Bivariate.BivariateAnalysisParameterRecoveryTests.RecoverGumbelCopulaParameters", + "source": "src/RMC.BestFit.Verification/Bivariate/BivariateAnalysisParameterRecoveryTests.cs", + "model": "Gumbel copula with two Normal marginals", + "analysis": "BivariateAnalysis", + "area": "Bivariate", + "declaredDependencies": [ + "Gumbel", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Bivariate.BivariateAnalysisParameterRecoveryTests.RecoverJoeCopulaParameters", + "source": "src/RMC.BestFit.Verification/Bivariate/BivariateAnalysisParameterRecoveryTests.cs", + "model": "Joe copula with two Normal marginals", + "analysis": "BivariateAnalysis", + "area": "Bivariate", + "declaredDependencies": [ + "Gumbel", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Bivariate.BivariateAnalysisParameterRecoveryTests.RecoverNormalCopulaParameters", + "source": "src/RMC.BestFit.Verification/Bivariate/BivariateAnalysisParameterRecoveryTests.cs", + "model": "Normal copula with two Normal marginals", + "analysis": "BivariateAnalysis", + "area": "Bivariate", + "declaredDependencies": [ + "Gumbel", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Bivariate.BivariateAnalysisParameterRecoveryTests.RecoverStudentTCopulaParametersWithMaximumLikelihood", + "source": "src/RMC.BestFit.Verification/Bivariate/BivariateAnalysisParameterRecoveryTests.cs", + "model": "StudentT copula with two Normal marginals", + "analysis": "BivariateDistribution maximum likelihood", + "area": "Bivariate", + "declaredDependencies": [ + "Gumbel", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Bivariate.CoincidentFrequencyAnalysisTests.ExponentialLinearCombination_ParentResponseInsidePredictiveBands", + "source": "src/RMC.BestFit.Verification/Bivariate/CoincidentFrequencyAnalysisTests.cs", + "model": "monotone nonlinear exp(0.01 X + 0.01 Y) response under Normal marginals and positive Gaussian-copula dependence", + "analysis": "CoincidentFrequencyAnalysis", + "area": "Bivariate", + "declaredDependencies": [ + "Exponential", + "LogNormal", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Bivariate.CoincidentFrequencyAnalysisTests.SumOfNormals_RhoNegative_MatchesClosedForm", + "source": "src/RMC.BestFit.Verification/Bivariate/CoincidentFrequencyAnalysisTests.cs", + "model": "sum of correlated Normal marginals; negative rho", + "analysis": "CoincidentFrequencyAnalysis", + "area": "Bivariate", + "declaredDependencies": [ + "Exponential", + "LogNormal", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Bivariate.CoincidentFrequencyAnalysisTests.SumOfNormals_RhoPositive_MatchesClosedForm", + "source": "src/RMC.BestFit.Verification/Bivariate/CoincidentFrequencyAnalysisTests.cs", + "model": "sum of correlated Normal marginals; positive rho", + "analysis": "CoincidentFrequencyAnalysis", + "area": "Bivariate", + "declaredDependencies": [ + "Exponential", + "LogNormal", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Bivariate.CoincidentFrequencyAnalysisTests.SumOfNormals_RhoZero_MatchesClosedForm", + "source": "src/RMC.BestFit.Verification/Bivariate/CoincidentFrequencyAnalysisTests.cs", + "model": "sum of correlated Normal marginals; rho=0", + "analysis": "CoincidentFrequencyAnalysis", + "area": "Bivariate", + "declaredDependencies": [ + "Exponential", + "LogNormal", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.AliMikhailHaq_InferenceFromMargins_MatchesIndependentOptimum", + "source": "src/RMC.BestFit.Verification/Bivariate/CopulaEstimationOracleTests.cs", + "model": "AliMikhailHaq copula; inference from margins", + "analysis": "BivariateDistribution maximum likelihood", + "area": "Bivariate", + "declaredDependencies": [ + "Gumbel", + "Normal", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.AliMikhailHaq_PseudoLikelihood_MatchesIndependentOptimum", + "source": "src/RMC.BestFit.Verification/Bivariate/CopulaEstimationOracleTests.cs", + "model": "AliMikhailHaq copula; maximum pseudo-likelihood", + "analysis": "BivariateDistribution maximum likelihood", + "area": "Bivariate", + "declaredDependencies": [ + "Gumbel", + "Normal", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Clayton_InferenceFromMargins_MatchesIndependentOptimum", + "source": "src/RMC.BestFit.Verification/Bivariate/CopulaEstimationOracleTests.cs", + "model": "Clayton copula; inference from margins", + "analysis": "BivariateDistribution maximum likelihood", + "area": "Bivariate", + "declaredDependencies": [ + "Gumbel", + "Normal", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Clayton_PseudoLikelihood_MatchesIndependentOptimum", + "source": "src/RMC.BestFit.Verification/Bivariate/CopulaEstimationOracleTests.cs", + "model": "Clayton copula; maximum pseudo-likelihood", + "analysis": "BivariateDistribution maximum likelihood", + "area": "Bivariate", + "declaredDependencies": [ + "Gumbel", + "Normal", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Frank_InferenceFromMargins_MatchesIndependentOptimum", + "source": "src/RMC.BestFit.Verification/Bivariate/CopulaEstimationOracleTests.cs", + "model": "Frank copula; inference from margins", + "analysis": "BivariateDistribution maximum likelihood", + "area": "Bivariate", + "declaredDependencies": [ + "Gumbel", + "Normal", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Frank_PseudoLikelihood_MatchesIndependentOptimum", + "source": "src/RMC.BestFit.Verification/Bivariate/CopulaEstimationOracleTests.cs", + "model": "Frank copula; maximum pseudo-likelihood", + "analysis": "BivariateDistribution maximum likelihood", + "area": "Bivariate", + "declaredDependencies": [ + "Gumbel", + "Normal", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Gumbel_InferenceFromMargins_MatchesIndependentOptimum", + "source": "src/RMC.BestFit.Verification/Bivariate/CopulaEstimationOracleTests.cs", + "model": "Gumbel copula; inference from margins", + "analysis": "BivariateDistribution maximum likelihood", + "area": "Bivariate", + "declaredDependencies": [ + "Gumbel", + "Normal", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Gumbel_PseudoLikelihood_MatchesIndependentOptimum", + "source": "src/RMC.BestFit.Verification/Bivariate/CopulaEstimationOracleTests.cs", + "model": "Gumbel copula; maximum pseudo-likelihood", + "analysis": "BivariateDistribution maximum likelihood", + "area": "Bivariate", + "declaredDependencies": [ + "Gumbel", + "Normal", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Joe_InferenceFromMargins_MatchesIndependentOptimum", + "source": "src/RMC.BestFit.Verification/Bivariate/CopulaEstimationOracleTests.cs", + "model": "Joe copula; inference from margins", + "analysis": "BivariateDistribution maximum likelihood", + "area": "Bivariate", + "declaredDependencies": [ + "Gumbel", + "Normal", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Joe_PseudoLikelihood_MatchesIndependentOptimum", + "source": "src/RMC.BestFit.Verification/Bivariate/CopulaEstimationOracleTests.cs", + "model": "Joe copula; maximum pseudo-likelihood", + "analysis": "BivariateDistribution maximum likelihood", + "area": "Bivariate", + "declaredDependencies": [ + "Gumbel", + "Normal", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Normal_InferenceFromMargins_MatchesIndependentOptimum", + "source": "src/RMC.BestFit.Verification/Bivariate/CopulaEstimationOracleTests.cs", + "model": "Normal copula; inference from margins", + "analysis": "BivariateDistribution maximum likelihood", + "area": "Bivariate", + "declaredDependencies": [ + "Gumbel", + "Normal", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Normal_PseudoLikelihood_MatchesIndependentOptimum", + "source": "src/RMC.BestFit.Verification/Bivariate/CopulaEstimationOracleTests.cs", + "model": "Normal copula; maximum pseudo-likelihood", + "analysis": "BivariateDistribution maximum likelihood", + "area": "Bivariate", + "declaredDependencies": [ + "Gumbel", + "Normal", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.StudentT_InferenceFromMargins_MatchesIndependentOptimum", + "source": "src/RMC.BestFit.Verification/Bivariate/CopulaEstimationOracleTests.cs", + "model": "StudentT copula; inference from margins", + "analysis": "BivariateDistribution maximum likelihood", + "area": "Bivariate", + "declaredDependencies": [ + "Gumbel", + "Normal", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.StudentT_PseudoLikelihood_MatchesIndependentOptimum", + "source": "src/RMC.BestFit.Verification/Bivariate/CopulaEstimationOracleTests.cs", + "model": "StudentT copula; maximum pseudo-likelihood", + "analysis": "BivariateDistribution maximum likelihood", + "area": "Bivariate", + "declaredDependencies": [ + "Gumbel", + "Normal", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.FittingAnalysisCriteriaVerificationTests.CriteriaRankingWeightsAndConfiguredOrder_MatchIndependentOracle", + "source": "src/RMC.BestFit.Verification/DistributionFitting/FittingAnalysisCriteriaVerificationTests.cs", + "model": "Gumbel, Normal, and Logistic criteria/ranking/weights", + "analysis": "FittingAnalysis", + "area": "DistributionFitting", + "declaredDependencies": [ + "Gumbel", + "Logistic", + "Normal", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.FittingAnalysisCriteriaVerificationTests.FittedParameters_MatchIndependentOracle", + "source": "src/RMC.BestFit.Verification/DistributionFitting/FittingAnalysisCriteriaVerificationTests.cs", + "model": "Gumbel, Normal, and Logistic candidates on common data", + "analysis": "FittingAnalysis", + "area": "DistributionFitting", + "declaredDependencies": [ + "Gumbel", + "Logistic", + "Normal", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Exponential_N1000_FittingAnalysisRecoversGeneratingFamily", + "source": "src/RMC.BestFit.Verification/DistributionFitting/FittingAnalysisRecoveryTests.cs", + "model": "Exponential(xi=10, alpha=25)", + "analysis": "FittingAnalysis", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Gamma_N1000_FittingAnalysisRecoversGeneratingFamily", + "source": "src/RMC.BestFit.Verification/DistributionFitting/FittingAnalysisRecoveryTests.cs", + "model": "Gamma(theta=5, kappa=3)", + "analysis": "FittingAnalysis", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.GeneralizedExtremeValue_N1000_FittingAnalysisRecoversGeneratingFamily", + "source": "src/RMC.BestFit.Verification/DistributionFitting/FittingAnalysisRecoveryTests.cs", + "model": "GeneralizedExtremeValue(xi=50, alpha=15, kappa=0.1)", + "analysis": "FittingAnalysis", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.GeneralizedLogistic_N1000_FittingAnalysisRecoversGeneratingFamily", + "source": "src/RMC.BestFit.Verification/DistributionFitting/FittingAnalysisRecoveryTests.cs", + "model": "GeneralizedLogistic(xi=75, alpha=10, kappa=0.15)", + "analysis": "FittingAnalysis", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.GeneralizedNormal_N1000_FittingAnalysisRecoversGeneratingFamily", + "source": "src/RMC.BestFit.Verification/DistributionFitting/FittingAnalysisRecoveryTests.cs", + "model": "GeneralizedNormal(xi=50, alpha=10, kappa=-0.3)", + "analysis": "FittingAnalysis", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.GeneralizedPareto_N1000_FittingAnalysisRecoversGeneratingFamily", + "source": "src/RMC.BestFit.Verification/DistributionFitting/FittingAnalysisRecoveryTests.cs", + "model": "GeneralizedPareto(xi=0, alpha=20, kappa=0.15)", + "analysis": "FittingAnalysis", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Gumbel_N1000_FittingAnalysisRecoversGeneratingFamily", + "source": "src/RMC.BestFit.Verification/DistributionFitting/FittingAnalysisRecoveryTests.cs", + "model": "Gumbel(xi=50, alpha=15)", + "analysis": "FittingAnalysis", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.KappaFour_N1000_FittingAnalysisRecoversGeneratingFamily", + "source": "src/RMC.BestFit.Verification/DistributionFitting/FittingAnalysisRecoveryTests.cs", + "model": "KappaFour(xi=100, alpha=20, kappa=-0.1, hondo=0.1)", + "analysis": "FittingAnalysis", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily", + "source": "src/RMC.BestFit.Verification/DistributionFitting/FittingAnalysisRecoveryTests.cs", + "model": "LnNormal(real-space mean=3.5, real-space standard deviation=0.4; covariance coordinates Mu, Sigma^2)", + "analysis": "FittingAnalysis", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Logistic_N1000_FittingAnalysisRecoversGeneratingFamily", + "source": "src/RMC.BestFit.Verification/DistributionFitting/FittingAnalysisRecoveryTests.cs", + "model": "Logistic(xi=75, alpha=10)", + "analysis": "FittingAnalysis", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.LogNormal_N1000_FittingAnalysisRecoversGeneratingFamily", + "source": "src/RMC.BestFit.Verification/DistributionFitting/FittingAnalysisRecoveryTests.cs", + "model": "LogNormal base-10(mu=3, sigma=0.5)", + "analysis": "FittingAnalysis", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily", + "source": "src/RMC.BestFit.Verification/DistributionFitting/FittingAnalysisRecoveryTests.cs", + "model": "LogPearsonTypeIII(moment coordinates mu=2, sigma=0.3, gamma=0.5; MLE covariance coordinates Mu, 1/Beta, Alpha)", + "analysis": "FittingAnalysis", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Normal_N1000_FittingAnalysisRecoversGeneratingFamily", + "source": "src/RMC.BestFit.Verification/DistributionFitting/FittingAnalysisRecoveryTests.cs", + "model": "Normal(mu=100, sigma=15)", + "analysis": "FittingAnalysis", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily", + "source": "src/RMC.BestFit.Verification/DistributionFitting/FittingAnalysisRecoveryTests.cs", + "model": "PearsonTypeIII(moment coordinates mu=100, sigma=20, gamma=0.8; MLE covariance coordinates Mu, 1/Beta, Alpha)", + "analysis": "FittingAnalysis", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Weibull_N1000_FittingAnalysisRecoversGeneratingFamily", + "source": "src/RMC.BestFit.Verification/DistributionFitting/FittingAnalysisRecoveryTests.cs", + "model": "Weibull(lambda=100, kappa=2.5)", + "analysis": "FittingAnalysis", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.KappaFourZeroShapeVerificationTests.ZeroShapeNonzeroHondo_PdfMatchesAnalyticalDerivative", + "source": "src/RMC.BestFit.Verification/DistributionFitting/KappaFourZeroShapeVerificationTests.cs", + "model": "KappaFour (xi=0, alpha=1, kappa=0, hondo=0.2)", + "analysis": "Distribution function evaluation", + "area": "DistributionFitting", + "declaredDependencies": [ + "KappaFour" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.KappaFourZeroShapeVerificationTests.ZeroShapeNonzeroHondo_QuantileInvertsCdf", + "source": "src/RMC.BestFit.Verification/DistributionFitting/KappaFourZeroShapeVerificationTests.cs", + "model": "KappaFour (xi=0, alpha=1, kappa=0, hondo=0.2)", + "analysis": "Distribution function evaluation", + "area": "DistributionFitting", + "declaredDependencies": [ + "KappaFour" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.LmomcoDistributionFittingVerificationTests.GeneralizedLogistic_MleAndDistributionFunctionsMatchLmomco", + "source": "src/RMC.BestFit.Verification/DistributionFitting/LmomcoDistributionFittingVerificationTests.cs", + "model": "UnivariateDistribution (GeneralizedLogistic)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "GeneralizedLogistic", + "GeneralizedNormal", + "Logistic", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.LmomcoDistributionFittingVerificationTests.GeneralizedNormal_MleAndDistributionFunctionsMatchLmomco", + "source": "src/RMC.BestFit.Verification/DistributionFitting/LmomcoDistributionFittingVerificationTests.cs", + "model": "UnivariateDistribution (GeneralizedNormal)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "GeneralizedLogistic", + "GeneralizedNormal", + "Logistic", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.Log10NormalFittingVerificationTests.ClosedFormMle_LikelihoodCdfAndQuantileMatchAnalyticalOracle", + "source": "src/RMC.BestFit.Verification/DistributionFitting/Log10NormalFittingVerificationTests.cs", + "model": "UnivariateDistribution (LogNormal, base-10)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "LogNormal", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Exponential_MleAndDistributionFunctionsMatchScipy", + "source": "src/RMC.BestFit.Verification/DistributionFitting/ScipyDistributionFittingVerificationTests.cs", + "model": "UnivariateDistribution (Exponential)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Gamma_MleAndDistributionFunctionsMatchScipy", + "source": "src/RMC.BestFit.Verification/DistributionFitting/ScipyDistributionFittingVerificationTests.cs", + "model": "UnivariateDistribution (GammaDistribution)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.GeneralizedExtremeValue_MleAndDistributionFunctionsMatchScipy", + "source": "src/RMC.BestFit.Verification/DistributionFitting/ScipyDistributionFittingVerificationTests.cs", + "model": "UnivariateDistribution (GeneralizedExtremeValue)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.GeneralizedPareto_MleAndDistributionFunctionsMatchScipy", + "source": "src/RMC.BestFit.Verification/DistributionFitting/ScipyDistributionFittingVerificationTests.cs", + "model": "UnivariateDistribution (GeneralizedPareto)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Gumbel_MleAndDistributionFunctionsMatchScipy", + "source": "src/RMC.BestFit.Verification/DistributionFitting/ScipyDistributionFittingVerificationTests.cs", + "model": "UnivariateDistribution (Gumbel)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.KappaFour_MleAndDistributionFunctionsMatchScipy", + "source": "src/RMC.BestFit.Verification/DistributionFitting/ScipyDistributionFittingVerificationTests.cs", + "model": "UnivariateDistribution (KappaFour)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.LnNormal_MleAndDistributionFunctionsMatchScipy", + "source": "src/RMC.BestFit.Verification/DistributionFitting/ScipyDistributionFittingVerificationTests.cs", + "model": "UnivariateDistribution (LnNormal)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Logistic_MleAndDistributionFunctionsMatchScipy", + "source": "src/RMC.BestFit.Verification/DistributionFitting/ScipyDistributionFittingVerificationTests.cs", + "model": "UnivariateDistribution (Logistic)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.LogNormal_MleAndDistributionFunctionsMatchScipy", + "source": "src/RMC.BestFit.Verification/DistributionFitting/ScipyDistributionFittingVerificationTests.cs", + "model": "UnivariateDistribution (LogNormal)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.LogPearsonTypeIII_MleAndDistributionFunctionsMatchScipy", + "source": "src/RMC.BestFit.Verification/DistributionFitting/ScipyDistributionFittingVerificationTests.cs", + "model": "UnivariateDistribution (LogPearsonTypeIII)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Normal_MleAndDistributionFunctionsMatchScipy", + "source": "src/RMC.BestFit.Verification/DistributionFitting/ScipyDistributionFittingVerificationTests.cs", + "model": "UnivariateDistribution (Normal)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.PearsonTypeIII_MleAndDistributionFunctionsMatchScipy", + "source": "src/RMC.BestFit.Verification/DistributionFitting/ScipyDistributionFittingVerificationTests.cs", + "model": "UnivariateDistribution (PearsonTypeIII)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Weibull_MleAndDistributionFunctionsMatchScipy", + "source": "src/RMC.BestFit.Verification/DistributionFitting/ScipyDistributionFittingVerificationTests.cs", + "model": "UnivariateDistribution (Weibull)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_Exponential_MLE", + "source": "src/RMC.BestFit.Verification/DistributionFitting/UnivariateDistributionMLETests.cs", + "model": "Exponential (xi, alpha)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_GammaDist_MLE", + "source": "src/RMC.BestFit.Verification/DistributionFitting/UnivariateDistributionMLETests.cs", + "model": "GammaDistribution (theta, kappa)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_GeneralizedExtremeValue_MLE", + "source": "src/RMC.BestFit.Verification/DistributionFitting/UnivariateDistributionMLETests.cs", + "model": "GeneralizedExtremeValue (xi, alpha, kappa)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_GeneralizedLogistic_MLE", + "source": "src/RMC.BestFit.Verification/DistributionFitting/UnivariateDistributionMLETests.cs", + "model": "GeneralizedLogistic (xi, alpha, kappa)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_GeneralizedNormal_MLE", + "source": "src/RMC.BestFit.Verification/DistributionFitting/UnivariateDistributionMLETests.cs", + "model": "GeneralizedNormal (xi, alpha, kappa)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_GeneralizedPareto_MLE", + "source": "src/RMC.BestFit.Verification/DistributionFitting/UnivariateDistributionMLETests.cs", + "model": "GeneralizedPareto (xi, alpha, kappa)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_Gumbel_MLE", + "source": "src/RMC.BestFit.Verification/DistributionFitting/UnivariateDistributionMLETests.cs", + "model": "Gumbel (xi, alpha)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_Kappa4_MLE", + "source": "src/RMC.BestFit.Verification/DistributionFitting/UnivariateDistributionMLETests.cs", + "model": "KappaFour (xi, alpha, kappa, hondo)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_LnNormal_MLE", + "source": "src/RMC.BestFit.Verification/DistributionFitting/UnivariateDistributionMLETests.cs", + "model": "LnNormal (natural-log mu, sigma)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_Logistic_MLE", + "source": "src/RMC.BestFit.Verification/DistributionFitting/UnivariateDistributionMLETests.cs", + "model": "Logistic (xi, alpha)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_LogNormal_MLE", + "source": "src/RMC.BestFit.Verification/DistributionFitting/UnivariateDistributionMLETests.cs", + "model": "LogNormal (base-10-log mu, sigma)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_LogPearsonTypeIII_MLE", + "source": "src/RMC.BestFit.Verification/DistributionFitting/UnivariateDistributionMLETests.cs", + "model": "LogPearsonTypeIII (base-10-log mu, sigma, gamma)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_Normal_MLE", + "source": "src/RMC.BestFit.Verification/DistributionFitting/UnivariateDistributionMLETests.cs", + "model": "Normal (mu, sigma)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_PearsonTypeIII_MLE", + "source": "src/RMC.BestFit.Verification/DistributionFitting/UnivariateDistributionMLETests.cs", + "model": "PearsonTypeIII (mu, sigma, gamma)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_Weibull_MLE", + "source": "src/RMC.BestFit.Verification/DistributionFitting/UnivariateDistributionMLETests.cs", + "model": "Weibull (lambda, kappa)", + "analysis": "MaximumLikelihood", + "area": "DistributionFitting", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.BayesianAnalysisRecoveryTests.Test_Estimate_PosteriorMean_NearTrueValue", + "source": "src/RMC.BestFit.Verification/ModelEstimation/BayesianAnalysisRecoveryTests.cs", + "model": "UnivariateDistribution (Normal)", + "analysis": "BayesianAnalysis", + "area": "ModelEstimation", + "declaredDependencies": [ + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.BayesianAnalysisRecoveryTests.Test_N1000_DefaultPosteriorIntervals_ContainGeneratingParameters", + "source": "src/RMC.BestFit.Verification/ModelEstimation/BayesianAnalysisRecoveryTests.cs", + "model": "UnivariateDistribution (Normal)", + "analysis": "BayesianAnalysis", + "area": "ModelEstimation", + "declaredDependencies": [ + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.BayesianConjugateOracleVerificationTests.InformativePrior_NormalNormalConjugatePosterior_MatchesClosedForm", + "source": "src/RMC.BestFit.Verification/ModelEstimation/BayesianConjugateOracleVerificationTests.cs", + "model": "Known-scale Normal mean with Normal(120,5) location prior", + "analysis": "BayesianAnalysis", + "area": "ModelEstimation", + "declaredDependencies": [ + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.GmmInfluenceDiagnosticsVerificationTests.Log10NormalObservationInfluence_MatchesRGmmOracle", + "source": "src/RMC.BestFit.Verification/ModelEstimation/GmmInfluenceDiagnosticsVerificationTests.cs", + "model": "Bulletin17CDistribution (Log10-Normal moments)", + "analysis": "GeneralizedMethodOfMoments / LeverageDiagnostics", + "area": "ModelEstimation", + "declaredDependencies": [ + "LogNormal", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.GmmInfluenceDiagnosticsVerificationTests.VanishingCenteredPenalty_PreservesObservationInfluenceScale", + "source": "src/RMC.BestFit.Verification/ModelEstimation/GmmInfluenceDiagnosticsVerificationTests.cs", + "model": "Bulletin17CDistribution (Log10-Normal moments with quadratic penalty)", + "analysis": "GeneralizedMethodOfMoments / LeverageDiagnostics", + "area": "ModelEstimation", + "declaredDependencies": [ + "LogNormal", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.InformationCriterionOracleTests.DIC_MatchesRBayesianToolsOracle", + "source": "src/RMC.BestFit.Verification/ModelEstimation/InformationCriterionOracleTests.cs", + "model": "UnivariateDistribution (Normal) with fixed posterior draws", + "analysis": "BayesianAnalysis information criteria", + "area": "ModelEstimation", + "declaredDependencies": [ + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.InformationCriterionOracleTests.WAIC_MatchesRLooOracle", + "source": "src/RMC.BestFit.Verification/ModelEstimation/InformationCriterionOracleTests.cs", + "model": "UnivariateDistribution (Normal) with fixed posterior draws", + "analysis": "BayesianAnalysis information criteria", + "area": "ModelEstimation", + "declaredDependencies": [ + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.JointPriorSamplingVerificationTests.SampleFromPriors_SoftJointPrior_CurrentlyDrawsIndependentMarginals", + "source": "src/RMC.BestFit.Verification/ModelEstimation/JointPriorSamplingVerificationTests.cs", + "model": "Soft-coupled two-parameter prior model", + "analysis": "PriorPredictiveCheck", + "area": "ModelEstimation", + "declaredDependencies": [ + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.Log10NormalEstimationEquivalenceTests.FlatPriorMap_MatchesClosedFormLog10NormalMle", + "source": "src/RMC.BestFit.Verification/ModelEstimation/Log10NormalEstimationEquivalenceTests.cs", + "model": "Log10-Normal UnivariateDistribution / Bulletin17CDistribution", + "analysis": "MaximumLikelihood and flat-prior MaximumAPosteriori", + "area": "ModelEstimation", + "declaredDependencies": [ + "LogNormal", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.Log10NormalEstimationEquivalenceTests.GmmMuPenaltyRegimes_MatchAnalyticalFitAndVarianceInfluence", + "source": "src/RMC.BestFit.Verification/ModelEstimation/Log10NormalEstimationEquivalenceTests.cs", + "model": "Log10-Normal UnivariateDistribution / Bulletin17CDistribution", + "analysis": "GeneralizedMethodOfMoments", + "area": "ModelEstimation", + "declaredDependencies": [ + "LogNormal", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.Log10NormalEstimationEquivalenceTests.MapAndGmmMuPosterior_InverseVarianceReferenceInsideCentral95Intervals", + "source": "src/RMC.BestFit.Verification/ModelEstimation/Log10NormalEstimationEquivalenceTests.cs", + "model": "Log10-Normal UnivariateDistribution / Bulletin17CDistribution", + "analysis": "MaximumAPosteriori and GeneralizedMethodOfMoments", + "area": "ModelEstimation", + "declaredDependencies": [ + "LogNormal", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.Log10NormalEstimationEquivalenceTests.MapMuPriorRegimes_MatchAnalyticalFitAndVarianceInfluence", + "source": "src/RMC.BestFit.Verification/ModelEstimation/Log10NormalEstimationEquivalenceTests.cs", + "model": "Log10-Normal UnivariateDistribution / Bulletin17CDistribution", + "analysis": "MaximumAPosteriori", + "area": "ModelEstimation", + "declaredDependencies": [ + "LogNormal", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.Log10NormalEstimationEquivalenceTests.UnpenalizedB17CGmm_MatchesExactSampleMoments", + "source": "src/RMC.BestFit.Verification/ModelEstimation/Log10NormalEstimationEquivalenceTests.cs", + "model": "Log10-Normal UnivariateDistribution / Bulletin17CDistribution", + "analysis": "GeneralizedMethodOfMoments", + "area": "ModelEstimation", + "declaredDependencies": [ + "LogNormal", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.Log10NormalInfluenceVerificationTests.GmmObservationInfluence_CurrentMagnitudeDiffersFromExactDeletionScale", + "source": "src/RMC.BestFit.Verification/ModelEstimation/Log10NormalInfluenceVerificationTests.cs", + "model": "Bulletin17CDistribution (Log10-Normal moments)", + "analysis": "GeneralizedMethodOfMoments / LeverageDiagnostics", + "area": "ModelEstimation", + "declaredDependencies": [ + "LogNormal", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.MaximumAPosterioriInformationCriteriaOracleTests.AIC_UsesDataLikelihoodAtMap_ExcludesPriorDensity", + "source": "src/RMC.BestFit.Verification/ModelEstimation/MaximumAPosterioriInformationCriteriaOracleTests.cs", + "model": "Inline Normal likelihood with bounded Uniform priors", + "analysis": "MaximumAPosteriori information criteria", + "area": "ModelEstimation", + "declaredDependencies": [ + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.MaximumAPosterioriInformationCriteriaOracleTests.BIC_UsesDataLikelihoodAtMap_ExcludesPriorDensity", + "source": "src/RMC.BestFit.Verification/ModelEstimation/MaximumAPosterioriInformationCriteriaOracleTests.cs", + "model": "Inline Normal likelihood with bounded Uniform priors", + "analysis": "MaximumAPosteriori information criteria", + "area": "ModelEstimation", + "declaredDependencies": [ + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.MaximumAPosterioriRecoveryTests.Test_Estimate_NormalData_FindsMode", + "source": "src/RMC.BestFit.Verification/ModelEstimation/MaximumAPosterioriRecoveryTests.cs", + "model": "Inline Normal likelihood with bounded Uniform priors", + "analysis": "MaximumAPosteriori", + "area": "ModelEstimation", + "declaredDependencies": [ + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.McmcNumericalVerificationTests.Arwmh_BestFitWiringRecordsEveryRealizedStateInAdaptiveCovariance", + "source": "src/RMC.BestFit.Verification/ModelEstimation/McmcNumericalVerificationTests.cs", + "model": "Two-parameter spike target", + "analysis": "BayesianAnalysis / ARWMH", + "area": "ModelEstimation", + "declaredDependencies": [ + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.McmcNumericalVerificationTests.ConservativeEss_MatchesRPosteriorBulkAndTailOracle", + "source": "src/RMC.BestFit.Verification/ModelEstimation/McmcNumericalVerificationTests.cs", + "model": "Nine deterministic multi-chain diagnostic fixtures", + "analysis": "MCMCDiagnostics", + "area": "ModelEstimation", + "declaredDependencies": [ + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.McmcNumericalVerificationTests.Nuts_BestFitNumericalGradientMatchesPosteriorGradient", + "source": "src/RMC.BestFit.Verification/ModelEstimation/McmcNumericalVerificationTests.cs", + "model": "Two-parameter smooth Gaussian-plus-coupled-prior target", + "analysis": "BayesianAnalysis / NUTS", + "area": "ModelEstimation", + "declaredDependencies": [ + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.McmcNumericalVerificationTests.RankNormalizedRhat_MatchesRPosteriorOracle", + "source": "src/RMC.BestFit.Verification/ModelEstimation/McmcNumericalVerificationTests.cs", + "model": "Nine deterministic multi-chain diagnostic fixtures", + "analysis": "MCMCDiagnostics", + "area": "ModelEstimation", + "declaredDependencies": [ + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_Exponential_RecoversTrueParameters", + "source": "src/RMC.BestFit.Verification/ModelEstimation/MLEIntegrationTests.cs", + "model": "UnivariateDistribution (Exponential)", + "analysis": "MaximumLikelihood", + "area": "ModelEstimation", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "LnNormal", + "Logistic", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_Gamma_RecoversTrueParameters", + "source": "src/RMC.BestFit.Verification/ModelEstimation/MLEIntegrationTests.cs", + "model": "UnivariateDistribution (GammaDistribution)", + "analysis": "MaximumLikelihood", + "area": "ModelEstimation", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "LnNormal", + "Logistic", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_GeneralizedLogistic_RecoversTrueParameters", + "source": "src/RMC.BestFit.Verification/ModelEstimation/MLEIntegrationTests.cs", + "model": "UnivariateDistribution (GeneralizedLogistic)", + "analysis": "MaximumLikelihood", + "area": "ModelEstimation", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "LnNormal", + "Logistic", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_GeneralizedNormal_RecoversTrueParameters", + "source": "src/RMC.BestFit.Verification/ModelEstimation/MLEIntegrationTests.cs", + "model": "UnivariateDistribution (GeneralizedNormal)", + "analysis": "MaximumLikelihood", + "area": "ModelEstimation", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "LnNormal", + "Logistic", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_GeneralizedPareto_RecoversTrueParameters", + "source": "src/RMC.BestFit.Verification/ModelEstimation/MLEIntegrationTests.cs", + "model": "UnivariateDistribution (GeneralizedPareto)", + "analysis": "MaximumLikelihood", + "area": "ModelEstimation", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "LnNormal", + "Logistic", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_GEV_RecoversTrueParameters", + "source": "src/RMC.BestFit.Verification/ModelEstimation/MLEIntegrationTests.cs", + "model": "UnivariateDistribution (GeneralizedExtremeValue)", + "analysis": "MaximumLikelihood", + "area": "ModelEstimation", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "LnNormal", + "Logistic", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_Gumbel_RecoversTrueParameters", + "source": "src/RMC.BestFit.Verification/ModelEstimation/MLEIntegrationTests.cs", + "model": "UnivariateDistribution (Gumbel)", + "analysis": "MaximumLikelihood", + "area": "ModelEstimation", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "LnNormal", + "Logistic", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_LnNormal_MatchesClosedFormLogSampleMLE", + "source": "src/RMC.BestFit.Verification/ModelEstimation/MLEIntegrationTests.cs", + "model": "UnivariateDistribution (LnNormal)", + "analysis": "MaximumLikelihood", + "area": "ModelEstimation", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "LnNormal", + "Logistic", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_LnNormal_RecoversGeneratingParameters", + "source": "src/RMC.BestFit.Verification/ModelEstimation/MLEIntegrationTests.cs", + "model": "UnivariateDistribution (LnNormal)", + "analysis": "MaximumLikelihood", + "area": "ModelEstimation", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "LnNormal", + "Logistic", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_Logistic_RecoversTrueParameters", + "source": "src/RMC.BestFit.Verification/ModelEstimation/MLEIntegrationTests.cs", + "model": "UnivariateDistribution (Logistic)", + "analysis": "MaximumLikelihood", + "area": "ModelEstimation", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "LnNormal", + "Logistic", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_LogPearsonTypeIII_RecoversTrueParameters", + "source": "src/RMC.BestFit.Verification/ModelEstimation/MLEIntegrationTests.cs", + "model": "UnivariateDistribution (LogPearsonTypeIII)", + "analysis": "MaximumLikelihood", + "area": "ModelEstimation", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "LnNormal", + "Logistic", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_Normal_RecoversTrueParameters", + "source": "src/RMC.BestFit.Verification/ModelEstimation/MLEIntegrationTests.cs", + "model": "UnivariateDistribution (Normal)", + "analysis": "MaximumLikelihood", + "area": "ModelEstimation", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "LnNormal", + "Logistic", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_PearsonTypeIII_RecoversTrueParameters", + "source": "src/RMC.BestFit.Verification/ModelEstimation/MLEIntegrationTests.cs", + "model": "UnivariateDistribution (PearsonTypeIII)", + "analysis": "MaximumLikelihood", + "area": "ModelEstimation", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "LnNormal", + "Logistic", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_Weibull_RecoversTrueParameters", + "source": "src/RMC.BestFit.Verification/ModelEstimation/MLEIntegrationTests.cs", + "model": "UnivariateDistribution (Weibull)", + "analysis": "MaximumLikelihood", + "area": "ModelEstimation", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "LnNormal", + "Logistic", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.PosteriorResamplingVerificationTests.CoincidentFrequencyPosteriorResampling_MatchesIndependentClosedFormOracle", + "source": "src/RMC.BestFit.Verification/ModelEstimation/PosteriorResamplingVerificationTests.cs", + "model": "Independent Normal copula marginals with additive response surface", + "analysis": "CoincidentFrequencyAnalysis", + "area": "ModelEstimation", + "declaredDependencies": [ + "Normal", + "CompetingRisks" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.PosteriorResamplingVerificationTests.CompositePosteriorResampling_MatchesIndependentCartesianOracle", + "source": "src/RMC.BestFit.Verification/ModelEstimation/PosteriorResamplingVerificationTests.cs", + "model": "Composite competing-risk maximum of two Normal child analyses", + "analysis": "CompositeAnalysis", + "area": "ModelEstimation", + "declaredDependencies": [ + "Normal", + "CompetingRisks" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.ProfileQRecoveryTests.TrueProfile_LP3Q99_RecoversGeneratingResponse", + "source": "src/RMC.BestFit.Verification/ModelEstimation/ProfileQRecoveryTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII)", + "analysis": "GeneralizedMethodOfMoments Profile-Q", + "area": "ModelEstimation", + "declaredDependencies": [ + "LogPearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.PsisLooOracleVerificationTests.ParetoInfluence_UsesRloo210DiagnosticThreshold", + "source": "src/RMC.BestFit.Verification/ModelEstimation/PsisLooOracleVerificationTests.cs", + "model": "Fixed Normal pointwise log-likelihood matrix", + "analysis": "BayesianAnalysis / InfluenceDiagnostics", + "area": "ModelEstimation", + "declaredDependencies": [ + "Exponential", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.PsisLooOracleVerificationTests.PSISLOO_MatchesRLooOracle", + "source": "src/RMC.BestFit.Verification/ModelEstimation/PsisLooOracleVerificationTests.cs", + "model": "Fixed Normal pointwise log-likelihood matrix", + "analysis": "BayesianAnalysis PSIS-LOO", + "area": "ModelEstimation", + "declaredDependencies": [ + "Exponential", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.RatingCurve.RatingCurveBayesianRecoveryTests.Test_EstimateParameters_SingleSegment_Default", + "source": "src/RMC.BestFit.Verification/RatingCurve/RatingCurveBayesianRecoveryTests.cs", + "model": "one-control synthetic rating curve; SingleSegment_Default", + "analysis": "RatingCurveAnalysis", + "area": "RatingCurve", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.RatingCurve.RatingCurveBayesianRecoveryTests.Test_EstimateParameters_SingleSegment_LowNoise", + "source": "src/RMC.BestFit.Verification/RatingCurve/RatingCurveBayesianRecoveryTests.cs", + "model": "one-control synthetic rating curve; SingleSegment_LowNoise", + "analysis": "RatingCurveAnalysis", + "area": "RatingCurve", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.RatingCurve.RatingCurveBayesianRecoveryTests.Test_EstimateParameters_SingleSegment_WideRange", + "source": "src/RMC.BestFit.Verification/RatingCurve/RatingCurveBayesianRecoveryTests.cs", + "model": "one-control synthetic rating curve; SingleSegment_WideRange", + "analysis": "RatingCurveAnalysis", + "area": "RatingCurve", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.RatingCurve.RatingCurveBayesianRecoveryTests.Test_EstimateParameters_ThreeSegment_MultipleControl", + "source": "src/RMC.BestFit.Verification/RatingCurve/RatingCurveBayesianRecoveryTests.cs", + "model": "three-control synthetic rating curve; ThreeSegment_MultipleControl", + "analysis": "RatingCurveAnalysis", + "area": "RatingCurve", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.RatingCurve.RatingCurveBayesianRecoveryTests.Test_EstimateParameters_TwoSegment_BankfullTransition", + "source": "src/RMC.BestFit.Verification/RatingCurve/RatingCurveBayesianRecoveryTests.cs", + "model": "two-control synthetic rating curve; TwoSegment_BankfullTransition", + "analysis": "RatingCurveAnalysis", + "area": "RatingCurve", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.RatingCurve.RatingCurveExampleRecoveryTests.Mle_OneSegment_RecoversExampleCurve", + "source": "src/RMC.BestFit.Verification/RatingCurve/RatingCurveExampleRecoveryTests.cs", + "model": "one-control piecewise power-law rating curve", + "analysis": "RatingCurveAnalysis", + "area": "RatingCurve", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.RatingCurve.RatingCurveExampleRecoveryTests.Mle_ThreeSegment_RecoversExampleCurve", + "source": "src/RMC.BestFit.Verification/RatingCurve/RatingCurveExampleRecoveryTests.cs", + "model": "three-control piecewise power-law rating curve", + "analysis": "RatingCurveAnalysis", + "area": "RatingCurve", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.RatingCurve.RatingCurveExampleRecoveryTests.Mle_TwoSegment_RecoversExampleCurve", + "source": "src/RMC.BestFit.Verification/RatingCurve/RatingCurveExampleRecoveryTests.cs", + "model": "two-control piecewise power-law rating curve", + "analysis": "RatingCurveAnalysis", + "area": "RatingCurve", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.RatingCurve.RatingCurveLikelihoodOracleTests.OneSegment_DataLogLikelihood_IsDischargeSpaceDensity", + "source": "src/RMC.BestFit.Verification/RatingCurve/RatingCurveLikelihoodOracleTests.cs", + "model": "one-control piecewise power-law rating curve", + "analysis": "RatingCurveAnalysis likelihood", + "area": "RatingCurve", + "declaredDependencies": [ + "LogNormal", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.RatingCurve.RatingCurveLikelihoodOracleTests.ThreeSegment_DataLogLikelihood_IsDischargeSpaceDensity", + "source": "src/RMC.BestFit.Verification/RatingCurve/RatingCurveLikelihoodOracleTests.cs", + "model": "three-control piecewise power-law rating curve", + "analysis": "RatingCurveAnalysis likelihood", + "area": "RatingCurve", + "declaredDependencies": [ + "LogNormal", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.RatingCurve.RatingCurveLikelihoodOracleTests.TwoSegment_DataLogLikelihood_IsDischargeSpaceDensity", + "source": "src/RMC.BestFit.Verification/RatingCurve/RatingCurveLikelihoodOracleTests.cs", + "model": "two-control piecewise power-law rating curve", + "analysis": "RatingCurveAnalysis likelihood", + "area": "RatingCurve", + "declaredDependencies": [ + "LogNormal", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.RatingCurve.RatingCurveMLERecoveryTests.Test_EstimateParameters_SingleSegment_Default", + "source": "src/RMC.BestFit.Verification/RatingCurve/RatingCurveMLERecoveryTests.cs", + "model": "one-control synthetic rating curve; SingleSegment_Default", + "analysis": "RatingCurveAnalysis", + "area": "RatingCurve", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.RatingCurve.RatingCurveMLERecoveryTests.Test_EstimateParameters_SingleSegment_LowNoise", + "source": "src/RMC.BestFit.Verification/RatingCurve/RatingCurveMLERecoveryTests.cs", + "model": "one-control synthetic rating curve; SingleSegment_LowNoise", + "analysis": "RatingCurveAnalysis", + "area": "RatingCurve", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.RatingCurve.RatingCurveMLERecoveryTests.Test_EstimateParameters_SingleSegment_WideRange", + "source": "src/RMC.BestFit.Verification/RatingCurve/RatingCurveMLERecoveryTests.cs", + "model": "one-control synthetic rating curve; SingleSegment_WideRange", + "analysis": "RatingCurveAnalysis", + "area": "RatingCurve", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.RatingCurve.RatingCurveMLERecoveryTests.Test_EstimateParameters_ThreeSegment_MultipleControl", + "source": "src/RMC.BestFit.Verification/RatingCurve/RatingCurveMLERecoveryTests.cs", + "model": "three-control synthetic rating curve; ThreeSegment_MultipleControl", + "analysis": "RatingCurveAnalysis", + "area": "RatingCurve", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.RatingCurve.RatingCurveMLERecoveryTests.Test_EstimateParameters_TwoSegment_BankfullTransition", + "source": "src/RMC.BestFit.Verification/RatingCurve/RatingCurveMLERecoveryTests.cs", + "model": "two-control synthetic rating curve; TwoSegment_BankfullTransition", + "analysis": "RatingCurveAnalysis", + "area": "RatingCurve", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVBayesianRecoveryTests.Bayesian_BasicHomogeneous_RecoversParameters", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVBayesianRecoveryTests.cs", + "model": "Homogeneous SpatialGEV without copula", + "analysis": "SpatialGEVAnalysis Bayesian estimation", + "area": "SpatialExtremes", + "declaredDependencies": [ + "Exponential", + "Gumbel" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVBayesianRecoveryTests.Bayesian_WithCopula_RecoversParameters", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVBayesianRecoveryTests.cs", + "model": "Homogeneous SpatialGEV with exponential Gaussian copula", + "analysis": "SpatialGEVAnalysis Bayesian estimation", + "area": "SpatialExtremes", + "declaredDependencies": [ + "Exponential", + "Gumbel" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVBayesianRecoveryTests.Bayesian_WithLocationRegression_RecoversParameters", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVBayesianRecoveryTests.cs", + "model": "SpatialGEV with X/Y location regression, no copula", + "analysis": "SpatialGEVAnalysis Bayesian estimation", + "area": "SpatialExtremes", + "declaredDependencies": [ + "Exponential", + "Gumbel" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVBayesianRecoveryTests.Bayesian_WithNegativeShape_RecoversParameters", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVBayesianRecoveryTests.cs", + "model": "Homogeneous SpatialGEV without copula, shape -0.2", + "analysis": "SpatialGEVAnalysis Bayesian estimation", + "area": "SpatialExtremes", + "declaredDependencies": [ + "Exponential", + "Gumbel" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVBayesianRecoveryTests.Bayesian_WithPositiveShape_RecoversParameters", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVBayesianRecoveryTests.cs", + "model": "Homogeneous SpatialGEV without copula, shape 0.1", + "analysis": "SpatialGEVAnalysis Bayesian estimation", + "area": "SpatialExtremes", + "declaredDependencies": [ + "Exponential", + "Gumbel" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVBayesianRecoveryTests.Bayesian_WithZeroShape_RecoversParameters", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVBayesianRecoveryTests.cs", + "model": "Homogeneous SpatialGEV without copula, shape 0", + "analysis": "SpatialGEVAnalysis Bayesian estimation", + "area": "SpatialExtremes", + "declaredDependencies": [ + "Exponential", + "Gumbel" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVChunk14OracleTests.GodambeSensitivityVariabilityAndSandwich_MatchIndependentOracle", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVChunk14OracleTests.cs", + "model": "Three-site intercept-only SpatialGEV on 24 complete row/year blocks", + "analysis": "SpatialGEV Godambe covariance", + "area": "SpatialExtremes", + "declaredDependencies": [ + "Exponential", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVChunk14OracleTests.HeldOutCopulaFold_MatchesIndependentFittedOracle", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVChunk14OracleTests.cs", + "model": "Three-site Cartesian exponential Gaussian-copula training network with one held-out target", + "analysis": "SpatialGEV held-out Gaussian-copula fold", + "area": "SpatialExtremes", + "declaredDependencies": [ + "Exponential", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVChunk14OracleTests.RegionalFixedDrawAggregation_MatchesIndependentPosteriorOracle", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVChunk14OracleTests.cs", + "model": "Three-site location-regression SpatialGEV with nine fixed independently supplied draws", + "analysis": "SpatialGEV regional posterior aggregation", + "area": "SpatialExtremes", + "declaredDependencies": [ + "Exponential", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVChunk14OracleTests.TemporalBlockBootstrap_MatchesIndependentWholeRowOracle", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVChunk14OracleTests.cs", + "model": "Twelve row/year vectors across three sites, wrapping blocks of four, five fixed replicates", + "analysis": "SpatialGEV temporal block bootstrap", + "area": "SpatialExtremes", + "declaredDependencies": [ + "Exponential", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVChunk14OracleTests.VarianceInflation_UsesExactIndependentAnalyticalTransformation", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVChunk14OracleTests.cs", + "model": "Ten complete row/year vectors across three sites with fixed posterior draws", + "analysis": "SpatialGEV posterior variance inflation", + "area": "SpatialExtremes", + "declaredDependencies": [ + "Exponential", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVInformationCriteriaTests.MissingSiteModel_InformationCriteria_UseRowYearBlocks", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVInformationCriteriaTests.cs", + "model": "Five-site SpatialGEV with exponential Gaussian copula and structured missing sites", + "analysis": "SpatialGEVAnalysis information criteria", + "area": "SpatialExtremes", + "declaredDependencies": [ + "Exponential" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVLikelihoodOracleTests.CompleteRows_CopulaLikelihood_MatchesIndependentOracle", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVLikelihoodOracleTests.cs", + "model": "Five-site homogeneous SpatialGEV, exponential Gaussian copula, complete rows", + "analysis": "SpatialGEV likelihood", + "area": "SpatialExtremes", + "declaredDependencies": [ + "Exponential" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVLikelihoodOracleTests.LocationErrorModel_DataLogLikelihood_ExcludesProcessDensity", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVLikelihoodOracleTests.cs", + "model": "Five-site SpatialGEV with exponential latent location-error Gaussian process", + "analysis": "SpatialGEV likelihood", + "area": "SpatialExtremes", + "declaredDependencies": [ + "Exponential" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVLikelihoodOracleTests.LocationErrorModel_PosteriorKernel_IsInvariantToTheDecomposition", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVLikelihoodOracleTests.cs", + "model": "Five-site SpatialGEV with exponential latent location-error Gaussian process", + "analysis": "SpatialGEV likelihood", + "area": "SpatialExtremes", + "declaredDependencies": [ + "Exponential" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVLikelihoodOracleTests.LocationErrorModel_ScalarAndPointwiseDecompositionsAgree", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVLikelihoodOracleTests.cs", + "model": "Five-site SpatialGEV with exponential latent location-error Gaussian process", + "analysis": "SpatialGEV likelihood", + "area": "SpatialExtremes", + "declaredDependencies": [ + "Exponential" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVLikelihoodOracleTests.LocationErrorModel_ScalarAndPointwiseGradientsAgree", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVLikelihoodOracleTests.cs", + "model": "Five-site SpatialGEV with exponential latent location-error Gaussian process", + "analysis": "SpatialGEV likelihood", + "area": "SpatialExtremes", + "declaredDependencies": [ + "Exponential" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVLikelihoodOracleTests.MarginalOnly_WithoutCopula_MatchesIndependentOracle", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVLikelihoodOracleTests.cs", + "model": "Five-site homogeneous SpatialGEV margins without copula, structured missing sites", + "analysis": "SpatialGEV likelihood", + "area": "SpatialExtremes", + "declaredDependencies": [ + "Exponential" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVLikelihoodOracleTests.MissingSites_DataLogLikelihood_UsesObservedSiteCopulaSubmatrix", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVLikelihoodOracleTests.cs", + "model": "Five-site homogeneous SpatialGEV, exponential Gaussian copula, structured missing sites", + "analysis": "SpatialGEV likelihood", + "area": "SpatialExtremes", + "declaredDependencies": [ + "Exponential" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVLikelihoodOracleTests.MissingSites_PointwiseRows_MatchObservedSubsetOracle", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVLikelihoodOracleTests.cs", + "model": "Five-site homogeneous SpatialGEV, exponential Gaussian copula, structured missing sites", + "analysis": "SpatialGEV likelihood", + "area": "SpatialExtremes", + "declaredDependencies": [ + "Exponential" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVMLERecoveryTests.MLE_BasicHomogeneous_RecoversParameters", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVMLERecoveryTests.cs", + "model": "Homogeneous SpatialGEV without copula", + "analysis": "MaximumLikelihood", + "area": "SpatialExtremes", + "declaredDependencies": [ + "Exponential" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVMLERecoveryTests.MLE_WithCopula_RecoversParameters", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVMLERecoveryTests.cs", + "model": "Homogeneous SpatialGEV with exponential Gaussian copula", + "analysis": "MaximumLikelihood", + "area": "SpatialExtremes", + "declaredDependencies": [ + "Exponential" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVSimulationVerificationTests.GenerateRandomValues_WithCopula_ReproducesTheFittedIntersiteDependence", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVSimulationVerificationTests.cs", + "model": "Five-site homogeneous SpatialGEV with exponential Gaussian copula", + "analysis": "SpatialGEV simulation", + "area": "SpatialExtremes", + "declaredDependencies": [ + "Exponential", + "GeneralizedExtremeValue", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.TimeSeriesAnalysis.ARAnalysisTests.Test_EstimateParameters_AR1", + "source": "src/RMC.BestFit.Verification/TimeSeriesAnalysis/ARAnalysisTests.cs", + "model": "AR(1)", + "analysis": "Bayesian time-series analysis", + "area": "TimeSeries", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.TimeSeriesAnalysis.AutoRegressiveMLERecoveryTests.Test_EstimateParameters_AR1", + "source": "src/RMC.BestFit.Verification/TimeSeriesAnalysis/AutoRegressiveMLERecoveryTests.cs", + "model": "AR(1)", + "analysis": "MaximumLikelihood", + "area": "TimeSeries", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.TimeSeriesAnalysis.MAAnalysisTests.Test_EstimateParameters_MA1", + "source": "src/RMC.BestFit.Verification/TimeSeriesAnalysis/MAAnalysisTests.cs", + "model": "MA(1)", + "analysis": "Bayesian time-series analysis", + "area": "TimeSeries", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.TimeSeriesAnalysis.MovingAverageMLERecoveryTests.Test_EstimateParameters_MA1", + "source": "src/RMC.BestFit.Verification/TimeSeriesAnalysis/MovingAverageMLERecoveryTests.cs", + "model": "MA(1)", + "analysis": "MaximumLikelihood", + "area": "TimeSeries", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesChunk13OracleTests.ArimaxTrendSeasonalityAndCovariatesMatchIndependentPythonOracle", + "source": "src/RMC.BestFit.Verification/TimeSeriesAnalysis/TimeSeriesChunk13OracleTests.cs", + "model": "ARIMAX(1,0,1), intercept + linear trend + monthly Fourier seasonality + two level covariates", + "analysis": "ARIMAX conditional recurrence", + "area": "TimeSeries", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesChunk13OracleTests.FirstOrderConditionalObjectivesMatchIndependentPythonOracle", + "source": "src/RMC.BestFit.Verification/TimeSeriesAnalysis/TimeSeriesChunk13OracleTests.cs", + "model": "AutoRegressive(1) and MovingAverage(1)", + "analysis": "Time-series conditional recurrence", + "area": "TimeSeries", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesChunk13OracleTests.HigherOrderArAndMaResponsesMatchIndependentPythonOracle", + "source": "src/RMC.BestFit.Verification/TimeSeriesAnalysis/TimeSeriesChunk13OracleTests.cs", + "model": "AutoRegressive(2) and MovingAverage(2)", + "analysis": "Time-series conditional recurrence", + "area": "TimeSeries", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesChunk13OracleTests.PureArAndMaThroughArimaMatchIndependentPythonOracle", + "source": "src/RMC.BestFit.Verification/TimeSeriesAnalysis/TimeSeriesChunk13OracleTests.cs", + "model": "ARIMA(2,0,0) and ARIMA(0,0,2)", + "analysis": "ARIMA conditional recurrence", + "area": "TimeSeries", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.ArAndMaTransformedGeneratorsMatchIndependentOracle", + "source": "src/RMC.BestFit.Verification/TimeSeriesAnalysis/TimeSeriesIndependentOracleTests.cs", + "model": "Transformed AR(1) and MA(1) generators", + "analysis": "Time-series model calculation/oracle", + "area": "TimeSeries", + "declaredDependencies": [ + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.ArimaAndArimaxPredictionUncertaintyBeginsAtForecastBoundary", + "source": "src/RMC.BestFit.Verification/TimeSeriesAnalysis/TimeSeriesIndependentOracleTests.cs", + "model": "ARIMA and ARIMAX forecast variance recurrence", + "analysis": "Time-series model calculation/oracle", + "area": "TimeSeries", + "declaredDependencies": [ + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.ArimaDifferencedTransformedGeneratorMatchesIndependentOracle", + "source": "src/RMC.BestFit.Verification/TimeSeriesAnalysis/TimeSeriesIndependentOracleTests.cs", + "model": "ARIMA(1,1,1) transformed/differenced generator", + "analysis": "Time-series model calculation/oracle", + "area": "TimeSeries", + "declaredDependencies": [ + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.ArimaxDifferencedLikelihoodMatchesDateIndexedIndependentOracle", + "source": "src/RMC.BestFit.Verification/TimeSeriesAnalysis/TimeSeriesIndependentOracleTests.cs", + "model": "ARIMAX(1,1,0), date-indexed contemporaneous covariate", + "analysis": "Time-series model calculation/oracle", + "area": "TimeSeries", + "declaredDependencies": [ + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.ArimaxTransformedDifferencedGeneratorMatchesIndependentOracle", + "source": "src/RMC.BestFit.Verification/TimeSeriesAnalysis/TimeSeriesIndependentOracleTests.cs", + "model": "ARIMAX transformed/differenced date and covariate generator", + "analysis": "Time-series model calculation/oracle", + "area": "TimeSeries", + "declaredDependencies": [ + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.InformationCriteriaUseDataLikelihoodAtMapAndExcludePrior", + "source": "src/RMC.BestFit.Verification/TimeSeriesAnalysis/TimeSeriesIndependentOracleTests.cs", + "model": "AR, MA, ARIMA, and ARIMAX information criteria", + "analysis": "Time-series model calculation/oracle", + "area": "TimeSeries", + "declaredDependencies": [ + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.InvalidScaleBehaviorMatchesScalarAndPointwiseOracle", + "source": "src/RMC.BestFit.Verification/TimeSeriesAnalysis/TimeSeriesIndependentOracleTests.cs", + "model": "AR, MA, ARIMA, and ARIMAX invalid innovation scales", + "analysis": "Time-series model calculation/oracle", + "area": "TimeSeries", + "declaredDependencies": [ + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.ManualTransformLambdaRebuildMatchesIndependentLikelihoodOracle", + "source": "src/RMC.BestFit.Verification/TimeSeriesAnalysis/TimeSeriesIndependentOracleTests.cs", + "model": "AR, MA, ARIMA, and ARIMAX manual Box-Cox/Yeo-Johnson transforms", + "analysis": "Time-series model calculation/oracle", + "area": "TimeSeries", + "declaredDependencies": [ + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.TransformedArimaAndArimaxForecastsMatchModelScaleOracle", + "source": "src/RMC.BestFit.Verification/TimeSeriesAnalysis/TimeSeriesIndependentOracleTests.cs", + "model": "Transformed ARIMA and ARIMAX stochastic forecasts", + "analysis": "Time-series model calculation/oracle", + "area": "TimeSeries", + "declaredDependencies": [ + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.TransformLambdaMatchesIndependentTrainingOnlyOracle", + "source": "src/RMC.BestFit.Verification/TimeSeriesAnalysis/TimeSeriesIndependentOracleTests.cs", + "model": "AR, MA, ARIMA, and ARIMAX automatic Box-Cox/Yeo-Johnson fitting", + "analysis": "Time-series model calculation/oracle", + "area": "TimeSeries", + "declaredDependencies": [ + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentRecoveryTests.BayesianArima111LogD1RecoversGeneratingParameters", + "source": "src/RMC.BestFit.Verification/TimeSeriesAnalysis/TimeSeriesIndependentRecoveryTests.cs", + "model": "ARIMA(1,1,1), logarithmic response transform, no intercept", + "analysis": "Bayesian time-series analysis", + "area": "TimeSeries", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentRecoveryTests.BayesianArimax10D1LevelCovariateRecoversGeneratingParameters", + "source": "src/RMC.BestFit.Verification/TimeSeriesAnalysis/TimeSeriesIndependentRecoveryTests.cs", + "model": "ARIMAX(1,1,0), intercept and one level covariate, no response transform", + "analysis": "Bayesian time-series analysis", + "area": "TimeSeries", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentRecoveryTests.MleArima111LogD1RecoversGeneratingParameters", + "source": "src/RMC.BestFit.Verification/TimeSeriesAnalysis/TimeSeriesIndependentRecoveryTests.cs", + "model": "ARIMA(1,1,1), logarithmic response transform, no intercept", + "analysis": "MaximumLikelihood", + "area": "TimeSeries", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentRecoveryTests.MleArimax10D1LevelCovariateRecoversGeneratingParameters", + "source": "src/RMC.BestFit.Verification/TimeSeriesAnalysis/TimeSeriesIndependentRecoveryTests.cs", + "model": "ARIMAX(1,1,0), intercept and one level covariate, no response transform", + "analysis": "MaximumLikelihood", + "area": "TimeSeries", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.Exponential_Covariance_N100", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCovarianceTests.cs", + "model": "Exponential GMM covariance", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.Exponential_Covariance_N25", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCovarianceTests.cs", + "model": "Exponential GMM covariance", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.Gamma_Covariance_N100", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCovarianceTests.cs", + "model": "Gamma GMM covariance", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.Gamma_Covariance_N25", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCovarianceTests.cs", + "model": "Gamma GMM covariance", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.LogNormal_Covariance_N100", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCovarianceTests.cs", + "model": "LogNormal GMM covariance", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.LogNormal_Covariance_N25", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCovarianceTests.cs", + "model": "LogNormal GMM covariance", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.LogPearsonTypeIII_Covariance_Example1", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCovarianceTests.cs", + "model": "LogPearsonTypeIII GMM covariance", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.LogPearsonTypeIII_Covariance_N100", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCovarianceTests.cs", + "model": "LogPearsonTypeIII GMM covariance", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.LogPearsonTypeIII_Covariance_N25", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCovarianceTests.cs", + "model": "LogPearsonTypeIII GMM covariance", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.Normal_Covariance_N100", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCovarianceTests.cs", + "model": "Normal GMM covariance", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.Normal_Covariance_N25", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCovarianceTests.cs", + "model": "Normal GMM covariance", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.PearsonTypeIII_Covariance_N100", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCovarianceTests.cs", + "model": "PearsonTypeIII GMM covariance", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.PearsonTypeIII_Covariance_N25", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCovarianceTests.cs", + "model": "PearsonTypeIII GMM covariance", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CExampleTests.Test_Example1", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CExampleTests.cs", + "model": "LogPearsonTypeIII; Bulletin 17C worked Example 1", + "analysis": "Bulletin17CAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "LogPearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CExampleTests.Test_Example2", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CExampleTests.cs", + "model": "LogPearsonTypeIII; Bulletin 17C worked Example 2", + "analysis": "Bulletin17CAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "LogPearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CExampleTests.Test_Example3", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CExampleTests.cs", + "model": "LogPearsonTypeIII; Bulletin 17C worked Example 3", + "analysis": "Bulletin17CAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "LogPearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CExampleTests.Test_Example4", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CExampleTests.cs", + "model": "LogPearsonTypeIII; Bulletin 17C worked Example 4", + "analysis": "Bulletin17CAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "LogPearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CExampleTests.Test_Example5", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CExampleTests.cs", + "model": "LogPearsonTypeIII; Bulletin 17C worked Example 5", + "analysis": "Bulletin17CAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "LogPearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CExampleTests.Test_Example6", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CExampleTests.cs", + "model": "LogPearsonTypeIII; Bulletin 17C worked Example 6", + "analysis": "Bulletin17CAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "LogPearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CExampleTests.Test_Example7", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CExampleTests.cs", + "model": "LogPearsonTypeIII; Bulletin 17C worked Example 7", + "analysis": "Bulletin17CAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "LogPearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogNormal_PenalityOnMu_N100", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CPenalityTests.cs", + "model": "LogNormal regional parameter/quantile penalties", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "LogNormal", + "LogPearsonTypeIII", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogNormal_PenalityOnMu_N25", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CPenalityTests.cs", + "model": "LogNormal regional parameter/quantile penalties", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "LogNormal", + "LogPearsonTypeIII", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogNormal_PenalityOnMuAndSigma_N100", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CPenalityTests.cs", + "model": "LogNormal regional parameter/quantile penalties", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "LogNormal", + "LogPearsonTypeIII", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogNormal_PenalityOnMuAndSigma_N25", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CPenalityTests.cs", + "model": "LogNormal regional parameter/quantile penalties", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "LogNormal", + "LogPearsonTypeIII", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogNormal_PenalityOnMuSigmaAndQ99_N25", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CPenalityTests.cs", + "model": "LogNormal regional parameter/quantile penalties", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "LogNormal", + "LogPearsonTypeIII", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogNormal_PenalityOnQ99_N100", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CPenalityTests.cs", + "model": "LogNormal regional parameter/quantile penalties", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "LogNormal", + "LogPearsonTypeIII", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogNormal_PenalityOnQ99_N25", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CPenalityTests.cs", + "model": "LogNormal regional parameter/quantile penalties", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "LogNormal", + "LogPearsonTypeIII", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogNormal_PenalityOnSigma_N100", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CPenalityTests.cs", + "model": "LogNormal regional parameter/quantile penalties", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "LogNormal", + "LogPearsonTypeIII", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogNormal_PenalityOnSigma_N25", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CPenalityTests.cs", + "model": "LogNormal regional parameter/quantile penalties", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "LogNormal", + "LogPearsonTypeIII", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogPearsonTypeIII_PenalityOnAllParams_N25", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CPenalityTests.cs", + "model": "LogPearsonTypeIII regional parameter/quantile penalties", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "LogNormal", + "LogPearsonTypeIII", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogPearsonTypeIII_PenalityOnAllParamsAndQ99_N25", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CPenalityTests.cs", + "model": "LogPearsonTypeIII regional parameter/quantile penalties", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "LogNormal", + "LogPearsonTypeIII", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogPearsonTypeIII_PenalityOnMuAndGamma_N25", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CPenalityTests.cs", + "model": "LogPearsonTypeIII regional parameter/quantile penalties", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "LogNormal", + "LogPearsonTypeIII", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CSyntheticDataTests.Exponential_GmmRecoversGeneratingParent", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CSyntheticDataTests.cs", + "model": "Bulletin17CDistribution (Exponential)", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CSyntheticDataTests.Gamma_GmmRecoversGeneratingParent", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CSyntheticDataTests.cs", + "model": "Bulletin17CDistribution (Gamma)", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CSyntheticDataTests.LogNormal_GmmRecoversGeneratingLog10Parent", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CSyntheticDataTests.cs", + "model": "Bulletin17CDistribution (LogNormal)", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CSyntheticDataTests.LogPearsonTypeIII_GmmRecoversGeneratingLog10ParentAndQ99", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CSyntheticDataTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII)", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CSyntheticDataTests.Normal_GmmRecoversGeneratingParent", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CSyntheticDataTests.cs", + "model": "Bulletin17CDistribution (Normal)", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CSyntheticDataTests.PearsonTypeIII_GmmRecoversGeneratingParentAndQ99", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CSyntheticDataTests.cs", + "model": "Bulletin17CDistribution (PearsonTypeIII)", + "analysis": "Bulletin17CDistribution / GeneralizedMethodOfMoments", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskDependencyVerificationTests.Test_CorrelationMatrixSimulation_MatchesRankDependenceAndCompositeCdf", + "source": "src/RMC.BestFit.Verification/Univariate/CompetingRiskTests/CompetingRiskDependencyVerificationTests.cs", + "model": "two-Normal competing-risk maximum under the named Gaussian-copula dependence mode", + "analysis": "CompetingRiskAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Normal", + "CompetingRisks" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskDependencyVerificationTests.Test_IndependentSimulation_MatchesRankDependenceAndCompositeCdf", + "source": "src/RMC.BestFit.Verification/Univariate/CompetingRiskTests/CompetingRiskDependencyVerificationTests.cs", + "model": "two-Normal competing-risk maximum under the named Gaussian-copula dependence mode", + "analysis": "CompetingRiskAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Normal", + "CompetingRisks" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskDependencyVerificationTests.Test_PerfectlyNegativeSimulation_MatchesRankDependenceAndCompositeCdf", + "source": "src/RMC.BestFit.Verification/Univariate/CompetingRiskTests/CompetingRiskDependencyVerificationTests.cs", + "model": "two-Normal competing-risk maximum under the named Gaussian-copula dependence mode", + "analysis": "CompetingRiskAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Normal", + "CompetingRisks" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskDependencyVerificationTests.Test_PerfectlyPositiveSimulation_MatchesRankDependenceAndCompositeCdf", + "source": "src/RMC.BestFit.Verification/Univariate/CompetingRiskTests/CompetingRiskDependencyVerificationTests.cs", + "model": "two-Normal competing-risk maximum under the named Gaussian-copula dependence mode", + "analysis": "CompetingRiskAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Normal", + "CompetingRisks" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskRecoveryTests.Bayesian_Maximum_WeibullGumbelDogLeg_RecoversParent", + "source": "src/RMC.BestFit.Verification/Univariate/CompetingRiskTests/CompetingRiskRecoveryTests.cs", + "model": "Maximum WeibullGumbelDogLeg", + "analysis": "CompetingRiskAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Gumbel", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskRecoveryTests.Bayesian_Minimum_TwoWeibullDogLeg_RecoversParent", + "source": "src/RMC.BestFit.Verification/Univariate/CompetingRiskTests/CompetingRiskRecoveryTests.cs", + "model": "Minimum TwoWeibullDogLeg", + "analysis": "CompetingRiskAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Gumbel", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskRecoveryTests.MLE_Maximum_WeibullGumbelDogLeg_RecoversParent", + "source": "src/RMC.BestFit.Verification/Univariate/CompetingRiskTests/CompetingRiskRecoveryTests.cs", + "model": "Maximum WeibullGumbelDogLeg", + "analysis": "CompetingRiskAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Gumbel", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskRecoveryTests.MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent", + "source": "src/RMC.BestFit.Verification/Univariate/CompetingRiskTests/CompetingRiskRecoveryTests.cs", + "model": "Minimum CorrelatedTwoWeibullDogLeg", + "analysis": "CompetingRiskAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Gumbel", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskRecoveryTests.MLE_Minimum_TwoWeibullDogLeg_RecoversParent", + "source": "src/RMC.BestFit.Verification/Univariate/CompetingRiskTests/CompetingRiskRecoveryTests.cs", + "model": "Minimum TwoWeibullDogLeg", + "analysis": "CompetingRiskAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Gumbel", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.CombinationRules_SatisfyTheoreticalBracketingAndRemainDistinct", + "source": "src/RMC.BestFit.Verification/Univariate/CompositeTests/CompositeOracleVerificationTests.cs", + "model": "CombinationRules SatisfyTheoreticalBracketingAndRemainDistinct", + "analysis": "CompositeAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Normal", + "Mixture", + "CompetingRisks" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.CorrelationMatrix_MinimumAndMaximumMatchBivariateNormalOrthants", + "source": "src/RMC.BestFit.Verification/Univariate/CompositeTests/CompositeOracleVerificationTests.cs", + "model": "CorrelationMatrix MinimumAndMaximumMatchBivariateNormalOrthants", + "analysis": "CompositeAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Normal", + "Mixture", + "CompetingRisks" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.MaximumComposite_MatchesIndependentAndComonotonicClosedForms", + "source": "src/RMC.BestFit.Verification/Univariate/CompositeTests/CompositeOracleVerificationTests.cs", + "model": "MaximumComposite IndependentAndComonotonicClosedForms", + "analysis": "CompositeAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Normal", + "Mixture", + "CompetingRisks" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.MaximumPosterior_MatchesCompleteCartesianOracle", + "source": "src/RMC.BestFit.Verification/Univariate/CompositeTests/CompositeOracleVerificationTests.cs", + "model": "MaximumPosterior CompleteCartesianOracle", + "analysis": "CompositeAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Normal", + "Mixture", + "CompetingRisks" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.MinimumComposite_MatchesIndependentAndComonotonicClosedForms", + "source": "src/RMC.BestFit.Verification/Univariate/CompositeTests/CompositeOracleVerificationTests.cs", + "model": "MinimumComposite IndependentAndComonotonicClosedForms", + "analysis": "CompositeAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Normal", + "Mixture", + "CompetingRisks" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.MinimumPosterior_MatchesCompleteCartesianOracle", + "source": "src/RMC.BestFit.Verification/Univariate/CompositeTests/CompositeOracleVerificationTests.cs", + "model": "MinimumPosterior CompleteCartesianOracle", + "analysis": "CompositeAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Normal", + "Mixture", + "CompetingRisks" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.MixtureCdf_MatchesExactWeightedNormalSum", + "source": "src/RMC.BestFit.Verification/Univariate/CompositeTests/CompositeOracleVerificationTests.cs", + "model": "MixtureCdf ExactWeightedNormalSum", + "analysis": "CompositeAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Normal", + "Mixture", + "CompetingRisks" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.MixturePosterior_MatchesCompleteCartesianOracle", + "source": "src/RMC.BestFit.Verification/Univariate/CompositeTests/CompositeOracleVerificationTests.cs", + "model": "MixturePosterior CompleteCartesianOracle", + "analysis": "CompositeAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Normal", + "Mixture", + "CompetingRisks" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.MixtureQuantiles_InvertAnalyticWeightedNormalCdf", + "source": "src/RMC.BestFit.Verification/Univariate/CompositeTests/CompositeOracleVerificationTests.cs", + "model": "MixtureQuantiles InvertAnalyticWeightedNormalCdf", + "analysis": "CompositeAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Normal", + "Mixture", + "CompetingRisks" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.MixtureQuantiles_MatchPublishedRMistrTable45", + "source": "src/RMC.BestFit.Verification/Univariate/CompositeTests/CompositeOracleVerificationTests.cs", + "model": "MixtureQuantiles MatchPublishedRMistrTable45", + "analysis": "CompositeAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Normal", + "Mixture", + "CompetingRisks" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.CompositeTests.CompositePredictiveRecoveryTests.EqualWeightModelAverage_EndToEndPredictiveRecovery", + "source": "src/RMC.BestFit.Verification/Univariate/CompositeTests/CompositePredictiveRecoveryTests.cs", + "model": "equal-weight model average of two Normal child analyses", + "analysis": "CompositeAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Normal", + "Mixture", + "CompetingRisks" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.CompositeTests.CompositePredictiveRecoveryTests.MaximumComposite_EndToEndPredictiveRecovery", + "source": "src/RMC.BestFit.Verification/Univariate/CompositeTests/CompositePredictiveRecoveryTests.cs", + "model": "independent maximum of two Normal child analyses", + "analysis": "CompositeAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Normal", + "Mixture", + "CompetingRisks" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.CompositeTests.CompositePredictiveRecoveryTests.MinimumComposite_EndToEndPredictiveRecovery", + "source": "src/RMC.BestFit.Verification/Univariate/CompositeTests/CompositePredictiveRecoveryTests.cs", + "model": "independent minimum of two Normal child analyses", + "analysis": "CompositeAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Normal", + "Mixture", + "CompetingRisks" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.CompositeTests.CompositePredictiveRecoveryTests.MixtureComposite_EndToEndPredictiveRecovery", + "source": "src/RMC.BestFit.Verification/Univariate/CompositeTests/CompositePredictiveRecoveryTests.cs", + "model": "unequal-weight mixture of two Normal child analyses", + "analysis": "CompositeAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Normal", + "Mixture", + "CompetingRisks" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.MixtureTests.MixtureExternalPackageOracleTests.OrdinaryNormalMixture2D_MatchesScikitLearnArtifact", + "source": "src/RMC.BestFit.Verification/Univariate/MixtureTests/MixtureExternalPackageOracleTests.cs", + "model": "ordinary two-component Normal mixture", + "analysis": "MixtureAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Normal", + "Mixture" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.NormalMixture2D_BayesianRecovery", + "source": "src/RMC.BestFit.Verification/Univariate/MixtureTests/MixtureRecoveryTests.cs", + "model": "two-component Normal mixture", + "analysis": "MixtureAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Normal", + "Mixture" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.NormalMixture2D_Recovery_Parity", + "source": "src/RMC.BestFit.Verification/Univariate/MixtureTests/MixtureRecoveryTests.cs", + "model": "two-component Normal mixture", + "analysis": "MixtureAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Normal", + "Mixture" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.NormalMixture3D_BayesianRecovery", + "source": "src/RMC.BestFit.Verification/Univariate/MixtureTests/MixtureRecoveryTests.cs", + "model": "three-component Normal mixture", + "analysis": "MixtureAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Normal", + "Mixture" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.NormalMixture3D_Recovery_Parity", + "source": "src/RMC.BestFit.Verification/Univariate/MixtureTests/MixtureRecoveryTests.cs", + "model": "three-component Normal mixture", + "analysis": "MixtureAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Normal", + "Mixture" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.ZeroInflatedNormalMixture2D_BayesianRecovery", + "source": "src/RMC.BestFit.Verification/Univariate/MixtureTests/MixtureRecoveryTests.cs", + "model": "zero-inflated two-component Normal mixture", + "analysis": "MixtureAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Normal", + "Mixture" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.ZeroInflatedNormalMixture2D_Recovery_Parity", + "source": "src/RMC.BestFit.Verification/Univariate/MixtureTests/MixtureRecoveryTests.cs", + "model": "zero-inflated two-component Normal mixture", + "analysis": "MixtureAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Normal", + "Mixture" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessExternalPackageOracleTests.StationaryPoissonGpa_MatchesSciPyArtifact", + "source": "src/RMC.BestFit.Verification/Univariate/PointProcessTests/PointProcessExternalPackageOracleTests.cs", + "model": "stationary Poisson-GPA threshold process", + "analysis": "PointProcessModel", + "area": "Univariate", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessLikelihoodOracleTests.NonseasonalMixedObservations_MatchIndependentLikelihood", + "source": "src/RMC.BestFit.Verification/Univariate/PointProcessTests/PointProcessLikelihoodOracleTests.cs", + "model": "nonseasonal mixed-observation POT likelihood", + "analysis": "PointProcessModel", + "area": "Univariate", + "declaredDependencies": [ + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessLikelihoodOracleTests.SeasonalMixedObservations_MatchIndependentAnnualMaximumLikelihood", + "source": "src/RMC.BestFit.Verification/Univariate/PointProcessTests/PointProcessLikelihoodOracleTests.cs", + "model": "seasonal mixed-observation annual-maximum likelihood", + "analysis": "PointProcessModel", + "area": "Univariate", + "declaredDependencies": [ + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessPriorTests.Test_CalendarYearPertHistogram_MatchesIndependentPriorPlacementOracle", + "source": "src/RMC.BestFit.Verification/Univariate/PointProcessTests/PointProcessPriorTests.cs", + "model": "seasonal PointProcessModel; calendar year automatic changepoint priors", + "analysis": "PointProcessAnalysis", + "area": "Univariate", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessPriorTests.Test_WaterYearPertHistogram_MatchesIndependentPriorPlacementOracle", + "source": "src/RMC.BestFit.Verification/Univariate/PointProcessTests/PointProcessPriorTests.cs", + "model": "seasonal PointProcessModel; October water year automatic changepoint priors", + "analysis": "PointProcessAnalysis", + "area": "Univariate", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessRecoveryTests.Test_CalendarYearUniformSeasonality_AutomaticPriorsRecoverParentAndBothChangePoints", + "source": "src/RMC.BestFit.Verification/Univariate/PointProcessTests/PointProcessRecoveryTests.Uniform.cs", + "model": "seasonal PointProcessModel with two changepoints", + "analysis": "PointProcessAnalysis", + "area": "Univariate", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessRecoveryTests.Test_NonSeasonalProductionGenerator_RecoversParent", + "source": "src/RMC.BestFit.Verification/Univariate/PointProcessTests/PointProcessRecoveryTests.cs", + "model": "nonseasonal PointProcessModel", + "analysis": "PointProcessAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "GeneralizedPareto", + "Mixture" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessRecoveryTests.Test_NonSeasonalSimulation_MatchesPoissonRateAndConditionalTail", + "source": "src/RMC.BestFit.Verification/Univariate/PointProcessTests/PointProcessRecoveryTests.cs", + "model": "nonseasonal Poisson/POT simulation", + "analysis": "PointProcessModel simulation", + "area": "Univariate", + "declaredDependencies": [ + "GeneralizedPareto", + "Mixture" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessRecoveryTests.Test_SeasonalProductionGenerator_RecoversParentAndBothChangePoints", + "source": "src/RMC.BestFit.Verification/Univariate/PointProcessTests/PointProcessRecoveryTests.cs", + "model": "seasonal PointProcessModel with two changepoints", + "analysis": "PointProcessAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "GeneralizedPareto", + "Mixture" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessRecoveryTests.Test_SeasonalProductionGenerator_WithUnequalIntensities_RecoversParentAndBothChangePoints", + "source": "src/RMC.BestFit.Verification/Univariate/PointProcessTests/PointProcessRecoveryTests.cs", + "model": "seasonal PointProcessModel with two changepoints", + "analysis": "PointProcessAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "GeneralizedPareto", + "Mixture" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessRecoveryTests.Test_SeasonalSimulation_MatchesSeasonRatesAssignmentsAndConditionalTails", + "source": "src/RMC.BestFit.Verification/Univariate/PointProcessTests/PointProcessRecoveryTests.cs", + "model": "seasonal Poisson/POT simulation", + "analysis": "PointProcessModel simulation", + "area": "Univariate", + "declaredDependencies": [ + "GeneralizedPareto", + "Mixture" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessRecoveryTests.Test_SeasonalSimulation_WithUnequalIntensities_MatchesSeasonRatesAssignmentsAndConditionalTails", + "source": "src/RMC.BestFit.Verification/Univariate/PointProcessTests/PointProcessRecoveryTests.cs", + "model": "seasonal Poisson/POT simulation", + "analysis": "PointProcessModel simulation", + "area": "Univariate", + "declaredDependencies": [ + "GeneralizedPareto", + "Mixture" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessRecoveryTests.Test_WaterYearUniformSeasonality_AutomaticPriorsRecoverParentAndBothChangePoints", + "source": "src/RMC.BestFit.Verification/Univariate/PointProcessTests/PointProcessRecoveryTests.Uniform.cs", + "model": "seasonal PointProcessModel with two changepoints", + "analysis": "PointProcessAnalysis", + "area": "Univariate", + "declaredDependencies": [], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.GeneralizedExtremeValue_LinearShape_RecoversParentResponses", + "source": "src/RMC.BestFit.Verification/Univariate/ValidationTests/NonstationaryParentTrendCoverageTests.cs", + "model": "GeneralizedExtremeValue with constant location/scale and linear shape", + "analysis": "UnivariateAnalysis Bayesian", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GeneralizedExtremeValue", + "GeneralizedPareto", + "LogPearsonTypeIII", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.GeneralizedPareto_LinearLocationExponentialScale_RecoversParentResponses", + "source": "src/RMC.BestFit.Verification/Univariate/ValidationTests/NonstationaryParentTrendCoverageTests.cs", + "model": "GeneralizedPareto with linear location, exponential scale, and constant shape", + "analysis": "UnivariateAnalysis Bayesian", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GeneralizedExtremeValue", + "GeneralizedPareto", + "LogPearsonTypeIII", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses", + "source": "src/RMC.BestFit.Verification/Univariate/ValidationTests/NonstationaryParentTrendCoverageTests.cs", + "model": "LogPearsonTypeIII with linear log-mean, exponential log-scale, and constant skew", + "analysis": "UnivariateAnalysis Bayesian", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GeneralizedExtremeValue", + "GeneralizedPareto", + "LogPearsonTypeIII", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.Normal_ReciprocalSigma_RecoversParentResponses", + "source": "src/RMC.BestFit.Verification/Univariate/ValidationTests/NonstationaryParentTrendCoverageTests.cs", + "model": "Normal with constant mean and reciprocal scale", + "analysis": "UnivariateAnalysis Bayesian", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GeneralizedExtremeValue", + "GeneralizedPareto", + "LogPearsonTypeIII", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.Normal_SinusoidalSigma_RecoversParentResponses", + "source": "src/RMC.BestFit.Verification/Univariate/ValidationTests/NonstationaryParentTrendCoverageTests.cs", + "model": "Normal with constant mean and sinusoidal scale", + "analysis": "UnivariateAnalysis Bayesian", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GeneralizedExtremeValue", + "GeneralizedPareto", + "LogPearsonTypeIII", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryValidationTests.Nonstationary_ConstantTrend_RecoversTrueParameters", + "source": "src/RMC.BestFit.Verification/Univariate/ValidationTests/NonstationaryValidationTests.cs", + "model": "Normal; constant mean and constant sigma trends", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "Logistic", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryValidationTests.Nonstationary_ReciprocalMeanTrend_RecoversParentResponses", + "source": "src/RMC.BestFit.Verification/Univariate/ValidationTests/NonstationaryValidationTests.cs", + "model": "Normal with reciprocal mean and constant scale", + "analysis": "UnivariateAnalysis Bayesian", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "Logistic", + "Normal" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.Exponential_RecoversTrueParameters", + "source": "src/RMC.BestFit.Verification/Univariate/ValidationTests/UnivariateValidationTests.cs", + "model": "UnivariateDistribution (Exponential), stationary", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.Gamma_RecoversTrueParameters", + "source": "src/RMC.BestFit.Verification/Univariate/ValidationTests/UnivariateValidationTests.cs", + "model": "UnivariateDistribution (Gamma), stationary", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.GeneralizedExtremeValue_RecoversTrueParameters", + "source": "src/RMC.BestFit.Verification/Univariate/ValidationTests/UnivariateValidationTests.cs", + "model": "UnivariateDistribution (GeneralizedExtremeValue), stationary", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.GeneralizedLogistic_RecoversTrueParameters", + "source": "src/RMC.BestFit.Verification/Univariate/ValidationTests/UnivariateValidationTests.cs", + "model": "UnivariateDistribution (GeneralizedLogistic), stationary", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.GeneralizedNormal_RecoversTrueParameters", + "source": "src/RMC.BestFit.Verification/Univariate/ValidationTests/UnivariateValidationTests.cs", + "model": "UnivariateDistribution (GeneralizedNormal), stationary", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.GeneralizedPareto_RecoversTrueParameters", + "source": "src/RMC.BestFit.Verification/Univariate/ValidationTests/UnivariateValidationTests.cs", + "model": "UnivariateDistribution (GeneralizedPareto), stationary", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.Gumbel_RecoversTrueParameters", + "source": "src/RMC.BestFit.Verification/Univariate/ValidationTests/UnivariateValidationTests.cs", + "model": "UnivariateDistribution (Gumbel), stationary", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.KappaFour_RecoversTrueParameters", + "source": "src/RMC.BestFit.Verification/Univariate/ValidationTests/UnivariateValidationTests.cs", + "model": "UnivariateDistribution (KappaFour), stationary", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.LnNormal_RecoversTrueParameters", + "source": "src/RMC.BestFit.Verification/Univariate/ValidationTests/UnivariateValidationTests.cs", + "model": "UnivariateDistribution (LnNormal), stationary", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.Logistic_RecoversTrueParameters", + "source": "src/RMC.BestFit.Verification/Univariate/ValidationTests/UnivariateValidationTests.cs", + "model": "UnivariateDistribution (Logistic), stationary", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.LogNormal_RecoversTrueParameters", + "source": "src/RMC.BestFit.Verification/Univariate/ValidationTests/UnivariateValidationTests.cs", + "model": "UnivariateDistribution (LogNormal), stationary", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.LogPearsonTypeIII_RecoversTrueParameters", + "source": "src/RMC.BestFit.Verification/Univariate/ValidationTests/UnivariateValidationTests.cs", + "model": "UnivariateDistribution (LogPearsonTypeIII), stationary", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.Normal_RecoversTrueParameters", + "source": "src/RMC.BestFit.Verification/Univariate/ValidationTests/UnivariateValidationTests.cs", + "model": "UnivariateDistribution (Normal), stationary", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.PearsonTypeIII_RecoversTrueParameters", + "source": "src/RMC.BestFit.Verification/Univariate/ValidationTests/UnivariateValidationTests.cs", + "model": "UnivariateDistribution (PearsonTypeIII), stationary", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.Weibull_RecoversTrueParameters", + "source": "src/RMC.BestFit.Verification/Univariate/ValidationTests/UnivariateValidationTests.cs", + "model": "UnivariateDistribution (Weibull), stationary", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "Exponential", + "GammaDistribution", + "GeneralizedExtremeValue", + "GeneralizedLogistic", + "GeneralizedNormal", + "GeneralizedPareto", + "Gumbel", + "KappaFour", + "LnNormal", + "Logistic", + "LogNormal", + "LogPearsonTypeIII", + "Normal", + "PearsonTypeIII", + "Weibull" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample3", + "source": "src/RMC.BestFit.Verification/Univariate/VerificationReportTests/ArrFlikeTests.cs", + "model": "LogPearsonTypeIII; ARR Flike example 3", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "GeneralizedExtremeValue", + "LogPearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample4", + "source": "src/RMC.BestFit.Verification/Univariate/VerificationReportTests/ArrFlikeTests.cs", + "model": "LogPearsonTypeIII with historical threshold; ARR Flike example 4", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "GeneralizedExtremeValue", + "LogPearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample5", + "source": "src/RMC.BestFit.Verification/Univariate/VerificationReportTests/ArrFlikeTests.cs", + "model": "LogPearsonTypeIII with regional-skew prior; ARR Flike example 5", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "GeneralizedExtremeValue", + "LogPearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample6a", + "source": "src/RMC.BestFit.Verification/Univariate/VerificationReportTests/ArrFlikeTests.cs", + "model": "LogPearsonTypeIII; ARR Flike example 6a", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "GeneralizedExtremeValue", + "LogPearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample6b", + "source": "src/RMC.BestFit.Verification/Univariate/VerificationReportTests/ArrFlikeTests.cs", + "model": "LogPearsonTypeIII with low-outlier censoring; ARR Flike example 6b", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "GeneralizedExtremeValue", + "LogPearsonTypeIII" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.VerificationReportTests.ViglioneEtAlTests.Test1_ExactData_1951_2001", + "source": "src/RMC.BestFit.Verification/Univariate/VerificationReportTests/ViglioneEtAlTests.cs", + "model": "GeneralizedExtremeValue; Viglione information-expansion case 1", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "GeneralizedExtremeValue" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.VerificationReportTests.ViglioneEtAlTests.Test2_ExactData_1951_2005", + "source": "src/RMC.BestFit.Verification/Univariate/VerificationReportTests/ViglioneEtAlTests.cs", + "model": "GeneralizedExtremeValue; Viglione information-expansion case 2", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "GeneralizedExtremeValue" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.VerificationReportTests.ViglioneEtAlTests.Test3_ExactData_1951_2001_TemporalExpansion", + "source": "src/RMC.BestFit.Verification/Univariate/VerificationReportTests/ViglioneEtAlTests.cs", + "model": "GeneralizedExtremeValue; Viglione information-expansion case 3", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "GeneralizedExtremeValue" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.VerificationReportTests.ViglioneEtAlTests.Test4_ExactData_1951_2005_TemporalExpansion", + "source": "src/RMC.BestFit.Verification/Univariate/VerificationReportTests/ViglioneEtAlTests.cs", + "model": "GeneralizedExtremeValue; Viglione information-expansion case 4", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "GeneralizedExtremeValue" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.VerificationReportTests.ViglioneEtAlTests.Test5_ExactData_1951_2001_CausalExpansion", + "source": "src/RMC.BestFit.Verification/Univariate/VerificationReportTests/ViglioneEtAlTests.cs", + "model": "GeneralizedExtremeValue; Viglione information-expansion case 5", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "GeneralizedExtremeValue" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.VerificationReportTests.ViglioneEtAlTests.Test6_ExactData_1951_2005_CausalExpansion", + "source": "src/RMC.BestFit.Verification/Univariate/VerificationReportTests/ViglioneEtAlTests.cs", + "model": "GeneralizedExtremeValue; Viglione information-expansion case 6", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "GeneralizedExtremeValue" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.VerificationReportTests.ViglioneEtAlTests.Test7_ExactData_1951_2001_TemporalAndCausalExpansion", + "source": "src/RMC.BestFit.Verification/Univariate/VerificationReportTests/ViglioneEtAlTests.cs", + "model": "GeneralizedExtremeValue; Viglione information-expansion case 7", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "GeneralizedExtremeValue" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.VerificationReportTests.ViglioneEtAlTests.Test8_ExactData_1951_2005_TemporalAndCausalExpansion", + "source": "src/RMC.BestFit.Verification/Univariate/VerificationReportTests/ViglioneEtAlTests.cs", + "model": "GeneralizedExtremeValue; Viglione information-expansion case 8", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "GeneralizedExtremeValue" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + }, + { + "method": "RMC.BestFit.Verification.Univariate.VerificationReportTests.ViglioneEtAlTests.Test9_ExactData_1951_2001_CausalExpansion_TheePriors", + "source": "src/RMC.BestFit.Verification/Univariate/VerificationReportTests/ViglioneEtAlTests.cs", + "model": "GeneralizedExtremeValue; Viglione information-expansion case 9", + "analysis": "UnivariateAnalysis", + "area": "Univariate", + "declaredDependencies": [ + "GeneralizedExtremeValue" + ], + "dependencyBasis": "Conservative source-file and catalog family references; shared distribution kernels may expand the dependency set.", + "initialStatus": "pending" + } + ], + "unrelatedExclusions": [ + { + "method": "RMC.BestFit.Verification.ModelEstimation.GmmObjectiveGradientVerificationTests.PenalizedObjectiveGradient_MatchesIndependentCentralDifference", + "source": "src/RMC.BestFit.Verification/ModelEstimation/GmmObjectiveGradientVerificationTests.cs", + "model": "Inline one-parameter nonlinear moment model", + "analysis": "GeneralizedMethodOfMoments" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.GmmObjectiveGradientVerificationTests.UnpenalizedEstimatingGradient_IsHalfConventionalObjectiveDerivative", + "source": "src/RMC.BestFit.Verification/ModelEstimation/GmmObjectiveGradientVerificationTests.cs", + "model": "Inline one-parameter nonlinear moment model", + "analysis": "GeneralizedMethodOfMoments" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.GmmSpecificationVerificationTests.HansenJ_MatchesRGmmSelectedWeightStatistic", + "source": "src/RMC.BestFit.Verification/ModelEstimation/GmmSpecificationVerificationTests.cs", + "model": "One-parameter, two-moment overidentified GMM fixture", + "analysis": "GeneralizedMethodOfMoments" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.GmmSpecificationVerificationTests.OveridentifiedOneStep_MatchesRGmmFixedWeightOracle", + "source": "src/RMC.BestFit.Verification/ModelEstimation/GmmSpecificationVerificationTests.cs", + "model": "One-parameter, two-moment overidentified GMM fixture", + "analysis": "GeneralizedMethodOfMoments" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.GmmSpecificationVerificationTests.OveridentifiedTwoStepSandwichCovariance_MatchesRGmmOracle", + "source": "src/RMC.BestFit.Verification/ModelEstimation/GmmSpecificationVerificationTests.cs", + "model": "One-parameter, two-moment overidentified GMM fixture", + "analysis": "GeneralizedMethodOfMoments" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.GmmSpecificationVerificationTests.OveridentifiedFixedWeightSandwichCovariance_MatchesRGmmOracle", + "source": "src/RMC.BestFit.Verification/ModelEstimation/GmmSpecificationVerificationTests.cs", + "model": "One-parameter, two-moment overidentified GMM fixture", + "analysis": "GeneralizedMethodOfMoments" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.MaximumLikelihoodCovarianceVerificationTests.MLE_OneParameterModel_CovarianceMatchesClosedFormInformation", + "source": "src/RMC.BestFit.Verification/ModelEstimation/MaximumLikelihoodCovarianceVerificationTests.cs", + "model": "Known-scale Normal mean model", + "analysis": "MaximumLikelihood" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.MaximumLikelihoodCovarianceVerificationTests.MLE_AndFlatPriorMAP_ReportTheSameCovariance", + "source": "src/RMC.BestFit.Verification/ModelEstimation/MaximumLikelihoodCovarianceVerificationTests.cs", + "model": "Two-parameter bounded quadratic likelihood", + "analysis": "MaximumLikelihood and MaximumAPosteriori" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.ProfileLikelihoodGridPointFailureTests.MLE_ProfileLikelihood_SupportedGridMatchesClosedFormProfile", + "source": "src/RMC.BestFit.Verification/ModelEstimation/ProfileLikelihoodGridPointFailureTests.cs", + "model": "Support-restricted correlated quadratic likelihood", + "analysis": "MaximumLikelihood" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.ProfileLikelihoodGridPointFailureTests.MAP_ProfileLikelihood_SupportedGridMatchesClosedFormProfile", + "source": "src/RMC.BestFit.Verification/ModelEstimation/ProfileLikelihoodGridPointFailureTests.cs", + "model": "Support-restricted correlated quadratic likelihood", + "analysis": "MaximumAPosteriori" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.ProfileLikelihoodVerificationTests.MLE_ProfileLikelihood_MatchesRTrueProfile", + "source": "src/RMC.BestFit.Verification/ModelEstimation/ProfileLikelihoodVerificationTests.cs", + "model": "Correlated two-parameter quadratic likelihood", + "analysis": "MaximumLikelihood" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.ProfileLikelihoodVerificationTests.MAP_ProfileLikelihood_WithFlatPriors_MatchesRTrueProfile", + "source": "src/RMC.BestFit.Verification/ModelEstimation/ProfileLikelihoodVerificationTests.cs", + "model": "Correlated two-parameter quadratic likelihood", + "analysis": "MaximumAPosteriori" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.ProfileLikelihoodVerificationTests.MAP_ProfileLikelihood_WithInformativePrior_ProfilesFullPosteriorKernel", + "source": "src/RMC.BestFit.Verification/ModelEstimation/ProfileLikelihoodVerificationTests.cs", + "model": "Correlated two-parameter quadratic likelihood", + "analysis": "MaximumAPosteriori" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.PsisLooOracleVerificationTests.RlooOracle_InternalIdentitiesAreConsistent", + "source": "src/RMC.BestFit.Verification/ModelEstimation/PsisLooOracleVerificationTests.cs", + "model": "Fixed Normal pointwise log-likelihood matrix", + "analysis": "BayesianAnalysis PSIS-LOO" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.PsisLooOracleVerificationTests.PsisTailRegimes_MatchRLooOracle", + "source": "src/RMC.BestFit.Verification/ModelEstimation/PsisLooOracleVerificationTests.cs", + "model": "Fixed Normal pointwise log-likelihood matrix", + "analysis": "BayesianAnalysis PSIS-LOO" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.GoodnessOfFitRmseVerificationTests.ParameterAdjustedRmse_UsesAllResidualsAndIsPermutationInvariant", + "source": "src/RMC.BestFit.Verification/DistributionFitting/GoodnessOfFitRmseVerificationTests.cs", + "model": "Parameter-adjusted RMSE", + "analysis": "GoodnessOfFit" + }, + { + "method": "RMC.BestFit.Verification.DistributionFitting.FittingAnalysisCriteriaVerificationTests.ExactSeriesReplacement_ProducesAnalyticalWeibullPositions", + "source": "src/RMC.BestFit.Verification/DistributionFitting/FittingAnalysisCriteriaVerificationTests.cs", + "model": "DataFrame exact-series Weibull plotting positions", + "analysis": "FittingAnalysis input preparation" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.HirschStedingerPlottingPositionVerificationTests.Example4_MatchesPeakFQ", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CExampleTests.cs", + "model": "Hirsch-Stedinger plotting positions; worked Example 4", + "analysis": "Bulletin17C plotting positions" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.HirschStedingerPlottingPositionVerificationTests.Example5_MatchesPeakFQ", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CExampleTests.cs", + "model": "Hirsch-Stedinger plotting positions; worked Example 5", + "analysis": "Bulletin17C plotting positions" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.HirschStedingerPlottingPositionVerificationTests.Example7_MatchesPeakFQ", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CExampleTests.cs", + "model": "Hirsch-Stedinger plotting positions; worked Example 7", + "analysis": "Bulletin17C plotting positions" + }, + { + "method": "RMC.BestFit.Verification.RatingCurve.RatingCurveContinuityVerificationTests.AddedControl_TwoSidedIncrementAtActivation_MatchesAnalyticalPowerLaw", + "source": "src/RMC.BestFit.Verification/RatingCurve/RatingCurveContinuityVerificationTests.cs", + "model": "piecewise added-control power law at activation", + "analysis": "RatingCurve model evaluation" + }, + { + "method": "RMC.BestFit.Verification.RatingCurve.RatingCurveContinuityVerificationTests.ExponentsAtDefaultLowerBound_AddedControlIncrementsVanishAtActivation", + "source": "src/RMC.BestFit.Verification/RatingCurve/RatingCurveContinuityVerificationTests.cs", + "model": "piecewise added-control power law at activation", + "analysis": "RatingCurve model evaluation" + }, + { + "method": "RMC.BestFit.Verification.RatingCurve.RatingCurveContinuityVerificationTests.ZeroExponent_AddedControlJumpsByItsCoefficientAtActivation", + "source": "src/RMC.BestFit.Verification/RatingCurve/RatingCurveContinuityVerificationTests.cs", + "model": "piecewise added-control power law at activation", + "analysis": "RatingCurve model evaluation" + }, + { + "method": "RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.ArimaAndArimaxPredictionReintegrationMatchesHandRecurrenceOracle", + "source": "src/RMC.BestFit.Verification/TimeSeriesAnalysis/TimeSeriesIndependentOracleTests.cs", + "model": "ARIMA(1,1,1) log transform and ARIMAX(1,1,0) level covariate", + "analysis": "Time-series model calculation/oracle" + }, + { + "method": "RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.JeffreysScaleMetadataMatchesIndependentPriorOracle", + "source": "src/RMC.BestFit.Verification/TimeSeriesAnalysis/TimeSeriesIndependentOracleTests.cs", + "model": "AR, MA, ARIMA, and ARIMAX scale priors", + "analysis": "Time-series model calculation/oracle" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVChunk14OracleTests.BasicExponentialCorrelation_MatchesAnalyticalGrid", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVChunk14OracleTests.cs", + "model": "Basic exponential correlation with Cartesian distance", + "analysis": "Spatial correlation" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVChunk14OracleTests.UngaugedDrawSpecificPrediction_MatchesIndependentGeodesicGaussianOracle", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVChunk14OracleTests.cs", + "model": "Three-site geodesic exponential latent-error network with four fixed parameter draws", + "analysis": "SpatialGEV ungauged prediction" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVChunk14OracleTests.PoweredExponentialCorrelation_MatchesAnalyticalGrid", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVChunk14OracleTests.cs", + "model": "Powered exponential correlation with range 20 and smoothness 1.6", + "analysis": "Spatial correlation" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVChunk14OracleTests.SphericalCorrelation_MatchesAnalyticalGridAndCompactSupport", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVChunk14OracleTests.cs", + "model": "Spherical correlation with compact support at range 20", + "analysis": "Spatial correlation" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVChunk14OracleTests.HeldOutCovariateFold_MatchesIndependentRegressionOracle", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVChunk14OracleTests.cs", + "model": "Four-site fixed log-location summaries with two location covariates and one held-out target row", + "analysis": "SpatialGEV held-out covariate fold" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVDistanceOracleTests.CartesianMetric_IsPlanarEuclidean", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVDistanceOracleTests.cs", + "model": "Five-site Cartesian distance with exponential correlation components", + "analysis": "Spatial distance" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVDistanceOracleTests.GeodesicMetric_MatchesHaversineOracle", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVDistanceOracleTests.cs", + "model": "Five-site exponential correlation with geodesic distance", + "analysis": "Spatial distance, correlation, and kriging" + }, + { + "method": "RMC.BestFit.Verification.SpatialExtremes.SpatialGEVKrigingOracleTests.KrigingPrediction_MatchesConditionalGaussianProcessOracle", + "source": "src/RMC.BestFit.Verification/SpatialExtremes/SpatialGEVKrigingOracleTests.cs", + "model": "Five-site exponential latent Gaussian process", + "analysis": "Spatial conditional prediction" + }, + { + "method": "RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryValidationTests.ReciprocalTrend_ValuesAndParameterDerivativesMatchAnalyticalFormula", + "source": "src/RMC.BestFit.Verification/Univariate/ValidationTests/NonstationaryValidationTests.cs", + "model": "Reciprocal trend f(t)=1/(a+b(t-StartIndex))", + "analysis": "ReciprocalTrend" + }, + { + "method": "RMC.BestFit.Verification.ModelEstimation.GmmGeneratedParentRecoveryTests.TwoStepGmm_RecoversGeneratedParent_WithinStandardizedError", + "source": "src/RMC.BestFit.Verification/ModelEstimation/GmmGeneratedParentRecoveryTests.cs", + "model": "One-parameter two-step GMM with two response/instrument moments", + "analysis": "GeneralizedMethodOfMoments" + } + ], + "absentCoverageMethods": [ + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCensoredCoverageTests.LP3_CensoredGrid_Diagnostics", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCensoredCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); censored confidence-interval coverage", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCensoredCoverageTests.LP3_CombinedCensoring_N50_Bootstrap", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCensoredCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); censored confidence-interval coverage", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCensoredCoverageTests.LP3_CombinedCensoring_N50_LinkedMVN", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCensoredCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); censored confidence-interval coverage", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCensoredCoverageTests.LP3_HistoricalThreshold_N50_Bootstrap", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCensoredCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); censored confidence-interval coverage", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCensoredCoverageTests.LP3_HistoricalThreshold_N50_LinkedMVN", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCensoredCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); censored confidence-interval coverage", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCensoredCoverageTests.LP3_LowOutliers_N50_Bootstrap", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCensoredCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); censored confidence-interval coverage", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCensoredCoverageTests.LP3_LowOutliers_N50_LinkedMVN", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCensoredCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); censored confidence-interval coverage", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCensoredCoverageTests.Normal_LowOutliers_N50_LinkedMVN", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCensoredCoverageTests.cs", + "model": "Bulletin17CDistribution (Normal); censored confidence-interval coverage", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_GammaMinus1p0_Ns25_Nh0_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=-1.0 Ns=25 Nh=0", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_GammaMinus1p0_Ns25_Nh50_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=-1.0 Ns=25 Nh=50", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_GammaMinus1p0_Ns25_Nh150_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=-1.0 Ns=25 Nh=150", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_GammaMinus1p0_Ns100_Nh0_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=-1.0 Ns=100 Nh=0", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_GammaMinus1p0_Ns100_Nh50_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=-1.0 Ns=100 Nh=50", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_GammaMinus1p0_Ns100_Nh150_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=-1.0 Ns=100 Nh=150", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_GammaMinus0p5_Ns25_Nh0_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=-0.5 Ns=25 Nh=0", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_GammaMinus0p5_Ns25_Nh50_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=-0.5 Ns=25 Nh=50", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_GammaMinus0p5_Ns25_Nh150_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=-0.5 Ns=25 Nh=150", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_GammaMinus0p5_Ns100_Nh0_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=-0.5 Ns=100 Nh=0", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_GammaMinus0p5_Ns100_Nh50_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=-0.5 Ns=100 Nh=50", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_GammaMinus0p5_Ns100_Nh150_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=-0.5 Ns=100 Nh=150", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_Gamma0p0_Ns25_Nh0_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=0.0 Ns=25 Nh=0", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_Gamma0p0_Ns25_Nh50_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=0.0 Ns=25 Nh=50", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_Gamma0p0_Ns25_Nh150_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=0.0 Ns=25 Nh=150", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_Gamma0p0_Ns100_Nh0_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=0.0 Ns=100 Nh=0", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_Gamma0p0_Ns100_Nh50_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=0.0 Ns=100 Nh=50", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_Gamma0p0_Ns100_Nh150_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=0.0 Ns=100 Nh=150", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_Gamma0p5_Ns25_Nh0_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=0.5 Ns=25 Nh=0", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_Gamma0p5_Ns25_Nh50_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=0.5 Ns=25 Nh=50", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_Gamma0p5_Ns25_Nh150_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=0.5 Ns=25 Nh=150", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_Gamma0p5_Ns100_Nh0_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=0.5 Ns=100 Nh=0", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_Gamma0p5_Ns100_Nh50_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=0.5 Ns=100 Nh=50", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_Gamma0p5_Ns100_Nh150_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=0.5 Ns=100 Nh=150", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_Gamma1p0_Ns25_Nh0_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=1.0 Ns=25 Nh=0", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_Gamma1p0_Ns25_Nh50_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=1.0 Ns=25 Nh=50", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_Gamma1p0_Ns25_Nh150_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=1.0 Ns=25 Nh=150", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_Gamma1p0_Ns100_Nh0_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=1.0 Ns=100 Nh=0", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_Gamma1p0_Ns100_Nh50_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=1.0 Ns=100 Nh=50", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCohnEtAlCoverageTests.CohnEtAl_LP3_Gamma1p0_Ns100_Nh150_Coverage", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCohnEtAlCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); Cohn et al. Table 3 coverage grid; LP3 γ=1.0 Ns=100 Nh=150", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCoverageTests.Exponential_Coverage_N100", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCoverageTests.cs", + "model": "Bulletin17CDistribution (Exponential); confidence-interval coverage", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCoverageTests.Exponential_Coverage_N25", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCoverageTests.cs", + "model": "Bulletin17CDistribution (Exponential); confidence-interval coverage", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCoverageTests.Gamma_Coverage_N100", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCoverageTests.cs", + "model": "Bulletin17CDistribution (Gamma); confidence-interval coverage", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCoverageTests.Gamma_Coverage_N25", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCoverageTests.cs", + "model": "Bulletin17CDistribution (Gamma); confidence-interval coverage", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCoverageTests.LogNormal_Coverage_N100", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCoverageTests.cs", + "model": "Bulletin17CDistribution (LogNormal); confidence-interval coverage", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCoverageTests.LogNormal_Coverage_N25", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCoverageTests.cs", + "model": "Bulletin17CDistribution (LogNormal); confidence-interval coverage", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCoverageTests.LogNormal_CoverageWithMuPenalty_N25", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCoverageTests.cs", + "model": "Bulletin17CDistribution (LogNormal); confidence-interval coverage", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCoverageTests.LogNormal_CoverageWithMuSigmaAndQ99Penalty_N25", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCoverageTests.cs", + "model": "Bulletin17CDistribution (LogNormal); confidence-interval coverage", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCoverageTests.LogNormal_CoverageWithMuSigmaPenalty_N25", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCoverageTests.cs", + "model": "Bulletin17CDistribution (LogNormal); confidence-interval coverage", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCoverageTests.LogPearsonTypeIII_Coverage_N100", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); confidence-interval coverage", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCoverageTests.LogPearsonTypeIII_Coverage_N25", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); confidence-interval coverage", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCoverageTests.LogPearsonTypeIII_CoverageWithAllParamsPenalty_N25", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); confidence-interval coverage", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCoverageTests.LogPearsonTypeIII_CoverageWithGammaPenalty_N100", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); confidence-interval coverage", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCoverageTests.LogPearsonTypeIII_CoverageWithGammaPenalty_N25", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCoverageTests.cs", + "model": "Bulletin17CDistribution (LogPearsonTypeIII); confidence-interval coverage", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCoverageTests.Normal_Coverage_N100", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCoverageTests.cs", + "model": "Bulletin17CDistribution (Normal); confidence-interval coverage", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCoverageTests.Normal_Coverage_N25", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCoverageTests.cs", + "model": "Bulletin17CDistribution (Normal); confidence-interval coverage", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCoverageTests.PearsonTypeIII_Coverage_N100", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCoverageTests.cs", + "model": "Bulletin17CDistribution (PearsonTypeIII); confidence-interval coverage", + "analysis": "Bulletin17CAnalysis" + }, + { + "method": "RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCoverageTests.PearsonTypeIII_Coverage_N25", + "source": "src/RMC.BestFit.Verification/Univariate/Bulletin17CTests/B17CCoverageTests.cs", + "model": "Bulletin17CDistribution (PearsonTypeIII); confidence-interval coverage", + "analysis": "Bulletin17CAnalysis" + } + ] +} diff --git a/docs/distributions/bestfit-regression-repair-plan.md b/docs/distributions/bestfit-regression-repair-plan.md new file mode 100644 index 00000000..ab90f93a --- /dev/null +++ b/docs/distributions/bestfit-regression-repair-plan.md @@ -0,0 +1,37 @@ +# Iterative BestFit distribution regression repair + +Approved by Haden Smith on 2026-09-08. This is an execution plan; scoped regression repairs and each exact method in the companion allowlist are authorized. + +## Objective and frozen contracts + +Eliminate numerical and reproducible runtime regressions attributable to the recent Numerics distribution hardening and BestFit integration. Restore the previous `GetParameterConstraints()` logic, initialization, family-specific magnitude rounding, and valid prior envelopes. Exceptional-input handling must remain separate from previously working cases. Apply complete covariance transformations into the verification tests' existing coordinates, retaining cross-terms and recovery rules. + +Do not change tolerances, acceptance thresholds, confidence levels, reference values, seeds, sample sizes, sampler settings, optimizer settings, or default scientific policies. Do not skip failing tests. A conflict requiring a new scientific policy goes to Haden for a specific decision. + +## Source versions and isolation + +- Numerics comparison: `d80bfa8621c48a78cf4ebad7f01326841fac37aa`; repair start: `a3bd2afd64286e1b8df3ada8be711d55180cbb11`. +- BestFit comparison: `fbe0989a87a0bd38a297f8a42e8d979585d0f4ad`; repair start: `3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a`. +- Earlier Kappa comparison if needed: `94d1713e03a4c416dd472d6fbef0ce907881e9d1` to `c0d67b9c52d62dc49bdbfb744ec47b9a205cc86b`. +- Both repositories have isolated repair and detached baseline worktrees under `artifacts/worktrees/bestfit-regression-{repair,baseline}`. Original dirty checkouts are preserved. +- Resolve local Numerics explicitly through `UseLocalRmcNumerics=true` and `RmcNumericsProjectPath`; record source and assembly fingerprints. + +## Allowlist and iteration + +The companion inventory freezes 293 exact catalog identities: 50 distribution-fitting, 40 estimation/diagnostic, 117 univariate, 25 bivariate, 16 rating-curve, 22 time-series, and 23 spatial. It records 35 unrelated exclusions and 56 source methods absent in the original dirty BestFit checkout. Reappearing coverage sources in a comparison worktree do not expand authorization. + +1. Run one allowlisted fully qualified method through BestFit's guarded runner, serially. Independently verify one TRX, one result, and the exact requested class/method. +2. Record result, test duration separately from build/host time, source versions/fingerprints, dependencies, and exact TRX path. +3. On failure or material slowdown, immediately compare the unchanged method with the relevant baseline; isolate Numerics versus integration effects and inspect inputs, initialization, bounds, likelihoods, estimates, diagnostics, and computational hot paths. +4. Fix attributable regressions immediately, add a focused regression check, rerun the method, and mark earlier dependent passes stale. Continue independent work if a case needs a scientific decision. +5. Begin with Pearson and Log-Pearson covariance failures and confirmed constraint regressions; then order by dependencies and observed failures. + +## Tasks and validation + +1. Freeze the allowlist, dirty-file fingerprints, and resumable ledger; reproduce the two covariance failures against the pinned baseline. +2. Restore legacy constraints and initialization for previously valid inputs, preserving readable family-specific rounding; retain robust exceptional-input fallback. Add baseline-derived checks and verify resulting BestFit Uniform priors and nonstationary bounds. +3. Correct full covariance coordinate transformations for Pearson, Log-Pearson, and LnNormal verification helpers. Keep existing numerical acceptance unchanged and validate cross-term propagation independently. +4. Execute the iterative 293-method loop; investigate all affected density/tail/interval/quantile/support/moment/derivative/cache/composite paths as evidence requires. +5. After repairs stabilize, obtain a fresh complete 293-method pass. Run Numerics Release and framework gates, BestFit fast and XML documentation gates, inspect prohibited-setting and unrelated-work diffs, and commit validated changes locally. Do not push. + +The ledger is authoritative for progress. A partial run is not completion. Any change to production dependencies invalidates earlier affected evidence until rerun. diff --git a/docs/distributions/bestfit-regression-runs.jsonl b/docs/distributions/bestfit-regression-runs.jsonl new file mode 100644 index 00000000..59a4ebe4 --- /dev/null +++ b/docs/distributions/bestfit-regression-runs.jsonl @@ -0,0 +1,804 @@ +{"runId":"20260908-174014-070-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"numerics-attribution","startedUtc":"2026-09-08T23:40:14.0737444+00:00","wallSeconds":7.6268591,"testSeconds":1.9725318,"buildSeconds":3.79,"result":"Failed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260908-174014-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-08_23_40_21.437.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-174014-070-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"0D9230B7258815CF1F335E47FA3B0B1AE60513437E3AF087DC92698F284A9CC5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"39B6C5910666D50AE6A7A93A5E7513F0E4B3CD5BD4721E744F42E91FE8EBF232"},"numericsAssemblySha256":"09071F0CE640A4DF35F563B146D9D3E2544947D836315C872FDC2E5C878A5BF5","bestFitAssemblySha256":"28CCB3C205FC372B48A4EC9BCDA10B8A40550C865918EED2B9EB539D88B0822D","verificationAssemblySha256":"BE10AA6F1AB342EA487BEB4D1755799DD23206E1499A1AC1161FC286287E2F6F","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":"Assert.IsTrue failed. OneOverBeta estimate 16.391525738995757, parent 13.333333333333334, and observed-information standard error 0.007018816504988889 give standardized parent error 435.71340032734821, exceeding 1.96.","note":"Unchanged pre-integration BestFit with hardened Numerics; isolate the covariance regression."} +{"runId":"20260908-164053-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogPearsonTypeIII_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.LogPearsonTypeIII_MleAndDistributionFunctionsMatchScipy","phase":"planning-probe","result":"Passed","testSeconds":0.1599188,"wallSeconds":null,"buildSeconds":null,"trx":"C:\\GIT\\RMC-BestFit\\TestResults\\VerificationFocused\\20260908-164053-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogPearsonTypeIII_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-08_22_41_16.335.trx","exactIdentityVerified":true,"numerics":{"commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11"},"bestFit":{"commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a"},"dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Original current source, before repair; imported exact TRX."} +{"runId":"20260908-164308-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"planning-probe","result":"Passed","testSeconds":2.3128399,"wallSeconds":null,"buildSeconds":null,"trx":"C:\\GIT\\RMC-BestFit\\TestResults\\VerificationFocused\\20260908-164308-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-08_22_43_21.056.trx","exactIdentityVerified":true,"numerics":{"commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11"},"bestFit":{"commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a"},"dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Original current source, before repair; imported exact TRX."} +{"runId":"20260908-164430-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"planning-probe","result":"Failed","testSeconds":1.7186883,"wallSeconds":null,"buildSeconds":null,"trx":"C:\\GIT\\RMC-BestFit\\TestResults\\VerificationFocused\\20260908-164430-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-08_22_44_37.466.trx","exactIdentityVerified":true,"numerics":{"commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11"},"bestFit":{"commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a"},"dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":"Assert.IsTrue failed. Alpha estimate 7.6131789678146005, parent 6.25, and observed-information standard error 0.062852346309427404 give standardized parent error 21.688593152967677, exceeding 1.96.","note":"Original current source, before repair; imported exact TRX."} +{"runId":"20260908-164646-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"planning-probe","result":"Failed","testSeconds":2.0314552,"wallSeconds":null,"buildSeconds":null,"trx":"C:\\GIT\\RMC-BestFit\\TestResults\\VerificationFocused\\20260908-164646-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-08_22_46_54.077.trx","exactIdentityVerified":true,"numerics":{"commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11"},"bestFit":{"commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a"},"dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":"Assert.IsTrue failed. OneOverBeta estimate 16.391525738995757, parent 13.333333333333334, and observed-information standard error 0.007018816504988889 give standardized parent error 435.71340032734821, exceeding 1.96.","note":"Original current source, before repair; imported exact TRX."} +{"runId":"20260908-173403-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"baseline","result":"Passed","testSeconds":2.1206479,"wallSeconds":null,"buildSeconds":null,"trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260908-173403-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-08_23_34_26.445.trx","exactIdentityVerified":true,"numerics":{"commit":"d80bfa8621c48a78cf4ebad7f01326841fac37aa"},"bestFit":{"commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad"},"dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Pinned pre-integration sources; imported exact TRX."} +{"runId":"20260908-173639-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"baseline","result":"Passed","testSeconds":2.2995572,"wallSeconds":null,"buildSeconds":null,"trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260908-173639-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-08_23_36_47.636.trx","exactIdentityVerified":true,"numerics":{"commit":"d80bfa8621c48a78cf4ebad7f01326841fac37aa"},"bestFit":{"commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad"},"dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Pinned pre-integration sources; imported exact TRX."} +{"runId":"20260908-173802-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"numerics-attribution","result":"Failed","testSeconds":1.8884252,"wallSeconds":null,"buildSeconds":null,"trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260908-173802-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-08_23_38_13.948.trx","exactIdentityVerified":true,"numerics":{"commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11"},"bestFit":{"commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad"},"dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":"Assert.IsTrue failed. Alpha estimate 7.6131789678146005, parent 6.25, and observed-information standard error 0.062852346309427404 give standardized parent error 21.688593152967677, exceeding 1.96.","note":"Pre-integration BestFit with hardened Numerics reproduces current failure."} +{"runId":"20260908-174144-050-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample3","phase":"initial-current","startedUtc":"2026-09-08T23:41:44.0522603+00:00","wallSeconds":11.4825626,"testSeconds":5.566251,"buildSeconds":4.05,"result":"Failed","trx":"C:\\GIT\\RMC-BestFit\\TestResults\\VerificationFocused\\20260908-174144-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3\\haden_HADEN_2026-09-08_23_41_55.259.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-174144-050-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"0D9230B7258815CF1F335E47FA3B0B1AE60513437E3AF087DC92698F284A9CC5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"E88460F3D355CEEC0DE0D2EF5FCF566892FAF88BF4F7C200E6DD56DF07C5843B"},"numericsAssemblySha256":"09071F0CE640A4DF35F563B146D9D3E2544947D836315C872FDC2E5C878A5BF5","bestFitAssemblySha256":"FF3E81B0EFF99D768317B48A0C591D8FC3C0AD4DC6716713240638C74408F903","verificationAssemblySha256":"5CD3BD063CC494F7664CEDC1EDE43D9A0708B491AC18D718CE78BCEE0C997646","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":"Assert.AreEqual failed. Expected a difference no greater than <42797.625> between expected value <570635> and actual value <523115.4582976627>. Upper CI 0 is incorrect.","note":"First unchanged published LP3 MCMC example; examine effects beyond covariance while constraint restoration proceeds in isolated worktree."} +{"runId":"20260908-174330-663-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample3","phase":"baseline","startedUtc":"2026-09-08T23:43:30.6671111+00:00","wallSeconds":13.3160851,"testSeconds":4.4055886,"buildSeconds":6.02,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260908-174330-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3\\haden_HADEN_2026-09-08_23_43_43.741.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-174330-663-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"d80bfa8621c48a78cf4ebad7f01326841fac37aa","sourceSha256":"95A9BB4F8927EF0934A13A39B566C1FA30BDB42B2F95AC965746DD122DB39BA0"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"39B6C5910666D50AE6A7A93A5E7513F0E4B3CD5BD4721E744F42E91FE8EBF232"},"numericsAssemblySha256":"3E82F983E4795FD50104CBF0F950A7513BDF94540C3397401A8EC5BBF1A6C60F","bestFitAssemblySha256":"67A7E4809110D816C06ED83E4F9068F6E6FA78E490A5486FCEF62A56D7019DC0","verificationAssemblySha256":"542282B3090D914193685DF647EB18960BC31D061A17CACA7E63A2AA92CC9E46","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Compare published LP3 upper credible limit failure without changing the test."} +{"runId":"20260908-174507-605-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"repair-confirmation","startedUtc":"2026-09-08T23:45:07.6089218+00:00","wallSeconds":15.2914098,"testSeconds":1.7995424,"buildSeconds":10.78,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-174507-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-08_23_45_22.654.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-174507-605-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"3A66E30BAAB4BC4AFD3722B43EC9A130D934B2063D1392A28A4A839762625A3D"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"183A32847E467815A18A3F41832B35000B861B50BF828A3DA46FCF42B4ECC537","bestFitAssemblySha256":"B82F2CCA2287940DFA736EF40649258B41FA04FFE067F5CDBBFF4E4565F48B0B","verificationAssemblySha256":"85B165C5351735F639F0E10689121321DF783FEA3B8ADD086B80308078F4A664","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Approved full covariance transformation with restored legacy constraints."} +{"runId":"20260908-174523-863-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"repair-confirmation","startedUtc":"2026-09-08T23:45:23.8632896+00:00","wallSeconds":7.6039762,"testSeconds":1.5903242,"buildSeconds":4.1,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-174523-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-08_23_45_31.205.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-174523-863-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"3A66E30BAAB4BC4AFD3722B43EC9A130D934B2063D1392A28A4A839762625A3D"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"183A32847E467815A18A3F41832B35000B861B50BF828A3DA46FCF42B4ECC537","bestFitAssemblySha256":"B82F2CCA2287940DFA736EF40649258B41FA04FFE067F5CDBBFF4E4565F48B0B","verificationAssemblySha256":"85B165C5351735F639F0E10689121321DF783FEA3B8ADD086B80308078F4A664","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Approved full covariance transformation with restored legacy constraints."} +{"runId":"20260908-174532-236-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"repair-confirmation","startedUtc":"2026-09-08T23:45:32.2362993+00:00","wallSeconds":7.7233239,"testSeconds":1.9712581,"buildSeconds":4.02,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-174532-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-08_23_45_39.714.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-174532-236-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"3A66E30BAAB4BC4AFD3722B43EC9A130D934B2063D1392A28A4A839762625A3D"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"183A32847E467815A18A3F41832B35000B861B50BF828A3DA46FCF42B4ECC537","bestFitAssemblySha256":"B82F2CCA2287940DFA736EF40649258B41FA04FFE067F5CDBBFF4E4565F48B0B","verificationAssemblySha256":"85B165C5351735F639F0E10689121321DF783FEA3B8ADD086B80308078F4A664","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Approved full covariance transformation with restored legacy constraints."} +{"runId":"20260908-174647-184-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample3","phase":"numerics-attribution","startedUtc":"2026-09-08T23:46:47.1868207+00:00","wallSeconds":14.0711622,"testSeconds":6.0658684,"buildSeconds":5.57,"result":"Failed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260908-174647-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3\\haden_HADEN_2026-09-08_23_47_00.922.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-174647-184-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"0D9230B7258815CF1F335E47FA3B0B1AE60513437E3AF087DC92698F284A9CC5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"D07BA0CC2820C78B1A275C995AA2EA481C15A370AAA96C55A5B7B1B1F4793BCA"},"numericsAssemblySha256":"09071F0CE640A4DF35F563B146D9D3E2544947D836315C872FDC2E5C878A5BF5","bestFitAssemblySha256":"28CCB3C205FC372B48A4EC9BCDA10B8A40550C865918EED2B9EB539D88B0822D","verificationAssemblySha256":"BE10AA6F1AB342EA487BEB4D1755799DD23206E1499A1AC1161FC286287E2F6F","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":"Assert.AreEqual failed. Expected a difference no greater than <42797.625> between expected value <570635> and actual value <523115.4582976627>. Upper CI 0 is incorrect.","note":"Baseline passes; isolate Numerics effects on the published LP3 upper credible interval."} +{"runId":"20260908-174949-725-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample3","phase":"stabilized-pass","startedUtc":"2026-09-08T23:49:49.7276205+00:00","wallSeconds":14.6718741,"testSeconds":5.4069217,"buildSeconds":7.0,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-174949-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3\\haden_HADEN_2026-09-08_23_50_04.139.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-174949-725-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Retest failed published LP3 MCMC case after legacy prior restoration; all scientific settings unchanged."} +{"runId":"20260908-175138-615-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample4","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample4","phase":"stabilized-pass","startedUtc":"2026-09-08T23:51:38.6168465+00:00","wallSeconds":11.2825897,"testSeconds":5.6341647,"buildSeconds":3.71,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175138-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample4\\haden_HADEN_2026-09-08_23_51_49.642.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175138-615-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample4.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Continue published LP3/GEV examples serially after prior restoration; stop immediately for any failure."} +{"runId":"20260908-175150-965-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample5","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample5","phase":"stabilized-pass","startedUtc":"2026-09-08T23:51:50.9654582+00:00","wallSeconds":10.5797365,"testSeconds":4.9342929,"buildSeconds":3.95,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175151-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample5\\haden_HADEN_2026-09-08_23_52_01.286.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175150-965-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample5.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Continue published LP3/GEV examples serially after prior restoration; stop immediately for any failure."} +{"runId":"20260908-175202-331-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6a","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample6a","phase":"stabilized-pass","startedUtc":"2026-09-08T23:52:02.3319668+00:00","wallSeconds":8.4895619,"testSeconds":2.9545495,"buildSeconds":3.85,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175202-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6a\\haden_HADEN_2026-09-08_23_52_10.580.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175202-331-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6a.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Continue published LP3/GEV examples serially after prior restoration; stop immediately for any failure."} +{"runId":"20260908-175211-534-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6b","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample6b","phase":"stabilized-pass","startedUtc":"2026-09-08T23:52:11.5345708+00:00","wallSeconds":8.5219184,"testSeconds":3.0151533,"buildSeconds":3.77,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175211-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6b\\haden_HADEN_2026-09-08_23_52_19.809.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175211-534-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6b.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Continue published LP3/GEV examples serially after prior restoration; stop immediately for any failure."} +{"runId":"20260908-175346-488-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-08T23:53:46.4905901+00:00","wallSeconds":7.2773772,"testSeconds":2.023944,"buildSeconds":3.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175346-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-08_23_53_53.522.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175346-488-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Distribution-fitting dependency group, serial exact method; earlier covariance passes rerun after constraint review."} +{"runId":"20260908-175354-797-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-08T23:53:54.7970906+00:00","wallSeconds":6.834891,"testSeconds":1.5967222,"buildSeconds":3.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175354-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-08_23_54_01.396.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175354-797-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Distribution-fitting dependency group, serial exact method; earlier covariance passes rerun after constraint review."} +{"runId":"20260908-175402-298-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-08T23:54:02.2986518+00:00","wallSeconds":7.4416966,"testSeconds":1.6363605,"buildSeconds":3.87,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175402-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-08_23_54_09.488.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175402-298-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Distribution-fitting dependency group, serial exact method; earlier covariance passes rerun after constraint review."} +{"runId":"20260908-175410-457-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Exponential_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Exponential_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-08T23:54:10.4577845+00:00","wallSeconds":5.6274362,"testSeconds":0.1003251,"buildSeconds":3.82,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175410-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Exponential_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-08_23_54_15.831.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175410-457-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Exponential_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Distribution-fitting dependency group, serial exact method; earlier covariance passes rerun after constraint review."} +{"runId":"20260908-175416-855-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Gamma_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Gamma_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-08T23:54:16.8555479+00:00","wallSeconds":5.6038554,"testSeconds":0.0638044,"buildSeconds":3.65,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175416-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Gamma_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-08_23_54_22.110.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175416-855-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Gamma_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Distribution-fitting dependency group, serial exact method; earlier covariance passes rerun after constraint review."} +{"runId":"20260908-175423-179-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_GeneralizedExtremeValue_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.GeneralizedExtremeValue_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-08T23:54:23.1791366+00:00","wallSeconds":5.9820459,"testSeconds":0.1109572,"buildSeconds":4.04,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175423-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_GeneralizedExtremeValue_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-08_23_54_28.928.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175423-179-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_GeneralizedExtremeValue_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Distribution-fitting dependency group, serial exact method; earlier covariance passes rerun after constraint review."} +{"runId":"20260908-175429-909-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_GeneralizedPareto_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.GeneralizedPareto_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-08T23:54:29.9100564+00:00","wallSeconds":5.3499955,"testSeconds":0.0728223,"buildSeconds":3.61,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175429-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_GeneralizedPareto_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-08_23_54_35.024.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175429-909-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_GeneralizedPareto_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Distribution-fitting dependency group, serial exact method; earlier covariance passes rerun after constraint review."} +{"runId":"20260908-175436-209-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Gumbel_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Gumbel_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-08T23:54:36.2092245+00:00","wallSeconds":5.5309776,"testSeconds":0.0572744,"buildSeconds":3.74,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175436-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Gumbel_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-08_23_54_41.425.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175436-209-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Gumbel_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Distribution-fitting dependency group, serial exact method; earlier covariance passes rerun after constraint review."} +{"runId":"20260908-175442-394-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_KappaFour_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.KappaFour_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-08T23:54:42.3949801+00:00","wallSeconds":5.90794,"testSeconds":0.3398164,"buildSeconds":3.91,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175442-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_KappaFour_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-08_23_54_48.070.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175442-394-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_KappaFour_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Distribution-fitting dependency group, serial exact method; earlier covariance passes rerun after constraint review."} +{"runId":"20260908-175448-964-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LnNormal_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.LnNormal_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-08T23:54:48.9647928+00:00","wallSeconds":5.6434464,"testSeconds":0.0618249,"buildSeconds":3.72,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175449-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LnNormal_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-08_23_54_54.368.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175448-964-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LnNormal_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Distribution-fitting dependency group, serial exact method; earlier covariance passes rerun after constraint review."} +{"runId":"20260908-175455-372-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Logistic_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Logistic_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-08T23:54:55.3722927+00:00","wallSeconds":6.321914,"testSeconds":0.0618429,"buildSeconds":4.2,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175455-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Logistic_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-08_23_55_01.388.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175455-372-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Logistic_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Distribution-fitting dependency group, serial exact method; earlier covariance passes rerun after constraint review."} +{"runId":"20260908-175502-557-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogNormal_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.LogNormal_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-08T23:55:02.5575540+00:00","wallSeconds":6.5197469,"testSeconds":0.0774743,"buildSeconds":4.23,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175502-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogNormal_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-08_23_55_08.832.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175502-557-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogNormal_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Distribution-fitting dependency group, serial exact method; earlier covariance passes rerun after constraint review."} +{"runId":"20260908-175509-901-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogPearsonTypeIII_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.LogPearsonTypeIII_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-08T23:55:09.9015084+00:00","wallSeconds":5.6726206,"testSeconds":0.0962713,"buildSeconds":3.9,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175509-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogPearsonTypeIII_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-08_23_55_15.340.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175509-901-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogPearsonTypeIII_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Distribution-fitting dependency group, serial exact method; earlier covariance passes rerun after constraint review."} +{"runId":"20260908-175516-298-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Normal_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Normal_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-08T23:55:16.2986852+00:00","wallSeconds":5.6898841,"testSeconds":0.0587154,"buildSeconds":3.89,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175516-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Normal_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-08_23_55_21.765.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175516-298-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Normal_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Distribution-fitting dependency group, serial exact method; earlier covariance passes rerun after constraint review."} +{"runId":"20260908-175522-654-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_PearsonTypeIII_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.PearsonTypeIII_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-08T23:55:22.6541244+00:00","wallSeconds":5.6115837,"testSeconds":0.1045736,"buildSeconds":3.78,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175522-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_PearsonTypeIII_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-08_23_55_28.036.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175522-654-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_PearsonTypeIII_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Distribution-fitting dependency group, serial exact method; earlier covariance passes rerun after constraint review."} +{"runId":"20260908-175528-985-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Weibull_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Weibull_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-08T23:55:28.9851063+00:00","wallSeconds":5.4857006,"testSeconds":0.0573422,"buildSeconds":3.7,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175529-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Weibull_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-08_23_55_34.224.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175528-985-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Weibull_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Distribution-fitting dependency group, serial exact method; earlier covariance passes rerun after constraint review."} +{"runId":"20260908-175535-201-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_CriteriaRankingWeightsAndConfiguredOrder_MatchIndependentOracle","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisCriteriaVerificationTests.CriteriaRankingWeightsAndConfiguredOrder_MatchIndependentOracle","phase":"stabilized-pass","startedUtc":"2026-09-08T23:55:35.2015665+00:00","wallSeconds":5.6246992,"testSeconds":0.0872041,"buildSeconds":3.85,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175535-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_CriteriaRankingWeightsAndConfiguredOrder_MatchIndependentOracle\\haden_HADEN_2026-09-08_23_55_40.595.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175535-201-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_CriteriaRankingWeightsAndConfiguredOrder_MatchIndependentOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["Gumbel","Logistic","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Distribution-fitting dependency group, serial exact method; earlier covariance passes rerun after constraint review."} +{"runId":"20260908-175541-488-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_FittedParameters_MatchIndependentOracle","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisCriteriaVerificationTests.FittedParameters_MatchIndependentOracle","phase":"stabilized-pass","startedUtc":"2026-09-08T23:55:41.4883370+00:00","wallSeconds":5.4082784,"testSeconds":0.0805801,"buildSeconds":3.54,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175541-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_FittedParameters_MatchIndependentOracle\\haden_HADEN_2026-09-08_23_55_46.574.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175541-488-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_FittedParameters_MatchIndependentOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["Gumbel","Logistic","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Distribution-fitting dependency group, serial exact method; earlier covariance passes rerun after constraint review."} +{"runId":"20260908-175547-605-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Exponential_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Exponential_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-08T23:55:47.6056048+00:00","wallSeconds":25.6159521,"testSeconds":20.3267947,"buildSeconds":3.66,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175547-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Exponential_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-08_23_56_12.989.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175547-605-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Exponential_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Distribution-fitting dependency group, serial exact method; earlier covariance passes rerun after constraint review."} +{"runId":"20260908-175613-978-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gamma_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Gamma_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-08T23:56:13.9781078+00:00","wallSeconds":7.3178143,"testSeconds":1.8843449,"buildSeconds":3.75,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175614-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gamma_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-08_23_56_21.047.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175613-978-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gamma_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Distribution-fitting dependency group, serial exact method; earlier covariance passes rerun after constraint review."} +{"runId":"20260908-175622-037-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedExtremeValue_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.GeneralizedExtremeValue_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-08T23:56:22.0378407+00:00","wallSeconds":7.5760483,"testSeconds":1.8801448,"buildSeconds":3.66,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175622-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedExtremeValue_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-08_23_56_29.091.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175622-037-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedExtremeValue_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Distribution-fitting dependency group, serial exact method; earlier covariance passes rerun after constraint review."} +{"runId":"20260908-175631-759-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedLogistic_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.GeneralizedLogistic_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-08T23:56:31.7593168+00:00","wallSeconds":13.0415048,"testSeconds":5.5754394,"buildSeconds":4.71,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175631-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedLogistic_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-08_23_56_44.541.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175631-759-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedLogistic_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Distribution-fitting dependency group, serial exact method; earlier covariance passes rerun after constraint review."} +{"runId":"20260908-175646-344-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedNormal_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.GeneralizedNormal_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-08T23:56:46.3449952+00:00","wallSeconds":10.9606448,"testSeconds":2.3345036,"buildSeconds":4.92,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175646-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedNormal_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-08_23_56_57.074.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175646-344-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedNormal_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Distribution-fitting dependency group, serial exact method; earlier covariance passes rerun after constraint review."} +{"runId":"20260908-175658-005-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedPareto_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.GeneralizedPareto_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-08T23:56:58.0054004+00:00","wallSeconds":28.8654768,"testSeconds":23.0952369,"buildSeconds":4.06,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175658-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedPareto_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-08_23_57_26.603.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175658-005-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedPareto_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Distribution-fitting dependency group, serial exact method; earlier covariance passes rerun after constraint review."} +{"runId":"20260908-175727-772-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gumbel_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Gumbel_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-08T23:57:27.7729283+00:00","wallSeconds":7.0155284,"testSeconds":1.5210037,"buildSeconds":3.77,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-175727-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gumbel_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-08_23_57_34.558.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-175727-772-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gumbel_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"E89A8F398BD0F1FB2447E067572778D0B9B7E1AF44785E6321207A720F391B2E"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"8276FFC5915EC4495A198B6B0B434D5DAB79642E27AEB264D461666651439B5A"},"numericsAssemblySha256":"C3D47E28AFA938B024CB683C2094877F266CC65ABCAC9255FB12649119C594DE","bestFitAssemblySha256":"75696E47D5CD0951F2FE53B80D8CEE2BFB3876F8F862EF8074426CCEC101B85D","verificationAssemblySha256":"CE60BA153DE7C1B1BDF76A5CEEE7C3FAB6993BE1AEF4C9732F25B9850BE97FA1","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Distribution-fitting dependency group, serial exact method; earlier covariance passes rerun after constraint review."} +{"runId":"20260908-180001-626-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Exponential_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Exponential_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"baseline-runtime","startedUtc":"2026-09-09T00:00:01.6285948+00:00","wallSeconds":39.2808266,"testSeconds":31.5207781,"buildSeconds":5.62,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260908-180001-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Exponential_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_00_40.675.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-180001-626-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Exponential_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"d80bfa8621c48a78cf4ebad7f01326841fac37aa","sourceSha256":"95A9BB4F8927EF0934A13A39B566C1FA30BDB42B2F95AC965746DD122DB39BA0"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"D07BA0CC2820C78B1A275C995AA2EA481C15A370AAA96C55A5B7B1B1F4793BCA"},"numericsAssemblySha256":"3E82F983E4795FD50104CBF0F950A7513BDF94540C3397401A8EC5BBF1A6C60F","bestFitAssemblySha256":"67A7E4809110D816C06ED83E4F9068F6E6FA78E490A5486FCEF62A56D7019DC0","verificationAssemblySha256":"542282B3090D914193685DF647EB18960BC31D061A17CACA7E63A2AA92CC9E46","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Compare unexpectedly long but passing default-list fitting against equivalent pre-integration sources."} +{"runId":"20260908-180041-846-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedPareto_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.GeneralizedPareto_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"baseline-runtime","startedUtc":"2026-09-09T00:00:41.8463955+00:00","wallSeconds":39.0897913,"testSeconds":33.4581912,"buildSeconds":3.65,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260908-180041-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedPareto_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_01_20.639.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-180041-846-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedPareto_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"d80bfa8621c48a78cf4ebad7f01326841fac37aa","sourceSha256":"95A9BB4F8927EF0934A13A39B566C1FA30BDB42B2F95AC965746DD122DB39BA0"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"D07BA0CC2820C78B1A275C995AA2EA481C15A370AAA96C55A5B7B1B1F4793BCA"},"numericsAssemblySha256":"3E82F983E4795FD50104CBF0F950A7513BDF94540C3397401A8EC5BBF1A6C60F","bestFitAssemblySha256":"67A7E4809110D816C06ED83E4F9068F6E6FA78E490A5486FCEF62A56D7019DC0","verificationAssemblySha256":"542282B3090D914193685DF647EB18960BC31D061A17CACA7E63A2AA92CC9E46","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Compare unexpectedly long but passing default-list fitting against equivalent pre-integration sources."} +{"runId":"20260908-180246-004-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample3","phase":"baseline-runtime","startedUtc":"2026-09-09T00:02:46.0059788+00:00","wallSeconds":10.7854245,"testSeconds":5.1661421,"buildSeconds":3.61,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260908-180246-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3\\haden_HADEN_2026-09-09_00_02_56.469.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-180246-004-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"d80bfa8621c48a78cf4ebad7f01326841fac37aa","sourceSha256":"95A9BB4F8927EF0934A13A39B566C1FA30BDB42B2F95AC965746DD122DB39BA0"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"D07BA0CC2820C78B1A275C995AA2EA481C15A370AAA96C55A5B7B1B1F4793BCA"},"numericsAssemblySha256":"3E82F983E4795FD50104CBF0F950A7513BDF94540C3397401A8EC5BBF1A6C60F","bestFitAssemblySha256":"67A7E4809110D816C06ED83E4F9068F6E6FA78E490A5486FCEF62A56D7019DC0","verificationAssemblySha256":"542282B3090D914193685DF647EB18960BC31D061A17CACA7E63A2AA92CC9E46","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Equivalent baseline repeat 1, no concurrent builds or measurements; before measured redundant-work repair."} +{"runId":"20260908-180257-916-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample3","phase":"baseline-runtime","startedUtc":"2026-09-09T00:02:57.9167822+00:00","wallSeconds":10.1082064,"testSeconds":4.4555848,"buildSeconds":3.78,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260908-180257-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3\\haden_HADEN_2026-09-09_00_03_07.763.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-180257-916-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"d80bfa8621c48a78cf4ebad7f01326841fac37aa","sourceSha256":"95A9BB4F8927EF0934A13A39B566C1FA30BDB42B2F95AC965746DD122DB39BA0"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"D07BA0CC2820C78B1A275C995AA2EA481C15A370AAA96C55A5B7B1B1F4793BCA"},"numericsAssemblySha256":"3E82F983E4795FD50104CBF0F950A7513BDF94540C3397401A8EC5BBF1A6C60F","bestFitAssemblySha256":"67A7E4809110D816C06ED83E4F9068F6E6FA78E490A5486FCEF62A56D7019DC0","verificationAssemblySha256":"542282B3090D914193685DF647EB18960BC31D061A17CACA7E63A2AA92CC9E46","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Equivalent baseline repeat 2, no concurrent builds or measurements; before measured redundant-work repair."} +{"runId":"20260908-180657-891-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample3","phase":"performance-repair","startedUtc":"2026-09-09T00:06:57.8936005+00:00","wallSeconds":14.2917329,"testSeconds":5.0904803,"buildSeconds":6.57,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-180658-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3\\haden_HADEN_2026-09-09_00_07_11.925.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-180657-891-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Repaired repeat 1 after immutable table and duplicate solve removal; compare exact test execution separately from build."} +{"runId":"20260908-180713-397-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample3","phase":"performance-repair","startedUtc":"2026-09-09T00:07:13.3978580+00:00","wallSeconds":10.1818355,"testSeconds":4.438097,"buildSeconds":3.8,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-180713-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3\\haden_HADEN_2026-09-09_00_07_23.320.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-180713-397-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Repaired repeat 2 after immutable table and duplicate solve removal; compare exact test execution separately from build."} +{"runId":"20260908-181542-336-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:15:42.3385265+00:00","wallSeconds":10.0887972,"testSeconds":2.1411821,"buildSeconds":5.84,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-181542-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_15_52.145.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-181542-336-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-181553-497-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:15:53.4972347+00:00","wallSeconds":7.7259402,"testSeconds":1.779716,"buildSeconds":4.06,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-181553-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_16_00.965.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-181553-497-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-181602-064-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:16:02.0648238+00:00","wallSeconds":7.5030729,"testSeconds":1.6513396,"buildSeconds":4.05,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-181602-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_16_09.303.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-181602-064-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-181610-581-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample4","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample4","phase":"stabilized-pass","startedUtc":"2026-09-09T00:16:10.5813493+00:00","wallSeconds":11.5817428,"testSeconds":5.6389384,"buildSeconds":4.13,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-181610-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample4\\haden_HADEN_2026-09-09_00_16_21.897.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-181610-581-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample4.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-181622-945-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample5","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample5","phase":"stabilized-pass","startedUtc":"2026-09-09T00:16:22.9454600+00:00","wallSeconds":11.6178658,"testSeconds":5.7697062,"buildSeconds":3.96,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-181623-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample5\\haden_HADEN_2026-09-09_00_16_34.289.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-181622-945-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample5.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-181635-395-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6a","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample6a","phase":"stabilized-pass","startedUtc":"2026-09-09T00:16:35.3960205+00:00","wallSeconds":9.7515234,"testSeconds":3.419867,"buildSeconds":4.38,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-181635-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6a\\haden_HADEN_2026-09-09_00_16_44.841.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-181635-395-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6a.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-181646-205-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6b","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample6b","phase":"stabilized-pass","startedUtc":"2026-09-09T00:16:46.2055034+00:00","wallSeconds":9.2257797,"testSeconds":3.0154925,"buildSeconds":4.31,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-181646-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6b\\haden_HADEN_2026-09-09_00_16_55.162.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-181646-205-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6b.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-181656-170-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_CriteriaRankingWeightsAndConfiguredOrder_MatchIndependentOracle","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisCriteriaVerificationTests.CriteriaRankingWeightsAndConfiguredOrder_MatchIndependentOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T00:16:56.1710501+00:00","wallSeconds":5.8185726,"testSeconds":0.0954508,"buildSeconds":3.87,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-181656-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_CriteriaRankingWeightsAndConfiguredOrder_MatchIndependentOracle\\haden_HADEN_2026-09-09_00_17_01.720.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-181656-170-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_CriteriaRankingWeightsAndConfiguredOrder_MatchIndependentOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Gumbel","Logistic","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-181702-786-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_FittedParameters_MatchIndependentOracle","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisCriteriaVerificationTests.FittedParameters_MatchIndependentOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T00:17:02.7868469+00:00","wallSeconds":5.9566689,"testSeconds":0.0808579,"buildSeconds":4.09,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-181702-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_FittedParameters_MatchIndependentOracle\\haden_HADEN_2026-09-09_00_17_08.504.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-181702-786-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_FittedParameters_MatchIndependentOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Gumbel","Logistic","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-181709-505-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Exponential_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Exponential_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:17:09.5060983+00:00","wallSeconds":28.0503086,"testSeconds":22.3847047,"buildSeconds":3.86,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-181709-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Exponential_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_17_37.297.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-181709-505-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Exponential_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-181738-358-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gamma_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Gamma_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:17:38.3589664+00:00","wallSeconds":7.3275331,"testSeconds":1.8319586,"buildSeconds":3.76,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-181738-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gamma_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_17_45.436.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-181738-358-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gamma_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-181746-494-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedExtremeValue_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.GeneralizedExtremeValue_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:17:46.4948302+00:00","wallSeconds":8.446541,"testSeconds":1.7335204,"buildSeconds":4.83,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-181746-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedExtremeValue_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_17_54.691.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-181746-494-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedExtremeValue_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-181755-717-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedLogistic_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.GeneralizedLogistic_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:17:55.7179363+00:00","wallSeconds":11.9497539,"testSeconds":6.0842874,"buildSeconds":3.94,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-181755-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedLogistic_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_18_07.389.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-181755-717-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedLogistic_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-181808-604-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedNormal_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.GeneralizedNormal_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:18:08.6041014+00:00","wallSeconds":8.5583779,"testSeconds":2.4559715,"buildSeconds":4.08,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-181808-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedNormal_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_18_16.889.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-181808-604-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedNormal_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-181818-009-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedPareto_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.GeneralizedPareto_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:18:18.0093096+00:00","wallSeconds":25.541417,"testSeconds":19.7201727,"buildSeconds":4.05,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-181818-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedPareto_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_18_43.312.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-181818-009-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedPareto_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-181844-312-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gumbel_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Gumbel_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:18:44.3124850+00:00","wallSeconds":7.0559863,"testSeconds":1.6161939,"buildSeconds":3.7,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-181844-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gumbel_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_18_51.097.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-181844-312-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gumbel_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-181852-159-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_KappaFour_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.KappaFour_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:18:52.1600031+00:00","wallSeconds":10.0033574,"testSeconds":4.462917,"buildSeconds":3.8,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-181852-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_KappaFour_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_19_01.920.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-181852-159-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_KappaFour_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-181902-922-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Logistic_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Logistic_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:19:02.9229773+00:00","wallSeconds":7.194418,"testSeconds":1.6691496,"buildSeconds":3.8,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-181902-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Logistic_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_19_09.884.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-181902-922-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Logistic_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-181910-922-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogNormal_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.LogNormal_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:19:10.9221730+00:00","wallSeconds":15.045857,"testSeconds":8.0687027,"buildSeconds":3.83,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-181910-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogNormal_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_19_24.454.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-181910-922-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogNormal_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-181926-718-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Normal_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Normal_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:19:26.7189569+00:00","wallSeconds":7.7602434,"testSeconds":1.9034838,"buildSeconds":4.06,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-181926-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Normal_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_19_34.223.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-181926-718-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Normal_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-181935-283-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Weibull_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Weibull_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:19:35.2834638+00:00","wallSeconds":7.8623855,"testSeconds":1.9840912,"buildSeconds":4.03,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-181935-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Weibull_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_19_42.869.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-181935-283-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Weibull_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-181944-225-RMC_BestFit_Verification_DistributionFitting_KappaFourZeroShapeVerificationTests_ZeroShapeNonzeroHondo_PdfMatchesAnalyticalDerivative","method":"RMC.BestFit.Verification.DistributionFitting.KappaFourZeroShapeVerificationTests.ZeroShapeNonzeroHondo_PdfMatchesAnalyticalDerivative","phase":"stabilized-pass","startedUtc":"2026-09-09T00:19:44.2257387+00:00","wallSeconds":8.2722037,"testSeconds":0.008235,"buildSeconds":5.31,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-181944-RMC_BestFit_Verification_DistributionFitting_KappaFourZeroShapeVerificationTests_ZeroShapeNonzeroHondo_PdfMatchesAnalyticalDerivative\\haden_HADEN_2026-09-09_00_19_52.213.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-181944-225-RMC_BestFit_Verification_DistributionFitting_KappaFourZeroShapeVerificationTests_ZeroShapeNonzeroHondo_PdfMatchesAnalyticalDerivative.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["KappaFour"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-181953-244-RMC_BestFit_Verification_DistributionFitting_KappaFourZeroShapeVerificationTests_ZeroShapeNonzeroHondo_QuantileInvertsCdf","method":"RMC.BestFit.Verification.DistributionFitting.KappaFourZeroShapeVerificationTests.ZeroShapeNonzeroHondo_QuantileInvertsCdf","phase":"stabilized-pass","startedUtc":"2026-09-09T00:19:53.2445987+00:00","wallSeconds":5.5453134,"testSeconds":0.0069788,"buildSeconds":3.83,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-181953-RMC_BestFit_Verification_DistributionFitting_KappaFourZeroShapeVerificationTests_ZeroShapeNonzeroHondo_QuantileInvertsCdf\\haden_HADEN_2026-09-09_00_19_58.538.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-181953-244-RMC_BestFit_Verification_DistributionFitting_KappaFourZeroShapeVerificationTests_ZeroShapeNonzeroHondo_QuantileInvertsCdf.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["KappaFour"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-181959-516-RMC_BestFit_Verification_DistributionFitting_LmomcoDistributionFittingVerificationTests_GeneralizedLogistic_MleAndDistributionFunctionsMatchLmomco","method":"RMC.BestFit.Verification.DistributionFitting.LmomcoDistributionFittingVerificationTests.GeneralizedLogistic_MleAndDistributionFunctionsMatchLmomco","phase":"stabilized-pass","startedUtc":"2026-09-09T00:19:59.5163943+00:00","wallSeconds":5.7805172,"testSeconds":0.0867661,"buildSeconds":3.94,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-181959-RMC_BestFit_Verification_DistributionFitting_LmomcoDistributionFittingVerificationTests_GeneralizedLogistic_MleAndDistributionFunctionsMatchLmomco\\haden_HADEN_2026-09-09_00_20_05.059.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-181959-516-RMC_BestFit_Verification_DistributionFitting_LmomcoDistributionFittingVerificationTests_GeneralizedLogistic_MleAndDistributionFunctionsMatchLmomco.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["GeneralizedLogistic","GeneralizedNormal","Logistic","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182006-068-RMC_BestFit_Verification_DistributionFitting_LmomcoDistributionFittingVerificationTests_GeneralizedNormal_MleAndDistributionFunctionsMatchLmomco","method":"RMC.BestFit.Verification.DistributionFitting.LmomcoDistributionFittingVerificationTests.GeneralizedNormal_MleAndDistributionFunctionsMatchLmomco","phase":"stabilized-pass","startedUtc":"2026-09-09T00:20:06.0686409+00:00","wallSeconds":5.8010959,"testSeconds":0.0908964,"buildSeconds":3.89,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182006-RMC_BestFit_Verification_DistributionFitting_LmomcoDistributionFittingVerificationTests_GeneralizedNormal_MleAndDistributionFunctionsMatchLmomco\\haden_HADEN_2026-09-09_00_20_11.618.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182006-068-RMC_BestFit_Verification_DistributionFitting_LmomcoDistributionFittingVerificationTests_GeneralizedNormal_MleAndDistributionFunctionsMatchLmomco.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["GeneralizedLogistic","GeneralizedNormal","Logistic","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182012-663-RMC_BestFit_Verification_DistributionFitting_Log10NormalFittingVerificationTests_ClosedFormMle_LikelihoodCdfAndQuantileMatchAnalyticalOracle","method":"RMC.BestFit.Verification.DistributionFitting.Log10NormalFittingVerificationTests.ClosedFormMle_LikelihoodCdfAndQuantileMatchAnalyticalOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T00:20:12.6636280+00:00","wallSeconds":5.8848098,"testSeconds":0.0573351,"buildSeconds":4.05,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182012-RMC_BestFit_Verification_DistributionFitting_Log10NormalFittingVerificationTests_ClosedFormMle_LikelihoodCdfAndQuantileMatchAnalyticalOracle\\haden_HADEN_2026-09-09_00_20_18.295.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182012-663-RMC_BestFit_Verification_DistributionFitting_Log10NormalFittingVerificationTests_ClosedFormMle_LikelihoodCdfAndQuantileMatchAnalyticalOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182019-309-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Exponential_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Exponential_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T00:20:19.3098497+00:00","wallSeconds":5.5854179,"testSeconds":0.0611945,"buildSeconds":3.76,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182019-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Exponential_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_00_20_24.658.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182019-309-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Exponential_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182025-659-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Gamma_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Gamma_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T00:20:25.6599002+00:00","wallSeconds":5.777701,"testSeconds":0.0683309,"buildSeconds":3.84,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182025-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Gamma_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_00_20_31.182.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182025-659-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Gamma_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182032-199-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_GeneralizedExtremeValue_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.GeneralizedExtremeValue_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T00:20:32.1994675+00:00","wallSeconds":5.5864087,"testSeconds":0.0859738,"buildSeconds":3.71,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182032-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_GeneralizedExtremeValue_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_00_20_37.531.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182032-199-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_GeneralizedExtremeValue_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182038-567-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_GeneralizedPareto_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.GeneralizedPareto_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T00:20:38.5673643+00:00","wallSeconds":5.9697261,"testSeconds":0.0902603,"buildSeconds":4.04,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182038-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_GeneralizedPareto_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_00_20_44.236.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182038-567-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_GeneralizedPareto_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182045-331-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Gumbel_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Gumbel_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T00:20:45.3313563+00:00","wallSeconds":6.6973489,"testSeconds":0.0628936,"buildSeconds":4.64,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182045-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Gumbel_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_00_20_51.696.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182045-331-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Gumbel_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182053-139-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_KappaFour_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.KappaFour_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T00:20:53.1394756+00:00","wallSeconds":6.3738117,"testSeconds":0.385864,"buildSeconds":4.06,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182053-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_KappaFour_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_00_20_59.249.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182053-139-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_KappaFour_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182100-394-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LnNormal_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.LnNormal_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T00:21:00.3941965+00:00","wallSeconds":6.6104517,"testSeconds":0.0664928,"buildSeconds":4.56,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182100-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LnNormal_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_00_21_06.749.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182100-394-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LnNormal_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182107-843-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Logistic_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Logistic_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T00:21:07.8431651+00:00","wallSeconds":5.7211509,"testSeconds":0.062692,"buildSeconds":3.87,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182107-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Logistic_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_00_21_13.333.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182107-843-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Logistic_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182114-319-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogNormal_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.LogNormal_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T00:21:14.3198156+00:00","wallSeconds":5.7009361,"testSeconds":0.0610322,"buildSeconds":3.89,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182114-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogNormal_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_00_21_19.754.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182114-319-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogNormal_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182120-804-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogPearsonTypeIII_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.LogPearsonTypeIII_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T00:21:20.8046123+00:00","wallSeconds":5.8127879,"testSeconds":0.0938893,"buildSeconds":3.89,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182120-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogPearsonTypeIII_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_00_21_26.336.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182120-804-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogPearsonTypeIII_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182127-365-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Normal_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Normal_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T00:21:27.3655952+00:00","wallSeconds":5.7331508,"testSeconds":0.0556146,"buildSeconds":3.88,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182127-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Normal_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_00_21_32.839.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182127-365-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Normal_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182133-878-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_PearsonTypeIII_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.PearsonTypeIII_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T00:21:33.8782154+00:00","wallSeconds":5.9091128,"testSeconds":0.0825931,"buildSeconds":4.03,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182133-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_PearsonTypeIII_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_00_21_39.540.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182133-878-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_PearsonTypeIII_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182140-519-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Weibull_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Weibull_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T00:21:40.5193122+00:00","wallSeconds":5.4025192,"testSeconds":0.0539682,"buildSeconds":3.64,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182140-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Weibull_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_00_21_45.658.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182140-519-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Weibull_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182146-694-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Exponential_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_Exponential_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T00:21:46.6947983+00:00","wallSeconds":6.1751556,"testSeconds":0.0526163,"buildSeconds":3.97,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182146-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Exponential_MLE\\haden_HADEN_2026-09-09_00_21_52.505.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182146-694-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Exponential_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182154-013-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GammaDist_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_GammaDist_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T00:21:54.0130879+00:00","wallSeconds":6.345197,"testSeconds":0.060165,"buildSeconds":4.18,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182154-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GammaDist_MLE\\haden_HADEN_2026-09-09_00_22_00.086.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182154-013-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GammaDist_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182201-545-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedExtremeValue_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_GeneralizedExtremeValue_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T00:22:01.5456356+00:00","wallSeconds":6.6370827,"testSeconds":0.0789007,"buildSeconds":4.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182201-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedExtremeValue_MLE\\haden_HADEN_2026-09-09_00_22_07.901.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182201-545-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedExtremeValue_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182209-127-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedLogistic_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_GeneralizedLogistic_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T00:22:09.1273118+00:00","wallSeconds":6.4808353,"testSeconds":0.1038343,"buildSeconds":4.22,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182209-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedLogistic_MLE\\haden_HADEN_2026-09-09_00_22_15.347.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182209-127-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedLogistic_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182216-486-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedNormal_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_GeneralizedNormal_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T00:22:16.4863831+00:00","wallSeconds":6.9242575,"testSeconds":0.1240366,"buildSeconds":4.62,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182216-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedNormal_MLE\\haden_HADEN_2026-09-09_00_22_23.143.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182216-486-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedNormal_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182224-204-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedPareto_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_GeneralizedPareto_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T00:22:24.2040757+00:00","wallSeconds":6.032552,"testSeconds":0.1148987,"buildSeconds":4.06,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182224-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedPareto_MLE\\haden_HADEN_2026-09-09_00_22_29.977.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182224-204-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedPareto_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182231-016-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Gumbel_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_Gumbel_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T00:22:31.0165804+00:00","wallSeconds":5.6900657,"testSeconds":0.0555311,"buildSeconds":3.88,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182231-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Gumbel_MLE\\haden_HADEN_2026-09-09_00_22_36.466.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182231-016-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Gumbel_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182237-482-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Kappa4_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_Kappa4_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T00:22:37.4821109+00:00","wallSeconds":5.8796928,"testSeconds":0.3255913,"buildSeconds":3.87,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182237-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Kappa4_MLE\\haden_HADEN_2026-09-09_00_22_43.126.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182237-482-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Kappa4_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182246-635-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LnNormal_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_LnNormal_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T00:22:46.6360160+00:00","wallSeconds":7.2583347,"testSeconds":0.0571146,"buildSeconds":4.77,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182247-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LnNormal_MLE\\haden_HADEN_2026-09-09_00_22_53.654.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182246-635-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LnNormal_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182254-622-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Logistic_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_Logistic_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T00:22:54.6220944+00:00","wallSeconds":5.6003133,"testSeconds":0.0522492,"buildSeconds":3.91,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182254-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Logistic_MLE\\haden_HADEN_2026-09-09_00_22_59.990.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182254-622-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Logistic_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182300-953-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LogNormal_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_LogNormal_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T00:23:00.9532463+00:00","wallSeconds":5.4535164,"testSeconds":0.0584822,"buildSeconds":3.71,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182301-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LogNormal_MLE\\haden_HADEN_2026-09-09_00_23_06.180.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182300-953-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LogNormal_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182307-148-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LogPearsonTypeIII_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_LogPearsonTypeIII_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T00:23:07.1481034+00:00","wallSeconds":5.6939694,"testSeconds":0.1108614,"buildSeconds":3.89,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182307-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LogPearsonTypeIII_MLE\\haden_HADEN_2026-09-09_00_23_12.606.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182307-148-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LogPearsonTypeIII_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182313-574-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Normal_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_Normal_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T00:23:13.5741059+00:00","wallSeconds":5.4656417,"testSeconds":0.0554998,"buildSeconds":3.67,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182313-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Normal_MLE\\haden_HADEN_2026-09-09_00_23_18.794.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182313-574-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Normal_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182319-757-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_PearsonTypeIII_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_PearsonTypeIII_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T00:23:19.7580439+00:00","wallSeconds":5.548677,"testSeconds":0.0854859,"buildSeconds":3.83,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182319-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_PearsonTypeIII_MLE\\haden_HADEN_2026-09-09_00_23_25.080.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182319-757-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_PearsonTypeIII_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182326-053-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Weibull_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_Weibull_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T00:23:26.0539703+00:00","wallSeconds":6.9379435,"testSeconds":0.0659944,"buildSeconds":3.86,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182326-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Weibull_MLE\\haden_HADEN_2026-09-09_00_23_31.471.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182326-053-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Weibull_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182336-158-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test1_ExactData_1951_2001","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ViglioneEtAlTests.Test1_ExactData_1951_2001","phase":"stabilized-pass","startedUtc":"2026-09-09T00:23:36.1590553+00:00","wallSeconds":10.2722459,"testSeconds":2.7983891,"buildSeconds":5.23,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182336-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test1_ExactData_1951_2001\\haden_HADEN_2026-09-09_00_23_46.176.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182336-158-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test1_ExactData_1951_2001.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["GeneralizedExtremeValue"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182347-141-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test2_ExactData_1951_2005","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ViglioneEtAlTests.Test2_ExactData_1951_2005","phase":"stabilized-pass","startedUtc":"2026-09-09T00:23:47.1415699+00:00","wallSeconds":8.3038594,"testSeconds":2.9734559,"buildSeconds":3.64,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182347-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test2_ExactData_1951_2005\\haden_HADEN_2026-09-09_00_23_55.176.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182347-141-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test2_ExactData_1951_2005.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["GeneralizedExtremeValue"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182358-990-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test3_ExactData_1951_2001_TemporalExpansion","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ViglioneEtAlTests.Test3_ExactData_1951_2001_TemporalExpansion","phase":"stabilized-pass","startedUtc":"2026-09-09T00:23:58.9909762+00:00","wallSeconds":11.0032985,"testSeconds":3.3018819,"buildSeconds":5.04,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182359-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test3_ExactData_1951_2001_TemporalExpansion\\haden_HADEN_2026-09-09_00_24_09.741.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182358-990-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test3_ExactData_1951_2001_TemporalExpansion.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["GeneralizedExtremeValue"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182410-742-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test4_ExactData_1951_2005_TemporalExpansion","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ViglioneEtAlTests.Test4_ExactData_1951_2005_TemporalExpansion","phase":"stabilized-pass","startedUtc":"2026-09-09T00:24:10.7421243+00:00","wallSeconds":9.7143426,"testSeconds":3.3943835,"buildSeconds":4.6,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182410-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test4_ExactData_1951_2005_TemporalExpansion\\haden_HADEN_2026-09-09_00_24_20.213.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182410-742-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test4_ExactData_1951_2005_TemporalExpansion.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["GeneralizedExtremeValue"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182421-183-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test5_ExactData_1951_2001_CausalExpansion","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ViglioneEtAlTests.Test5_ExactData_1951_2001_CausalExpansion","phase":"stabilized-pass","startedUtc":"2026-09-09T00:24:21.1835495+00:00","wallSeconds":8.7705608,"testSeconds":3.0621706,"buildSeconds":4.02,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182421-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test5_ExactData_1951_2001_CausalExpansion\\haden_HADEN_2026-09-09_00_24_29.691.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182421-183-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test5_ExactData_1951_2001_CausalExpansion.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["GeneralizedExtremeValue"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182430-688-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test6_ExactData_1951_2005_CausalExpansion","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ViglioneEtAlTests.Test6_ExactData_1951_2005_CausalExpansion","phase":"stabilized-pass","startedUtc":"2026-09-09T00:24:30.6889258+00:00","wallSeconds":9.5031946,"testSeconds":3.1976833,"buildSeconds":4.52,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182430-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test6_ExactData_1951_2005_CausalExpansion\\haden_HADEN_2026-09-09_00_24_39.909.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182430-688-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test6_ExactData_1951_2005_CausalExpansion.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["GeneralizedExtremeValue"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182440-970-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test7_ExactData_1951_2001_TemporalAndCausalExpansion","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ViglioneEtAlTests.Test7_ExactData_1951_2001_TemporalAndCausalExpansion","phase":"stabilized-pass","startedUtc":"2026-09-09T00:24:40.9700505+00:00","wallSeconds":8.9796599,"testSeconds":3.5697341,"buildSeconds":3.7,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182441-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test7_ExactData_1951_2001_TemporalAndCausalExpansion\\haden_HADEN_2026-09-09_00_24_49.699.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182440-970-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test7_ExactData_1951_2001_TemporalAndCausalExpansion.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["GeneralizedExtremeValue"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182450-662-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test8_ExactData_1951_2005_TemporalAndCausalExpansion","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ViglioneEtAlTests.Test8_ExactData_1951_2005_TemporalAndCausalExpansion","phase":"stabilized-pass","startedUtc":"2026-09-09T00:24:50.6629269+00:00","wallSeconds":9.4653065,"testSeconds":3.8781562,"buildSeconds":3.87,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182450-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test8_ExactData_1951_2005_TemporalAndCausalExpansion\\haden_HADEN_2026-09-09_00_24_59.879.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182450-662-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test8_ExactData_1951_2005_TemporalAndCausalExpansion.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["GeneralizedExtremeValue"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182500-860-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test9_ExactData_1951_2001_CausalExpansion_TheePriors","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ViglioneEtAlTests.Test9_ExactData_1951_2001_CausalExpansion_TheePriors","phase":"stabilized-pass","startedUtc":"2026-09-09T00:25:00.8609013+00:00","wallSeconds":9.2254895,"testSeconds":3.4153991,"buildSeconds":4.0,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182500-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test9_ExactData_1951_2001_CausalExpansion_TheePriors\\haden_HADEN_2026-09-09_00_25_09.751.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182500-860-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test9_ExactData_1951_2001_CausalExpansion_TheePriors.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["GeneralizedExtremeValue"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182514-813-RMC_BestFit_Verification_ModelEstimation_BayesianAnalysisRecoveryTests_Test_Estimate_PosteriorMean_NearTrueValue","method":"RMC.BestFit.Verification.ModelEstimation.BayesianAnalysisRecoveryTests.Test_Estimate_PosteriorMean_NearTrueValue","phase":"stabilized-pass","startedUtc":"2026-09-09T00:25:14.8136134+00:00","wallSeconds":14.742846,"testSeconds":8.8028572,"buildSeconds":4.23,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182514-RMC_BestFit_Verification_ModelEstimation_BayesianAnalysisRecoveryTests_Test_Estimate_PosteriorMean_NearTrueValue\\haden_HADEN_2026-09-09_00_25_29.305.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182514-813-RMC_BestFit_Verification_ModelEstimation_BayesianAnalysisRecoveryTests_Test_Estimate_PosteriorMean_NearTrueValue.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182530-321-RMC_BestFit_Verification_ModelEstimation_BayesianAnalysisRecoveryTests_Test_N1000_DefaultPosteriorIntervals_ContainGeneratingParameters","method":"RMC.BestFit.Verification.ModelEstimation.BayesianAnalysisRecoveryTests.Test_N1000_DefaultPosteriorIntervals_ContainGeneratingParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T00:25:30.3218639+00:00","wallSeconds":14.1374382,"testSeconds":8.4474866,"buildSeconds":3.92,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182530-RMC_BestFit_Verification_ModelEstimation_BayesianAnalysisRecoveryTests_Test_N1000_DefaultPosteriorIntervals_ContainGeneratingParameters\\haden_HADEN_2026-09-09_00_25_44.201.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182530-321-RMC_BestFit_Verification_ModelEstimation_BayesianAnalysisRecoveryTests_Test_N1000_DefaultPosteriorIntervals_ContainGeneratingParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-182545-929-RMC_BestFit_Verification_ModelEstimation_BayesianConjugateOracleVerificationTests_InformativePrior_NormalNormalConjugatePosterior_MatchesClosedForm","method":"RMC.BestFit.Verification.ModelEstimation.BayesianConjugateOracleVerificationTests.InformativePrior_NormalNormalConjugatePosterior_MatchesClosedForm","phase":"stabilized-pass","startedUtc":"2026-09-09T00:25:45.9291047+00:00","wallSeconds":9.6091076,"testSeconds":1.8417378,"buildSeconds":4.41,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-182546-RMC_BestFit_Verification_ModelEstimation_BayesianConjugateOracleVerificationTests_InformativePrior_NormalNormalConjugatePosterior_MatchesClosedForm\\haden_HADEN_2026-09-09_00_25_53.693.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-182545-929-RMC_BestFit_Verification_ModelEstimation_BayesianConjugateOracleVerificationTests_InformativePrior_NormalNormalConjugatePosterior_MatchesClosedForm.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"2624C213BFFAEAC60541F997D9B24E3574C30587C5FAC74CEF38559844D3C911"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"8E79B74C42747CBDCAEBD335152249DBE974DEA12C597911E92A2AC35EF63264","bestFitAssemblySha256":"73BFD19C998F3CFB1E2D668623DE44CC1F1F921E55503E73E9D220EF40FD2C3D","verificationAssemblySha256":"BAB9ACAADF4C55F2A9A1384928864E81F0263B1709A727D0E389BFEF8BDB1216","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-183502-845-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:35:02.8485837+00:00","wallSeconds":10.870267,"testSeconds":2.6618706,"buildSeconds":5.67,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-183502-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_35_13.416.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-183502-845-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-183514-809-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:35:14.8091539+00:00","wallSeconds":7.2926454,"testSeconds":1.6291905,"buildSeconds":3.77,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-183514-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_35_21.870.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-183514-809-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-183522-899-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:35:22.8992102+00:00","wallSeconds":7.7460289,"testSeconds":1.768541,"buildSeconds":3.87,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-183522-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_35_30.393.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-183522-899-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-183531-513-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample3","phase":"stabilized-pass","startedUtc":"2026-09-09T00:35:31.5134798+00:00","wallSeconds":11.3001783,"testSeconds":5.3219912,"buildSeconds":3.94,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-183531-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3\\haden_HADEN_2026-09-09_00_35_42.570.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-183531-513-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-183543-597-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample4","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample4","phase":"stabilized-pass","startedUtc":"2026-09-09T00:35:43.5974122+00:00","wallSeconds":11.6916512,"testSeconds":5.5881763,"buildSeconds":4.09,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-183543-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample4\\haden_HADEN_2026-09-09_00_35_54.904.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-183543-597-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample4.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-183556-019-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample5","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample5","phase":"stabilized-pass","startedUtc":"2026-09-09T00:35:56.0198177+00:00","wallSeconds":11.5291456,"testSeconds":5.6079209,"buildSeconds":3.78,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-183556-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample5\\haden_HADEN_2026-09-09_00_36_07.280.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-183556-019-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample5.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-183608-320-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6a","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample6a","phase":"stabilized-pass","startedUtc":"2026-09-09T00:36:08.3204872+00:00","wallSeconds":9.6311439,"testSeconds":3.2649536,"buildSeconds":3.93,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-183608-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6a\\haden_HADEN_2026-09-09_00_36_17.670.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-183608-320-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6a.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-183618-695-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6b","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample6b","phase":"stabilized-pass","startedUtc":"2026-09-09T00:36:18.6959505+00:00","wallSeconds":8.5811337,"testSeconds":2.9809453,"buildSeconds":3.87,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-183618-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6b\\haden_HADEN_2026-09-09_00_36_27.017.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-183618-695-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6b.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-183627-981-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_CriteriaRankingWeightsAndConfiguredOrder_MatchIndependentOracle","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisCriteriaVerificationTests.CriteriaRankingWeightsAndConfiguredOrder_MatchIndependentOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T00:36:27.9818875+00:00","wallSeconds":6.312998,"testSeconds":0.1086116,"buildSeconds":4.26,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-183628-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_CriteriaRankingWeightsAndConfiguredOrder_MatchIndependentOracle\\haden_HADEN_2026-09-09_00_36_33.930.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-183627-981-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_CriteriaRankingWeightsAndConfiguredOrder_MatchIndependentOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["Gumbel","Logistic","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-183635-118-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_FittedParameters_MatchIndependentOracle","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisCriteriaVerificationTests.FittedParameters_MatchIndependentOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T00:36:35.1187423+00:00","wallSeconds":5.6978203,"testSeconds":0.0867878,"buildSeconds":3.78,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-183635-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_FittedParameters_MatchIndependentOracle\\haden_HADEN_2026-09-09_00_36_40.530.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-183635-118-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_FittedParameters_MatchIndependentOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["Gumbel","Logistic","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-183641-547-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Exponential_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Exponential_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:36:41.5478663+00:00","wallSeconds":26.9932441,"testSeconds":21.2651544,"buildSeconds":3.72,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-183641-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Exponential_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_37_08.320.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-183641-547-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Exponential_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-183709-262-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gamma_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Gamma_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:37:09.2629815+00:00","wallSeconds":7.354998,"testSeconds":1.7204614,"buildSeconds":3.99,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-183709-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gamma_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_37_16.389.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-183709-262-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gamma_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-183717-324-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedExtremeValue_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.GeneralizedExtremeValue_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:37:17.3244123+00:00","wallSeconds":7.7165247,"testSeconds":1.8210583,"buildSeconds":4.17,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-183717-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedExtremeValue_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_37_24.799.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-183717-324-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedExtremeValue_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-183725-773-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedLogistic_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.GeneralizedLogistic_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:37:25.7736358+00:00","wallSeconds":11.3865333,"testSeconds":5.4763174,"buildSeconds":3.94,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-183725-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedLogistic_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_37_36.924.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-183725-773-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedLogistic_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-183737-839-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedNormal_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.GeneralizedNormal_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:37:37.8397219+00:00","wallSeconds":8.2431919,"testSeconds":2.3105754,"buildSeconds":4.08,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-183737-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedNormal_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_37_45.844.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-183737-839-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedNormal_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-183746-812-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedPareto_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.GeneralizedPareto_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:37:46.8125749+00:00","wallSeconds":28.2619332,"testSeconds":22.4085894,"buildSeconds":4.17,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-183746-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedPareto_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_38_14.834.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-183746-812-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedPareto_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-183815-817-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gumbel_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Gumbel_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:38:15.8173541+00:00","wallSeconds":7.9013241,"testSeconds":1.7021322,"buildSeconds":4.09,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-183815-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gumbel_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_38_23.488.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-183815-817-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gumbel_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-183824-486-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_KappaFour_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.KappaFour_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:38:24.4868529+00:00","wallSeconds":9.9764227,"testSeconds":4.3046356,"buildSeconds":3.92,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-183824-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_KappaFour_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_38_34.225.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-183824-486-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_KappaFour_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-183835-167-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Logistic_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Logistic_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:38:35.1678898+00:00","wallSeconds":7.1834469,"testSeconds":1.6003585,"buildSeconds":3.97,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-183835-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Logistic_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_38_42.119.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-183835-167-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Logistic_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-183843-123-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogNormal_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.LogNormal_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:38:43.1237051+00:00","wallSeconds":12.2694402,"testSeconds":6.8422285,"buildSeconds":3.7,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-183843-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogNormal_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_38_55.151.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-183843-123-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogNormal_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-183856-185-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Normal_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Normal_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:38:56.1854180+00:00","wallSeconds":8.0107864,"testSeconds":2.1810412,"buildSeconds":4.11,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-183856-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Normal_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_39_03.935.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-183856-185-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Normal_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-183905-036-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Weibull_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Weibull_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:39:05.0365055+00:00","wallSeconds":8.1806144,"testSeconds":1.9112356,"buildSeconds":4.52,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-183905-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Weibull_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_39_12.968.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-183905-036-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Weibull_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-183913-937-RMC_BestFit_Verification_DistributionFitting_KappaFourZeroShapeVerificationTests_ZeroShapeNonzeroHondo_PdfMatchesAnalyticalDerivative","method":"RMC.BestFit.Verification.DistributionFitting.KappaFourZeroShapeVerificationTests.ZeroShapeNonzeroHondo_PdfMatchesAnalyticalDerivative","phase":"stabilized-pass","startedUtc":"2026-09-09T00:39:13.9379911+00:00","wallSeconds":5.595614,"testSeconds":0.0082592,"buildSeconds":3.78,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-183914-RMC_BestFit_Verification_DistributionFitting_KappaFourZeroShapeVerificationTests_ZeroShapeNonzeroHondo_PdfMatchesAnalyticalDerivative\\haden_HADEN_2026-09-09_00_39_19.251.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-183913-937-RMC_BestFit_Verification_DistributionFitting_KappaFourZeroShapeVerificationTests_ZeroShapeNonzeroHondo_PdfMatchesAnalyticalDerivative.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["KappaFour"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-183920-450-RMC_BestFit_Verification_DistributionFitting_KappaFourZeroShapeVerificationTests_ZeroShapeNonzeroHondo_QuantileInvertsCdf","method":"RMC.BestFit.Verification.DistributionFitting.KappaFourZeroShapeVerificationTests.ZeroShapeNonzeroHondo_QuantileInvertsCdf","phase":"stabilized-pass","startedUtc":"2026-09-09T00:39:20.4500839+00:00","wallSeconds":5.533886,"testSeconds":0.0072018,"buildSeconds":3.8,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-183920-RMC_BestFit_Verification_DistributionFitting_KappaFourZeroShapeVerificationTests_ZeroShapeNonzeroHondo_QuantileInvertsCdf\\haden_HADEN_2026-09-09_00_39_25.747.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-183920-450-RMC_BestFit_Verification_DistributionFitting_KappaFourZeroShapeVerificationTests_ZeroShapeNonzeroHondo_QuantileInvertsCdf.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["KappaFour"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-183926-726-RMC_BestFit_Verification_DistributionFitting_LmomcoDistributionFittingVerificationTests_GeneralizedLogistic_MleAndDistributionFunctionsMatchLmomco","method":"RMC.BestFit.Verification.DistributionFitting.LmomcoDistributionFittingVerificationTests.GeneralizedLogistic_MleAndDistributionFunctionsMatchLmomco","phase":"stabilized-pass","startedUtc":"2026-09-09T00:39:26.7267476+00:00","wallSeconds":5.7065553,"testSeconds":0.0909091,"buildSeconds":3.95,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-183926-RMC_BestFit_Verification_DistributionFitting_LmomcoDistributionFittingVerificationTests_GeneralizedLogistic_MleAndDistributionFunctionsMatchLmomco\\haden_HADEN_2026-09-09_00_39_32.200.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-183926-726-RMC_BestFit_Verification_DistributionFitting_LmomcoDistributionFittingVerificationTests_GeneralizedLogistic_MleAndDistributionFunctionsMatchLmomco.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["GeneralizedLogistic","GeneralizedNormal","Logistic","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184012-431-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogPearsonTypeIII_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.LogPearsonTypeIII_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T00:40:12.4339658+00:00","wallSeconds":6.7282162,"testSeconds":0.1941662,"buildSeconds":4.08,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184012-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogPearsonTypeIII_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_00_40_18.892.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184012-431-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogPearsonTypeIII_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184020-138-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_PearsonTypeIII_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.PearsonTypeIII_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T00:40:20.1389057+00:00","wallSeconds":6.1232892,"testSeconds":0.116036,"buildSeconds":4.16,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184020-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_PearsonTypeIII_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_00_40_26.019.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184020-138-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_PearsonTypeIII_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184027-016-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LogPearsonTypeIII_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_LogPearsonTypeIII_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T00:40:27.0162713+00:00","wallSeconds":6.1992047,"testSeconds":0.1205044,"buildSeconds":4.22,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184027-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LogPearsonTypeIII_MLE\\haden_HADEN_2026-09-09_00_40_32.966.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184027-016-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LogPearsonTypeIII_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184034-105-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_PearsonTypeIII_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_PearsonTypeIII_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T00:40:34.1056352+00:00","wallSeconds":5.9889659,"testSeconds":0.1546439,"buildSeconds":3.93,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184034-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_PearsonTypeIII_MLE\\haden_HADEN_2026-09-09_00_40_39.844.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184034-105-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_PearsonTypeIII_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184041-027-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LogPearsonTypeIII_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_LogPearsonTypeIII_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T00:40:41.0272336+00:00","wallSeconds":6.8828885,"testSeconds":1.0168645,"buildSeconds":3.95,"result":"Failed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184041-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LogPearsonTypeIII_RecoversTrueParameters\\haden_HADEN_2026-09-09_00_40_47.666.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184041-027-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LogPearsonTypeIII_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"0AECEE3C1A63D859C59A83F0988D7FF74ED556A7B1DBCC190965E7BA8F7A8CFB"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"33222058D75C9BFCD1CF5930C03B7F7D356D4C8E32CBD9848CF9A8263D4A3505","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":"Assert.IsTrue failed. OneOverBeta estimate 16.406235723514122, parent 13.333333333333334, and observed-information standard error 0.0070167065487059498 give standardized parent error 437.94084430501169, exceeding 1.96.","note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184120-828-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LogPearsonTypeIII_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_LogPearsonTypeIII_RecoversTrueParameters","phase":"baseline","startedUtc":"2026-09-09T00:41:20.8305313+00:00","wallSeconds":6.6839984,"testSeconds":0.9264202,"buildSeconds":3.84,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260908-184120-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LogPearsonTypeIII_RecoversTrueParameters\\haden_HADEN_2026-09-09_00_41_27.282.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184120-828-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LogPearsonTypeIII_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"d80bfa8621c48a78cf4ebad7f01326841fac37aa","sourceSha256":"95A9BB4F8927EF0934A13A39B566C1FA30BDB42B2F95AC965746DD122DB39BA0"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"17C77A3DDAE1EF7B39BA3945DB5F6FB61F93F3D1D57FD48AF96248C9A6054F43"},"numericsAssemblySha256":"3E82F983E4795FD50104CBF0F950A7513BDF94540C3397401A8EC5BBF1A6C60F","bestFitAssemblySha256":"67A7E4809110D816C06ED83E4F9068F6E6FA78E490A5486FCEF62A56D7019DC0","verificationAssemblySha256":"542282B3090D914193685DF647EB18960BC31D061A17CACA7E63A2AA92CC9E46","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"New MLE integration failure: rate coordinate interpreted from public moment covariance; unchanged baseline comparison before full covariance-coordinate restoration."} +{"runId":"20260908-184205-365-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LogPearsonTypeIII_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_LogPearsonTypeIII_RecoversTrueParameters","phase":"numerics-isolation","startedUtc":"2026-09-09T00:42:05.3667753+00:00","wallSeconds":11.0179188,"testSeconds":0.9892577,"buildSeconds":7.73,"result":"Failed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260908-184205-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LogPearsonTypeIII_RecoversTrueParameters\\haden_HADEN_2026-09-09_00_42_16.097.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184205-365-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LogPearsonTypeIII_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"17C77A3DDAE1EF7B39BA3945DB5F6FB61F93F3D1D57FD48AF96248C9A6054F43"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"58BCDB1CC2D0645D277A91B71D3F21DE8DFD0DBED29AB48FA35B4D271CAC9EA0","verificationAssemblySha256":"D4CFA1AA6621AC20FB329397C85BECD13A44430215058373B4F951EDAEF3C7CF","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":"Assert.IsTrue failed. OneOverBeta estimate 16.406235723514122, parent 13.333333333333334, and observed-information standard error 0.0070167065487059498 give standardized parent error 437.94084430501169, exceeding 1.96.","note":"Hold BestFit at fbe0989 and substitute repaired Numerics; isolate the remaining MLE covariance-coordinate mismatch from BestFit integration."} +{"runId":"20260908-184501-183-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LogPearsonTypeIII_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_LogPearsonTypeIII_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T00:45:01.1870524+00:00","wallSeconds":7.4226859,"testSeconds":0.9619275,"buildSeconds":4.39,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184501-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LogPearsonTypeIII_RecoversTrueParameters\\haden_HADEN_2026-09-09_00_45_08.358.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184501-183-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LogPearsonTypeIII_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184509-779-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_PearsonTypeIII_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_PearsonTypeIII_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T00:45:09.7792314+00:00","wallSeconds":7.0953732,"testSeconds":0.8411584,"buildSeconds":4.38,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184509-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_PearsonTypeIII_RecoversTrueParameters\\haden_HADEN_2026-09-09_00_45_16.610.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184509-779-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_PearsonTypeIII_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184517-751-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LnNormal_RecoversGeneratingParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_LnNormal_RecoversGeneratingParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T00:45:17.7517616+00:00","wallSeconds":5.8448674,"testSeconds":0.1291916,"buildSeconds":3.75,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184517-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LnNormal_RecoversGeneratingParameters\\haden_HADEN_2026-09-09_00_45_23.320.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184517-751-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LnNormal_RecoversGeneratingParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184612-743-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:46:12.7458770+00:00","wallSeconds":8.6070171,"testSeconds":2.05168,"buildSeconds":4.03,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184612-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_46_21.075.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184612-743-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184622-455-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:46:22.4554241+00:00","wallSeconds":7.2420551,"testSeconds":1.7082767,"buildSeconds":3.77,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184622-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_46_29.450.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184622-455-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184630-471-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T00:46:30.4712731+00:00","wallSeconds":7.1312375,"testSeconds":1.5580673,"buildSeconds":3.9,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184630-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_00_46_37.364.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184630-471-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184638-376-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample3","phase":"stabilized-pass","startedUtc":"2026-09-09T00:46:38.3768321+00:00","wallSeconds":10.2751586,"testSeconds":4.7913381,"buildSeconds":3.72,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184638-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3\\haden_HADEN_2026-09-09_00_46_48.405.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184638-376-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184649-381-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample4","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample4","phase":"stabilized-pass","startedUtc":"2026-09-09T00:46:49.3820128+00:00","wallSeconds":11.0436624,"testSeconds":5.128439,"buildSeconds":4.11,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184649-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample4\\haden_HADEN_2026-09-09_00_47_00.172.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184649-381-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample4.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184701-172-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample5","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample5","phase":"stabilized-pass","startedUtc":"2026-09-09T00:47:01.1723765+00:00","wallSeconds":12.210573,"testSeconds":5.2775288,"buildSeconds":4.67,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184701-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample5\\haden_HADEN_2026-09-09_00_47_13.129.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184701-172-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample5.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184714-130-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6a","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample6a","phase":"stabilized-pass","startedUtc":"2026-09-09T00:47:14.1309189+00:00","wallSeconds":8.734663,"testSeconds":2.9446995,"buildSeconds":3.86,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184714-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6a\\haden_HADEN_2026-09-09_00_47_22.604.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184714-130-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6a.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184723-608-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6b","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample6b","phase":"stabilized-pass","startedUtc":"2026-09-09T00:47:23.6088565+00:00","wallSeconds":8.8387774,"testSeconds":3.050979,"buildSeconds":3.97,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184723-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6b\\haden_HADEN_2026-09-09_00_47_32.183.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184723-608-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6b.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184733-240-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogPearsonTypeIII_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.LogPearsonTypeIII_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T00:47:33.2407772+00:00","wallSeconds":5.6463103,"testSeconds":0.1307712,"buildSeconds":3.78,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184733-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogPearsonTypeIII_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_00_47_38.592.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184733-240-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogPearsonTypeIII_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184739-584-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_PearsonTypeIII_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.PearsonTypeIII_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T00:47:39.5840997+00:00","wallSeconds":5.477593,"testSeconds":0.0819264,"buildSeconds":3.61,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184739-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_PearsonTypeIII_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_00_47_44.819.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184739-584-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_PearsonTypeIII_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184745-774-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LogPearsonTypeIII_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_LogPearsonTypeIII_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T00:47:45.7746169+00:00","wallSeconds":5.7279867,"testSeconds":0.1443591,"buildSeconds":3.72,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184745-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LogPearsonTypeIII_MLE\\haden_HADEN_2026-09-09_00_47_51.225.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184745-774-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LogPearsonTypeIII_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184752-549-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_PearsonTypeIII_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_PearsonTypeIII_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T00:47:52.5495498+00:00","wallSeconds":5.750432,"testSeconds":0.0957337,"buildSeconds":3.93,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184752-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_PearsonTypeIII_MLE\\haden_HADEN_2026-09-09_00_47_58.065.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184752-549-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_PearsonTypeIII_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184759-011-RMC_BestFit_Verification_ModelEstimation_ProfileQRecoveryTests_TrueProfile_LP3Q99_RecoversGeneratingResponse","method":"RMC.BestFit.Verification.ModelEstimation.ProfileQRecoveryTests.TrueProfile_LP3Q99_RecoversGeneratingResponse","phase":"stabilized-pass","startedUtc":"2026-09-09T00:47:59.0120151+00:00","wallSeconds":7.2884554,"testSeconds":1.2354516,"buildSeconds":4.25,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184759-RMC_BestFit_Verification_ModelEstimation_ProfileQRecoveryTests_TrueProfile_LP3Q99_RecoversGeneratingResponse\\haden_HADEN_2026-09-09_00_48_06.072.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184759-011-RMC_BestFit_Verification_ModelEstimation_ProfileQRecoveryTests_TrueProfile_LP3Q99_RecoversGeneratingResponse.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184806-997-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Exponential_Covariance_N100","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.Exponential_Covariance_N100","phase":"stabilized-pass","startedUtc":"2026-09-09T00:48:06.9980622+00:00","wallSeconds":5.710961,"testSeconds":0.0781561,"buildSeconds":3.96,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184807-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Exponential_Covariance_N100\\haden_HADEN_2026-09-09_00_48_12.479.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184806-997-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Exponential_Covariance_N100.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184813-410-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Exponential_Covariance_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.Exponential_Covariance_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T00:48:13.4108568+00:00","wallSeconds":6.0493278,"testSeconds":0.067726,"buildSeconds":3.94,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184813-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Exponential_Covariance_N25\\haden_HADEN_2026-09-09_00_48_19.170.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184813-410-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Exponential_Covariance_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184820-231-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Gamma_Covariance_N100","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.Gamma_Covariance_N100","phase":"stabilized-pass","startedUtc":"2026-09-09T00:48:20.2315351+00:00","wallSeconds":5.9852746,"testSeconds":0.0588661,"buildSeconds":4.2,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184820-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Gamma_Covariance_N100\\haden_HADEN_2026-09-09_00_48_25.966.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184820-231-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Gamma_Covariance_N100.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184826-949-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Gamma_Covariance_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.Gamma_Covariance_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T00:48:26.9499428+00:00","wallSeconds":5.6598966,"testSeconds":0.0712714,"buildSeconds":3.86,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184827-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Gamma_Covariance_N25\\haden_HADEN_2026-09-09_00_48_32.367.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184826-949-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Gamma_Covariance_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184833-362-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogNormal_Covariance_N100","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.LogNormal_Covariance_N100","phase":"stabilized-pass","startedUtc":"2026-09-09T00:48:33.3624182+00:00","wallSeconds":5.644308,"testSeconds":0.0633536,"buildSeconds":3.88,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184833-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogNormal_Covariance_N100\\haden_HADEN_2026-09-09_00_48_38.775.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184833-362-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogNormal_Covariance_N100.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184839-707-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogNormal_Covariance_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.LogNormal_Covariance_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T00:48:39.7074447+00:00","wallSeconds":5.7151065,"testSeconds":0.0646193,"buildSeconds":3.97,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184839-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogNormal_Covariance_N25\\haden_HADEN_2026-09-09_00_48_45.187.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184839-707-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogNormal_Covariance_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184846-183-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogPearsonTypeIII_Covariance_Example1","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.LogPearsonTypeIII_Covariance_Example1","phase":"stabilized-pass","startedUtc":"2026-09-09T00:48:46.1833046+00:00","wallSeconds":6.0761665,"testSeconds":0.2957115,"buildSeconds":3.93,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184846-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogPearsonTypeIII_Covariance_Example1\\haden_HADEN_2026-09-09_00_48_52.016.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184846-183-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogPearsonTypeIII_Covariance_Example1.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184853-272-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogPearsonTypeIII_Covariance_N100","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.LogPearsonTypeIII_Covariance_N100","phase":"stabilized-pass","startedUtc":"2026-09-09T00:48:53.2728319+00:00","wallSeconds":5.5145371,"testSeconds":0.0624015,"buildSeconds":3.81,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184853-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogPearsonTypeIII_Covariance_N100\\haden_HADEN_2026-09-09_00_48_58.546.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184853-272-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogPearsonTypeIII_Covariance_N100.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184859-494-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogPearsonTypeIII_Covariance_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.LogPearsonTypeIII_Covariance_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T00:48:59.4945020+00:00","wallSeconds":5.8331347,"testSeconds":0.078674,"buildSeconds":3.79,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184859-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogPearsonTypeIII_Covariance_N25\\haden_HADEN_2026-09-09_00_49_05.045.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184859-494-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogPearsonTypeIII_Covariance_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184906-142-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Normal_Covariance_N100","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.Normal_Covariance_N100","phase":"stabilized-pass","startedUtc":"2026-09-09T00:49:06.1426974+00:00","wallSeconds":6.3601182,"testSeconds":0.0664762,"buildSeconds":4.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184906-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Normal_Covariance_N100\\haden_HADEN_2026-09-09_00_49_12.267.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184906-142-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Normal_Covariance_N100.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184913-210-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Normal_Covariance_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.Normal_Covariance_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T00:49:13.2103959+00:00","wallSeconds":5.773988,"testSeconds":0.1177571,"buildSeconds":3.95,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184913-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Normal_Covariance_N25\\haden_HADEN_2026-09-09_00_49_18.721.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184913-210-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Normal_Covariance_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184919-862-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_PearsonTypeIII_Covariance_N100","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.PearsonTypeIII_Covariance_N100","phase":"stabilized-pass","startedUtc":"2026-09-09T00:49:19.8629925+00:00","wallSeconds":5.621458,"testSeconds":0.0762069,"buildSeconds":3.8,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184919-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_PearsonTypeIII_Covariance_N100\\haden_HADEN_2026-09-09_00_49_25.254.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184919-862-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_PearsonTypeIII_Covariance_N100.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184926-185-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_PearsonTypeIII_Covariance_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.PearsonTypeIII_Covariance_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T00:49:26.1856591+00:00","wallSeconds":5.4382123,"testSeconds":0.0706529,"buildSeconds":3.72,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184926-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_PearsonTypeIII_Covariance_N25\\haden_HADEN_2026-09-09_00_49_31.393.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184926-185-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_PearsonTypeIII_Covariance_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184932-328-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogPearsonTypeIII_PenalityOnAllParams_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogPearsonTypeIII_PenalityOnAllParams_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T00:49:32.3283923+00:00","wallSeconds":5.637843,"testSeconds":0.0965045,"buildSeconds":3.79,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184932-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogPearsonTypeIII_PenalityOnAllParams_N25\\haden_HADEN_2026-09-09_00_49_37.723.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184932-328-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogPearsonTypeIII_PenalityOnAllParams_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["LogNormal","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184938-934-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogPearsonTypeIII_PenalityOnAllParamsAndQ99_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogPearsonTypeIII_PenalityOnAllParamsAndQ99_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T00:49:38.9346761+00:00","wallSeconds":5.6039182,"testSeconds":0.115776,"buildSeconds":3.83,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184938-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogPearsonTypeIII_PenalityOnAllParamsAndQ99_N25\\haden_HADEN_2026-09-09_00_49_44.306.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184938-934-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogPearsonTypeIII_PenalityOnAllParamsAndQ99_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["LogNormal","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184945-246-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogPearsonTypeIII_PenalityOnMuAndGamma_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogPearsonTypeIII_PenalityOnMuAndGamma_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T00:49:45.2470256+00:00","wallSeconds":5.9260477,"testSeconds":0.1022946,"buildSeconds":4.07,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184945-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogPearsonTypeIII_PenalityOnMuAndGamma_N25\\haden_HADEN_2026-09-09_00_49_50.913.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184945-246-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogPearsonTypeIII_PenalityOnMuAndGamma_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["LogNormal","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184951-862-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_LogPearsonTypeIII_GmmRecoversGeneratingLog10ParentAndQ99","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CSyntheticDataTests.LogPearsonTypeIII_GmmRecoversGeneratingLog10ParentAndQ99","phase":"stabilized-pass","startedUtc":"2026-09-09T00:49:51.8623939+00:00","wallSeconds":6.064011,"testSeconds":0.0835671,"buildSeconds":3.79,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184951-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_LogPearsonTypeIII_GmmRecoversGeneratingLog10ParentAndQ99\\haden_HADEN_2026-09-09_00_49_57.666.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184951-862-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_LogPearsonTypeIII_GmmRecoversGeneratingLog10ParentAndQ99.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-184958-656-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_PearsonTypeIII_GmmRecoversGeneratingParentAndQ99","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CSyntheticDataTests.PearsonTypeIII_GmmRecoversGeneratingParentAndQ99","phase":"stabilized-pass","startedUtc":"2026-09-09T00:49:58.6562284+00:00","wallSeconds":5.7753773,"testSeconds":0.1065534,"buildSeconds":3.95,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-184958-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_PearsonTypeIII_GmmRecoversGeneratingParentAndQ99\\haden_HADEN_2026-09-09_00_50_04.199.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-184958-656-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_PearsonTypeIII_GmmRecoversGeneratingParentAndQ99.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-185005-138-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedExtremeValue_LinearShape_RecoversParentResponses","method":"RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.GeneralizedExtremeValue_LinearShape_RecoversParentResponses","phase":"stabilized-pass","startedUtc":"2026-09-09T00:50:05.1381164+00:00","wallSeconds":111.1338968,"testSeconds":105.1904583,"buildSeconds":3.76,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-185005-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedExtremeValue_LinearShape_RecoversParentResponses\\haden_HADEN_2026-09-09_00_51_55.821.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-185005-138-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedExtremeValue_LinearShape_RecoversParentResponses.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["Exponential","GeneralizedExtremeValue","GeneralizedPareto","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-185157-062-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedPareto_LinearLocationExponentialScale_RecoversParentResponses","method":"RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.GeneralizedPareto_LinearLocationExponentialScale_RecoversParentResponses","phase":"stabilized-pass","startedUtc":"2026-09-09T00:51:57.0624882+00:00","wallSeconds":155.4505279,"testSeconds":149.5894408,"buildSeconds":3.77,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-185157-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedPareto_LinearLocationExponentialScale_RecoversParentResponses\\haden_HADEN_2026-09-09_00_54_32.055.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-185157-062-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedPareto_LinearLocationExponentialScale_RecoversParentResponses.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"5E53C15B39102B794C64875CBEF262A8FD06AA0C30E6050C1873A1C29AE4F844"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"97DDE082AE31BFBC18745DC701E07A4ABD8CC8BBADEE9722DD4F11386D197DC6","bestFitAssemblySha256":"33C6E955D969701D9129DF7FD504EADEBBB10D169F7785A80A50CE8AA805123D","verificationAssemblySha256":"8BACD6EC0C24CB65DF68DBE4B88CC03ECDB81BECC3D9BC002575E709515EB64F","dependencies":["Exponential","GeneralizedExtremeValue","GeneralizedPareto","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-185708-144-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedExtremeValue_LinearShape_RecoversParentResponses","method":"RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.GeneralizedExtremeValue_LinearShape_RecoversParentResponses","phase":"runtime-baseline","startedUtc":"2026-09-09T00:57:08.1460914+00:00","wallSeconds":99.1979276,"testSeconds":88.5106879,"buildSeconds":8.28,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260908-185708-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedExtremeValue_LinearShape_RecoversParentResponses\\haden_HADEN_2026-09-09_00_58_46.871.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-185708-144-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedExtremeValue_LinearShape_RecoversParentResponses.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"d80bfa8621c48a78cf4ebad7f01326841fac37aa","sourceSha256":"95A9BB4F8927EF0934A13A39B566C1FA30BDB42B2F95AC965746DD122DB39BA0"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"17C77A3DDAE1EF7B39BA3945DB5F6FB61F93F3D1D57FD48AF96248C9A6054F43"},"numericsAssemblySha256":"3E82F983E4795FD50104CBF0F950A7513BDF94540C3397401A8EC5BBF1A6C60F","bestFitAssemblySha256":"67A7E4809110D816C06ED83E4F9068F6E6FA78E490A5486FCEF62A56D7019DC0","verificationAssemblySha256":"542282B3090D914193685DF647EB18960BC31D061A17CACA7E63A2AA92CC9E46","dependencies":["Exponential","GeneralizedExtremeValue","GeneralizedPareto","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Pinned baseline timing comparison after current nonstationaryGEV105.190s andGPA149.589s passed but exceeded older source-unverified timings. Serial equivalent exact method, no setting changes."} +{"runId":"20260908-185848-462-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedPareto_LinearLocationExponentialScale_RecoversParentResponses","method":"RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.GeneralizedPareto_LinearLocationExponentialScale_RecoversParentResponses","phase":"runtime-baseline","startedUtc":"2026-09-09T00:58:48.4625794+00:00","wallSeconds":138.8853911,"testSeconds":132.8802468,"buildSeconds":3.92,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260908-185848-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedPareto_LinearLocationExponentialScale_RecoversParentResponses\\haden_HADEN_2026-09-09_01_01_06.902.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-185848-462-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedPareto_LinearLocationExponentialScale_RecoversParentResponses.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"d80bfa8621c48a78cf4ebad7f01326841fac37aa","sourceSha256":"95A9BB4F8927EF0934A13A39B566C1FA30BDB42B2F95AC965746DD122DB39BA0"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"17C77A3DDAE1EF7B39BA3945DB5F6FB61F93F3D1D57FD48AF96248C9A6054F43"},"numericsAssemblySha256":"3E82F983E4795FD50104CBF0F950A7513BDF94540C3397401A8EC5BBF1A6C60F","bestFitAssemblySha256":"67A7E4809110D816C06ED83E4F9068F6E6FA78E490A5486FCEF62A56D7019DC0","verificationAssemblySha256":"542282B3090D914193685DF647EB18960BC31D061A17CACA7E63A2AA92CC9E46","dependencies":["Exponential","GeneralizedExtremeValue","GeneralizedPareto","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Pinned baseline timing comparison after current nonstationaryGEV105.190s andGPA149.589s passed but exceeded older source-unverified timings. Serial equivalent exact method, no setting changes."} +{"runId":"20260908-191901-471-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedExtremeValue_LinearShape_RecoversParentResponses","method":"RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.GeneralizedExtremeValue_LinearShape_RecoversParentResponses","phase":"stabilized-pass","startedUtc":"2026-09-09T01:19:01.4749060+00:00","wallSeconds":83.6982304,"testSeconds":73.2362216,"buildSeconds":7.94,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-191901-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedExtremeValue_LinearShape_RecoversParentResponses\\haden_HADEN_2026-09-09_01_20_24.859.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-191901-471-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedExtremeValue_LinearShape_RecoversParentResponses.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"316FF021F2E92B8F82D24F0E333DC4A00D7FCF261D075E9EFAA076F3D9152490"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"AFEF6B3FB6EF568E12FDA8E39D671716E95A1FE04FCF516678ABAEF0948DDB75","bestFitAssemblySha256":"1DDE6D000FB8BC2D0EDB44FEDAFE1400996B6BCCA0EC1C2AC9280B9498D2ABEF","verificationAssemblySha256":"60AFD9D5D9687990E43EA566102B95A0AA80BFDAC14B97B4EE0B5130AA3DF5B7","dependencies":["Exponential","GeneralizedExtremeValue","GeneralizedPareto","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-192026-149-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedPareto_LinearLocationExponentialScale_RecoversParentResponses","method":"RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.GeneralizedPareto_LinearLocationExponentialScale_RecoversParentResponses","phase":"stabilized-pass","startedUtc":"2026-09-09T01:20:26.1491550+00:00","wallSeconds":95.6470219,"testSeconds":89.2320612,"buildSeconds":4.37,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-192026-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedPareto_LinearLocationExponentialScale_RecoversParentResponses\\haden_HADEN_2026-09-09_01_22_01.336.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-192026-149-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedPareto_LinearLocationExponentialScale_RecoversParentResponses.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"316FF021F2E92B8F82D24F0E333DC4A00D7FCF261D075E9EFAA076F3D9152490"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"AFEF6B3FB6EF568E12FDA8E39D671716E95A1FE04FCF516678ABAEF0948DDB75","bestFitAssemblySha256":"1DDE6D000FB8BC2D0EDB44FEDAFE1400996B6BCCA0EC1C2AC9280B9498D2ABEF","verificationAssemblySha256":"60AFD9D5D9687990E43EA566102B95A0AA80BFDAC14B97B4EE0B5130AA3DF5B7","dependencies":["Exponential","GeneralizedExtremeValue","GeneralizedPareto","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-192217-554-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses","method":"RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses","phase":"stabilized-pass","startedUtc":"2026-09-09T01:22:17.5572296+00:00","wallSeconds":203.534892,"testSeconds":197.2552163,"buildSeconds":4.11,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-192217-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses\\haden_HADEN_2026-09-09_01_25_40.629.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-192217-554-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"316FF021F2E92B8F82D24F0E333DC4A00D7FCF261D075E9EFAA076F3D9152490"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"AFEF6B3FB6EF568E12FDA8E39D671716E95A1FE04FCF516678ABAEF0948DDB75","bestFitAssemblySha256":"1DDE6D000FB8BC2D0EDB44FEDAFE1400996B6BCCA0EC1C2AC9280B9498D2ABEF","verificationAssemblySha256":"60AFD9D5D9687990E43EA566102B95A0AA80BFDAC14B97B4EE0B5130AA3DF5B7","dependencies":["Exponential","GeneralizedExtremeValue","GeneralizedPareto","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-192603-604-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses","method":"RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses","phase":"runtime-baseline","startedUtc":"2026-09-09T01:26:03.6064340+00:00","wallSeconds":161.6467651,"testSeconds":155.7355322,"buildSeconds":3.95,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260908-192603-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses\\haden_HADEN_2026-09-09_01_28_44.799.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-192603-604-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"d80bfa8621c48a78cf4ebad7f01326841fac37aa","sourceSha256":"95A9BB4F8927EF0934A13A39B566C1FA30BDB42B2F95AC965746DD122DB39BA0"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"17C77A3DDAE1EF7B39BA3945DB5F6FB61F93F3D1D57FD48AF96248C9A6054F43"},"numericsAssemblySha256":"3E82F983E4795FD50104CBF0F950A7513BDF94540C3397401A8EC5BBF1A6C60F","bestFitAssemblySha256":"67A7E4809110D816C06ED83E4F9068F6E6FA78E490A5486FCEF62A56D7019DC0","verificationAssemblySha256":"542282B3090D914193685DF647EB18960BC31D061A17CACA7E63A2AA92CC9E46","dependencies":["Exponential","GeneralizedExtremeValue","GeneralizedPareto","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Immediate runtime comparison after current LP3 nonstationary pass197.255s; unchanged exact method and settings on pinned fbe0989/d80bfa8."} +{"runId":"20260908-193813-963-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses","method":"RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses","phase":"stabilized-pass","startedUtc":"2026-09-09T01:38:13.9658547+00:00","wallSeconds":174.6149575,"testSeconds":166.5686974,"buildSeconds":5.63,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-193814-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses\\haden_HADEN_2026-09-09_01_41_08.126.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-193813-963-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"57794BF017DFEEAD11FA358D93F60D162A293B356001964CD8CE6A7B9B56C5CA"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"411C459C2C006F16A82651D9FACEE3AA9D2AFC5FEB5A14A42CF52A4F28368ED0","bestFitAssemblySha256":"25B94C800670E2BFBC9C675B18934527361E35CD2A9988A29C2409099ADAF073","verificationAssemblySha256":"1C7A825E6881086D655D8859EC7055F0C78E14ED16051C89F3BE3234E34D10C1","dependencies":["Exponential","GeneralizedExtremeValue","GeneralizedPareto","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-194240-237-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses","method":"RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses","phase":"runtime-baseline-repeat-interrupted","startedUtc":"2026-09-08T19:42:40.237-06:00","result":"Interrupted","wallSeconds":null,"testSeconds":null,"buildSeconds":3.74,"trx":null,"log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-194240-237-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses.log","exactIdentityVerified":false,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"d80bfa8621c48a78cf4ebad7f01326841fac37aa","sourceSha256":"95A9BB4F8927EF0934A13A39B566C1FA30BDB42B2F95AC965746DD122DB39BA0"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"17C77A3DDAE1EF7B39BA3945DB5F6FB61F93F3D1D57FD48AF96248C9A6054F43"},"dependencies":["Exponential","GeneralizedExtremeValue","GeneralizedPareto","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":null,"sourceProvenance":"Immutable baseline identity from preceding completed comparison; no final source or assembly validation possible for interrupted run.","note":"Turn interruption ended the process before a TRX or guarded-runner outcome was produced. No numerical or timing result is claimed; rerun required."} +{"runId":"20260908-201337-843-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses","method":"RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses","phase":"runtime-baseline-repeat","startedUtc":"2026-09-09T02:13:37.8436405+00:00","wallSeconds":155.4329399,"testSeconds":148.2666754,"buildSeconds":5.12,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260908-201337-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses\\haden_HADEN_2026-09-09_02_16_12.825.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-201337-843-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"d80bfa8621c48a78cf4ebad7f01326841fac37aa","sourceSha256":"95A9BB4F8927EF0934A13A39B566C1FA30BDB42B2F95AC965746DD122DB39BA0"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"17C77A3DDAE1EF7B39BA3945DB5F6FB61F93F3D1D57FD48AF96248C9A6054F43"},"numericsAssemblySha256":"3E82F983E4795FD50104CBF0F950A7513BDF94540C3397401A8EC5BBF1A6C60F","bestFitAssemblySha256":"67A7E4809110D816C06ED83E4F9068F6E6FA78E490A5486FCEF62A56D7019DC0","verificationAssemblySha256":"542282B3090D914193685DF647EB18960BC31D061A17CACA7E63A2AA92CC9E46","dependencies":["Exponential","GeneralizedExtremeValue","GeneralizedPareto","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Re-executing the incomplete baseline repeat after interruption; compare with repaired166.569s and initial baseline155.736s."} +{"runId":"20260908-201652-356-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.NormalMixture2D_BayesianRecovery","phase":"stabilized-pass","startedUtc":"2026-09-09T02:16:52.3590689+00:00","wallSeconds":171.3039466,"testSeconds":164.9721767,"buildSeconds":4.29,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260908-201652-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery\\haden_HADEN_2026-09-09_02_19_43.215.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-201652-356-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"57794BF017DFEEAD11FA358D93F60D162A293B356001964CD8CE6A7B9B56C5CA"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"411C459C2C006F16A82651D9FACEE3AA9D2AFC5FEB5A14A42CF52A4F28368ED0","bestFitAssemblySha256":"25B94C800670E2BFBC9C675B18934527361E35CD2A9988A29C2409099ADAF073","verificationAssemblySha256":"1C7A825E6881086D655D8859EC7055F0C78E14ED16051C89F3BE3234E34D10C1","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260908-202303-275-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.NormalMixture2D_BayesianRecovery","phase":"runtime-baseline","startedUtc":"2026-09-09T02:23:03.2772565+00:00","wallSeconds":124.900885,"testSeconds":119.2297198,"buildSeconds":3.59,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260908-202303-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery\\haden_HADEN_2026-09-09_02_25_07.735.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-202303-275-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"d80bfa8621c48a78cf4ebad7f01326841fac37aa","sourceSha256":"95A9BB4F8927EF0934A13A39B566C1FA30BDB42B2F95AC965746DD122DB39BA0"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"17C77A3DDAE1EF7B39BA3945DB5F6FB61F93F3D1D57FD48AF96248C9A6054F43"},"numericsAssemblySha256":"3E82F983E4795FD50104CBF0F950A7513BDF94540C3397401A8EC5BBF1A6C60F","bestFitAssemblySha256":"67A7E4809110D816C06ED83E4F9068F6E6FA78E490A5486FCEF62A56D7019DC0","verificationAssemblySha256":"542282B3090D914193685DF647EB18960BC31D061A17CACA7E63A2AA92CC9E46","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Immediate pinned comparison after repaired current NormalMixture2D Bayesian pass164.972s; historical132.3s is only an unverified triage hint."} +{"runId":"20260908-203450-753-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses","method":"RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses","phase":"stabilized-pass-interrupted","startedUtc":"2026-09-09T02:34:50.9199952Z","wallSeconds":null,"testSeconds":null,"buildSeconds":5.54,"result":"Interrupted","trx":null,"log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260908-203450-753-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses.log","exactIdentityVerified":false,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a"},"dependencies":["LogPearsonTypeIII","PearsonTypeIII"],"sourceChangedDuringRun":null,"failure":"Execution interrupted before any TRX or finalized source verification was produced.","note":"No result claimed. The second requested NormalMixture2D method was not started."} +{"runId":"20260909-062212-877-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses","method":"RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses","phase":"stabilized-pass","startedUtc":"2026-09-09T12:22:12.8794949+00:00","wallSeconds":131.8213686,"testSeconds":126.8669343,"buildSeconds":2.79,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-062213-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses\\haden_HADEN_2026-09-09_12_24_24.272.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-062212-877-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C7E9BB84CAE61D083767785B865854BFEDB4F27F505C8C45AF5979E74512F5BC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"8391DAB3D143BBE659329E4C7EE7967CAB4D1C2D25E8658477060603AE922E5A","bestFitAssemblySha256":"2A6201E80567EA2CA48DA412D7BBE048086E0D2F1B74EA64F401A1BBD1F44150","verificationAssemblySha256":"41BC5F15D5847D8993C51B45506E1383B880155C99FA0127803E8FEFDD331EDD","dependencies":["Exponential","GeneralizedExtremeValue","GeneralizedPareto","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-062425-328-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.NormalMixture2D_BayesianRecovery","phase":"stabilized-pass","startedUtc":"2026-09-09T12:24:25.3286982+00:00","wallSeconds":130.5301338,"testSeconds":126.6838005,"buildSeconds":1.94,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-062425-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery\\haden_HADEN_2026-09-09_12_26_35.451.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-062425-328-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C7E9BB84CAE61D083767785B865854BFEDB4F27F505C8C45AF5979E74512F5BC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"645D7B095936EB73256D19A4433A351CCDA407590C95E97CC4E035E539FC172B"},"numericsAssemblySha256":"8391DAB3D143BBE659329E4C7EE7967CAB4D1C2D25E8658477060603AE922E5A","bestFitAssemblySha256":"2A6201E80567EA2CA48DA412D7BBE048086E0D2F1B74EA64F401A1BBD1F44150","verificationAssemblySha256":"41BC5F15D5847D8993C51B45506E1383B880155C99FA0127803E8FEFDD331EDD","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-062705-907-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.NormalMixture2D_BayesianRecovery","phase":"runtime-baseline-repeat","startedUtc":"2026-09-09T12:27:05.9095723+00:00","wallSeconds":106.9931808,"testSeconds":102.8552066,"buildSeconds":1.84,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260909-062706-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery\\haden_HADEN_2026-09-09_12_28_52.462.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-062705-907-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"d80bfa8621c48a78cf4ebad7f01326841fac37aa","sourceSha256":"95A9BB4F8927EF0934A13A39B566C1FA30BDB42B2F95AC965746DD122DB39BA0"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"17C77A3DDAE1EF7B39BA3945DB5F6FB61F93F3D1D57FD48AF96248C9A6054F43"},"numericsAssemblySha256":"3E82F983E4795FD50104CBF0F950A7513BDF94540C3397401A8EC5BBF1A6C60F","bestFitAssemblySha256":"67A7E4809110D816C06ED83E4F9068F6E6FA78E490A5486FCEF62A56D7019DC0","verificationAssemblySha256":"542282B3090D914193685DF647EB18960BC31D061A17CACA7E63A2AA92CC9E46","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Adjacent comparison to final repaired NormalMixture2D pass126.6838005s; prior baseline119.2297198s was from the previous evening."} +{"runId":"20260909-063029-185-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.NormalMixture2D_BayesianRecovery","phase":"runtime-isolation","startedUtc":"2026-09-09T12:30:29.1871965+00:00","wallSeconds":9.0455052,"testSeconds":0.0365621,"buildSeconds":7.11,"result":"Failed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260909-063029-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery\\haden_HADEN_2026-09-09_12_30_37.985.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-063029-185-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C7E9BB84CAE61D083767785B865854BFEDB4F27F505C8C45AF5979E74512F5BC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"17C77A3DDAE1EF7B39BA3945DB5F6FB61F93F3D1D57FD48AF96248C9A6054F43"},"numericsAssemblySha256":"8391DAB3D143BBE659329E4C7EE7967CAB4D1C2D25E8658477060603AE922E5A","bestFitAssemblySha256":"973EE8B1255EFAEDB5FE628B980434E7A32122FE16F4520FA8001431ACDFC6A3","verificationAssemblySha256":"4716754F18407352D1C906A3360BA21FB60A1945E931ED305410D76CEA99D3F6","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":"Test method RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.NormalMixture2D_BayesianRecovery threw exception: \nSystem.ArgumentOutOfRangeException: Insufficient observations to initialize the distribution. (Parameter 'sample')","note":"B0/Ncurrent isolates Numerics from BestFit integration after final current126.6838005s and adjacent pinned B0/N0 baseline102.8552066s."} +{"runId":"20260909-065922-723-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_ZeroInflatedNormalMixture2D_BayesianRecovery","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.ZeroInflatedNormalMixture2D_BayesianRecovery","phase":"runtime-baseline","startedUtc":"2026-09-09T12:59:22.7245342+00:00","wallSeconds":234.0553055,"testSeconds":224.5834653,"buildSeconds":7.38,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260909-065922-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_ZeroInflatedNormalMixture2D_BayesianRecovery\\haden_HADEN_2026-09-09_13_03_16.315.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-065922-723-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_ZeroInflatedNormalMixture2D_BayesianRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"d80bfa8621c48a78cf4ebad7f01326841fac37aa","sourceSha256":"95A9BB4F8927EF0934A13A39B566C1FA30BDB42B2F95AC965746DD122DB39BA0"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"17C77A3DDAE1EF7B39BA3945DB5F6FB61F93F3D1D57FD48AF96248C9A6054F43"},"numericsAssemblySha256":"3E82F983E4795FD50104CBF0F950A7513BDF94540C3397401A8EC5BBF1A6C60F","bestFitAssemblySha256":"67A7E4809110D816C06ED83E4F9068F6E6FA78E490A5486FCEF62A56D7019DC0","verificationAssemblySha256":"542282B3090D914193685DF647EB18960BC31D061A17CACA7E63A2AA92CC9E46","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Pinned baseline for the zero-inflated Normal mixture normalization hotspot; source inspection and microdiagnostic identify repeated unchanged LogCCDF(0) as the candidate cause. Existing method/settings unchanged."} +{"runId":"20260909-070637-355-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_ZeroInflatedNormalMixture2D_BayesianRecovery","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.ZeroInflatedNormalMixture2D_BayesianRecovery","phase":"stabilized-pass","startedUtc":"2026-09-09T13:06:37.3572325+00:00","wallSeconds":144.9230021,"testSeconds":139.9281735,"buildSeconds":2.98,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-070637-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_ZeroInflatedNormalMixture2D_BayesianRecovery\\haden_HADEN_2026-09-09_13_09_01.859.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-070637-355-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_ZeroInflatedNormalMixture2D_BayesianRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C7C980C107E976E6D04555812E98C0A5DD09D5B50EB2A94C545CD5166752A295"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"2E5E4F2487E4D28D758B6C05B72743BA27DD94C464EEDF261D2AF360BAC5972F","bestFitAssemblySha256":"2655C7B2D5D976E65B77C58D262962E5F6D27F5BBD32F8BED5AD15D8C63C7C6C","verificationAssemblySha256":"123BE80FFA6ADB6091B8C5E14F30A6E98DFB4902949B84698E8A583E5E4888D0","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-070903-047-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.NormalMixture2D_BayesianRecovery","phase":"stabilized-pass","startedUtc":"2026-09-09T13:09:03.0476247+00:00","wallSeconds":122.4774941,"testSeconds":119.1123106,"buildSeconds":1.66,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-070903-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery\\haden_HADEN_2026-09-09_13_11_05.136.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-070903-047-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C7C980C107E976E6D04555812E98C0A5DD09D5B50EB2A94C545CD5166752A295"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"2E5E4F2487E4D28D758B6C05B72743BA27DD94C464EEDF261D2AF360BAC5972F","bestFitAssemblySha256":"2655C7B2D5D976E65B77C58D262962E5F6D27F5BBD32F8BED5AD15D8C63C7C6C","verificationAssemblySha256":"123BE80FFA6ADB6091B8C5E14F30A6E98DFB4902949B84698E8A583E5E4888D0","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-071106-090-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture3D_BayesianRecovery","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.NormalMixture3D_BayesianRecovery","phase":"stabilized-pass","startedUtc":"2026-09-09T13:11:06.0906530+00:00","wallSeconds":278.1053567,"testSeconds":274.7931237,"buildSeconds":1.57,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-071106-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture3D_BayesianRecovery\\haden_HADEN_2026-09-09_13_15_43.792.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-071106-090-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture3D_BayesianRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C7C980C107E976E6D04555812E98C0A5DD09D5B50EB2A94C545CD5166752A295"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"2E5E4F2487E4D28D758B6C05B72743BA27DD94C464EEDF261D2AF360BAC5972F","bestFitAssemblySha256":"2655C7B2D5D976E65B77C58D262962E5F6D27F5BBD32F8BED5AD15D8C63C7C6C","verificationAssemblySha256":"123BE80FFA6ADB6091B8C5E14F30A6E98DFB4902949B84698E8A583E5E4888D0","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-071544-743-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_Recovery_Parity","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.NormalMixture2D_Recovery_Parity","phase":"stabilized-pass","startedUtc":"2026-09-09T13:15:44.7432260+00:00","wallSeconds":3.3880823,"testSeconds":0.2465856,"buildSeconds":1.59,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-071544-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_Recovery_Parity\\haden_HADEN_2026-09-09_13_15_47.898.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-071544-743-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_Recovery_Parity.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C7C980C107E976E6D04555812E98C0A5DD09D5B50EB2A94C545CD5166752A295"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"2E5E4F2487E4D28D758B6C05B72743BA27DD94C464EEDF261D2AF360BAC5972F","bestFitAssemblySha256":"2655C7B2D5D976E65B77C58D262962E5F6D27F5BBD32F8BED5AD15D8C63C7C6C","verificationAssemblySha256":"123BE80FFA6ADB6091B8C5E14F30A6E98DFB4902949B84698E8A583E5E4888D0","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-071548-646-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture3D_Recovery_Parity","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.NormalMixture3D_Recovery_Parity","phase":"stabilized-pass","startedUtc":"2026-09-09T13:15:48.6464931+00:00","wallSeconds":4.5722793,"testSeconds":1.5931342,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-071548-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture3D_Recovery_Parity\\haden_HADEN_2026-09-09_13_15_52.993.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-071548-646-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture3D_Recovery_Parity.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C7C980C107E976E6D04555812E98C0A5DD09D5B50EB2A94C545CD5166752A295"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"2E5E4F2487E4D28D758B6C05B72743BA27DD94C464EEDF261D2AF360BAC5972F","bestFitAssemblySha256":"2655C7B2D5D976E65B77C58D262962E5F6D27F5BBD32F8BED5AD15D8C63C7C6C","verificationAssemblySha256":"123BE80FFA6ADB6091B8C5E14F30A6E98DFB4902949B84698E8A583E5E4888D0","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-071553-754-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_ZeroInflatedNormalMixture2D_Recovery_Parity","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.ZeroInflatedNormalMixture2D_Recovery_Parity","phase":"stabilized-pass","startedUtc":"2026-09-09T13:15:53.7542713+00:00","wallSeconds":3.286861,"testSeconds":0.3922624,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-071553-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_ZeroInflatedNormalMixture2D_Recovery_Parity\\haden_HADEN_2026-09-09_13_15_56.818.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-071553-754-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_ZeroInflatedNormalMixture2D_Recovery_Parity.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C7C980C107E976E6D04555812E98C0A5DD09D5B50EB2A94C545CD5166752A295"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"2E5E4F2487E4D28D758B6C05B72743BA27DD94C464EEDF261D2AF360BAC5972F","bestFitAssemblySha256":"2655C7B2D5D976E65B77C58D262962E5F6D27F5BBD32F8BED5AD15D8C63C7C6C","verificationAssemblySha256":"123BE80FFA6ADB6091B8C5E14F30A6E98DFB4902949B84698E8A583E5E4888D0","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-071816-781-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.NormalMixture2D_BayesianRecovery","phase":"runtime-baseline-final-comparison","startedUtc":"2026-09-09T13:18:16.7836043+00:00","wallSeconds":109.661153,"testSeconds":105.9387676,"buildSeconds":1.61,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260909-071816-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery\\haden_HADEN_2026-09-09_13_20_05.999.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-071816-781-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"d80bfa8621c48a78cf4ebad7f01326841fac37aa","sourceSha256":"95A9BB4F8927EF0934A13A39B566C1FA30BDB42B2F95AC965746DD122DB39BA0"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"17C77A3DDAE1EF7B39BA3945DB5F6FB61F93F3D1D57FD48AF96248C9A6054F43"},"numericsAssemblySha256":"3E82F983E4795FD50104CBF0F950A7513BDF94540C3397401A8EC5BBF1A6C60F","bestFitAssemblySha256":"67A7E4809110D816C06ED83E4F9068F6E6FA78E490A5486FCEF62A56D7019DC0","verificationAssemblySha256":"542282B3090D914193685DF647EB18960BC31D061A17CACA7E63A2AA92CC9E46","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Final comparison to cache-repaired119.1123106s; prior pinned baselines119.2297198/102.8552066s show host variability. Allsixcurrentmixturemethods pass."} +{"runId":"20260909-073209-363-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.NormalMixture2D_BayesianRecovery","phase":"stabilized-pass","startedUtc":"2026-09-09T13:32:09.3660655+00:00","wallSeconds":113.1794493,"testSeconds":108.424885,"buildSeconds":2.73,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073209-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery\\haden_HADEN_2026-09-09_13_34_02.120.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073209-363-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073418-970-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T13:34:18.9724034+00:00","wallSeconds":5.0077985,"testSeconds":1.8310231,"buildSeconds":1.56,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073419-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_13_34_23.740.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073418-970-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073424-756-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T13:34:24.7561970+00:00","wallSeconds":4.601845,"testSeconds":1.5615696,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073424-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_13_34_29.134.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073424-756-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073429-908-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T13:34:29.9081568+00:00","wallSeconds":4.5048022,"testSeconds":1.4892838,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073429-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_13_34_34.192.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073429-908-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073434-933-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample3","phase":"stabilized-pass","startedUtc":"2026-09-09T13:34:34.9334274+00:00","wallSeconds":6.3439987,"testSeconds":3.3187052,"buildSeconds":1.47,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073434-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3\\haden_HADEN_2026-09-09_13_34_41.048.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073434-933-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073441-960-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample4","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample4","phase":"stabilized-pass","startedUtc":"2026-09-09T13:34:41.9602687+00:00","wallSeconds":7.6979729,"testSeconds":4.3113729,"buildSeconds":1.7,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073442-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample4\\haden_HADEN_2026-09-09_13_34_49.409.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073441-960-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample4.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073450-172-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample5","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample5","phase":"stabilized-pass","startedUtc":"2026-09-09T13:34:50.1726615+00:00","wallSeconds":6.2338841,"testSeconds":3.2230109,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073450-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample5\\haden_HADEN_2026-09-09_13_34_56.172.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073450-172-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample5.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073456-931-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6a","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample6a","phase":"stabilized-pass","startedUtc":"2026-09-09T13:34:56.9315895+00:00","wallSeconds":5.2326441,"testSeconds":2.2219457,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073456-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6a\\haden_HADEN_2026-09-09_13_35_01.930.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073456-931-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6a.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073502-679-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6b","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample6b","phase":"stabilized-pass","startedUtc":"2026-09-09T13:35:02.6791837+00:00","wallSeconds":5.1234584,"testSeconds":2.1480893,"buildSeconds":1.45,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073502-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6b\\haden_HADEN_2026-09-09_13_35_07.577.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073502-679-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6b.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073508-331-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogPearsonTypeIII_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.LogPearsonTypeIII_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T13:35:08.3318012+00:00","wallSeconds":3.0641124,"testSeconds":0.0888585,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073508-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogPearsonTypeIII_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_13_35_11.177.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073508-331-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogPearsonTypeIII_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073511-907-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_PearsonTypeIII_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.PearsonTypeIII_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T13:35:11.9074297+00:00","wallSeconds":3.1690236,"testSeconds":0.0791032,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073511-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_PearsonTypeIII_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_13_35_14.718.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073511-907-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_PearsonTypeIII_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073515-608-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LogPearsonTypeIII_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_LogPearsonTypeIII_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T13:35:15.6088374+00:00","wallSeconds":3.1126697,"testSeconds":0.0954097,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073515-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LogPearsonTypeIII_MLE\\haden_HADEN_2026-09-09_13_35_18.493.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073515-608-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LogPearsonTypeIII_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073519-243-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_PearsonTypeIII_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_PearsonTypeIII_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T13:35:19.2435540+00:00","wallSeconds":3.0623938,"testSeconds":0.076104,"buildSeconds":1.47,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073519-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_PearsonTypeIII_MLE\\haden_HADEN_2026-09-09_13_35_22.084.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073519-243-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_PearsonTypeIII_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073522-824-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LogPearsonTypeIII_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_LogPearsonTypeIII_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T13:35:22.8248767+00:00","wallSeconds":3.7037008,"testSeconds":0.6719428,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073522-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LogPearsonTypeIII_RecoversTrueParameters\\haden_HADEN_2026-09-09_13_35_26.295.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073522-824-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LogPearsonTypeIII_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073527-069-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_PearsonTypeIII_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_PearsonTypeIII_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T13:35:27.0694635+00:00","wallSeconds":3.5157642,"testSeconds":0.4892504,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073527-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_PearsonTypeIII_RecoversTrueParameters\\haden_HADEN_2026-09-09_13_35_30.356.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073527-069-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_PearsonTypeIII_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073531-097-RMC_BestFit_Verification_ModelEstimation_ProfileQRecoveryTests_TrueProfile_LP3Q99_RecoversGeneratingResponse","method":"RMC.BestFit.Verification.ModelEstimation.ProfileQRecoveryTests.TrueProfile_LP3Q99_RecoversGeneratingResponse","phase":"stabilized-pass","startedUtc":"2026-09-09T13:35:31.0974515+00:00","wallSeconds":4.2238725,"testSeconds":1.238,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073531-RMC_BestFit_Verification_ModelEstimation_ProfileQRecoveryTests_TrueProfile_LP3Q99_RecoversGeneratingResponse\\haden_HADEN_2026-09-09_13_35_35.091.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073531-097-RMC_BestFit_Verification_ModelEstimation_ProfileQRecoveryTests_TrueProfile_LP3Q99_RecoversGeneratingResponse.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073535-831-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Exponential_Covariance_N100","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.Exponential_Covariance_N100","phase":"stabilized-pass","startedUtc":"2026-09-09T13:35:35.8317102+00:00","wallSeconds":3.0874346,"testSeconds":0.0648527,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073535-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Exponential_Covariance_N100\\haden_HADEN_2026-09-09_13_35_38.667.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073535-831-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Exponential_Covariance_N100.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073539-437-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Exponential_Covariance_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.Exponential_Covariance_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T13:35:39.4372731+00:00","wallSeconds":3.1605758,"testSeconds":0.0608006,"buildSeconds":1.56,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073539-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Exponential_Covariance_N25\\haden_HADEN_2026-09-09_13_35_42.375.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073539-437-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Exponential_Covariance_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073543-108-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Gamma_Covariance_N100","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.Gamma_Covariance_N100","phase":"stabilized-pass","startedUtc":"2026-09-09T13:35:43.1081425+00:00","wallSeconds":3.0629769,"testSeconds":0.0607728,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073543-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Gamma_Covariance_N100\\haden_HADEN_2026-09-09_13_35_45.949.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073543-108-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Gamma_Covariance_N100.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073546-679-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Gamma_Covariance_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.Gamma_Covariance_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T13:35:46.6791230+00:00","wallSeconds":3.2147192,"testSeconds":0.061166,"buildSeconds":1.65,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073546-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Gamma_Covariance_N25\\haden_HADEN_2026-09-09_13_35_49.675.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073546-679-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Gamma_Covariance_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073550-399-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogNormal_Covariance_N100","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.LogNormal_Covariance_N100","phase":"stabilized-pass","startedUtc":"2026-09-09T13:35:50.3994700+00:00","wallSeconds":2.9819178,"testSeconds":0.0587596,"buildSeconds":1.42,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073550-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogNormal_Covariance_N100\\haden_HADEN_2026-09-09_13_35_53.159.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073550-399-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogNormal_Covariance_N100.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073553-904-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogNormal_Covariance_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.LogNormal_Covariance_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T13:35:53.9043069+00:00","wallSeconds":3.0046035,"testSeconds":0.0595037,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073553-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogNormal_Covariance_N25\\haden_HADEN_2026-09-09_13_35_56.690.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073553-904-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogNormal_Covariance_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073557-433-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogPearsonTypeIII_Covariance_Example1","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.LogPearsonTypeIII_Covariance_Example1","phase":"stabilized-pass","startedUtc":"2026-09-09T13:35:57.4337594+00:00","wallSeconds":3.1095822,"testSeconds":0.1505571,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073557-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogPearsonTypeIII_Covariance_Example1\\haden_HADEN_2026-09-09_13_36_00.322.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073557-433-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogPearsonTypeIII_Covariance_Example1.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073601-049-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogPearsonTypeIII_Covariance_N100","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.LogPearsonTypeIII_Covariance_N100","phase":"stabilized-pass","startedUtc":"2026-09-09T13:36:01.0493646+00:00","wallSeconds":3.0777616,"testSeconds":0.0613867,"buildSeconds":1.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073601-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogPearsonTypeIII_Covariance_N100\\haden_HADEN_2026-09-09_13_36_03.865.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073601-049-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogPearsonTypeIII_Covariance_N100.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073604-634-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogPearsonTypeIII_Covariance_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.LogPearsonTypeIII_Covariance_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T13:36:04.6349217+00:00","wallSeconds":3.0396439,"testSeconds":0.0636708,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073604-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogPearsonTypeIII_Covariance_N25\\haden_HADEN_2026-09-09_13_36_07.451.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073604-634-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogPearsonTypeIII_Covariance_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073608-180-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Normal_Covariance_N100","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.Normal_Covariance_N100","phase":"stabilized-pass","startedUtc":"2026-09-09T13:36:08.1809820+00:00","wallSeconds":2.9953146,"testSeconds":0.0603949,"buildSeconds":1.45,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073608-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Normal_Covariance_N100\\haden_HADEN_2026-09-09_13_36_10.960.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073608-180-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Normal_Covariance_N100.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073611-687-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Normal_Covariance_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.Normal_Covariance_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T13:36:11.6871572+00:00","wallSeconds":2.9163738,"testSeconds":0.0583857,"buildSeconds":1.28,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073611-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Normal_Covariance_N25\\haden_HADEN_2026-09-09_13_36_14.345.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073611-687-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Normal_Covariance_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073615-118-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_PearsonTypeIII_Covariance_N100","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.PearsonTypeIII_Covariance_N100","phase":"stabilized-pass","startedUtc":"2026-09-09T13:36:15.1181181+00:00","wallSeconds":3.024063,"testSeconds":0.0690684,"buildSeconds":1.45,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073615-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_PearsonTypeIII_Covariance_N100\\haden_HADEN_2026-09-09_13_36_17.920.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073615-118-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_PearsonTypeIII_Covariance_N100.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073618-653-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_PearsonTypeIII_Covariance_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.PearsonTypeIII_Covariance_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T13:36:18.6534343+00:00","wallSeconds":3.0279574,"testSeconds":0.0629459,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073618-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_PearsonTypeIII_Covariance_N25\\haden_HADEN_2026-09-09_13_36_21.460.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073618-653-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_PearsonTypeIII_Covariance_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073622-191-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogPearsonTypeIII_PenalityOnAllParams_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogPearsonTypeIII_PenalityOnAllParams_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T13:36:22.1914117+00:00","wallSeconds":3.0443127,"testSeconds":0.0840762,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073622-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogPearsonTypeIII_PenalityOnAllParams_N25\\haden_HADEN_2026-09-09_13_36_25.012.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073622-191-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogPearsonTypeIII_PenalityOnAllParams_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["LogNormal","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073625-746-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogPearsonTypeIII_PenalityOnAllParamsAndQ99_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogPearsonTypeIII_PenalityOnAllParamsAndQ99_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T13:36:25.7468261+00:00","wallSeconds":3.0795358,"testSeconds":0.103372,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073625-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogPearsonTypeIII_PenalityOnAllParamsAndQ99_N25\\haden_HADEN_2026-09-09_13_36_28.608.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073625-746-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogPearsonTypeIII_PenalityOnAllParamsAndQ99_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["LogNormal","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073629-349-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogPearsonTypeIII_PenalityOnMuAndGamma_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogPearsonTypeIII_PenalityOnMuAndGamma_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T13:36:29.3496618+00:00","wallSeconds":3.0355541,"testSeconds":0.0807792,"buildSeconds":1.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073629-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogPearsonTypeIII_PenalityOnMuAndGamma_N25\\haden_HADEN_2026-09-09_13_36_32.153.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073629-349-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogPearsonTypeIII_PenalityOnMuAndGamma_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["LogNormal","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073632-912-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_LogPearsonTypeIII_GmmRecoversGeneratingLog10ParentAndQ99","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CSyntheticDataTests.LogPearsonTypeIII_GmmRecoversGeneratingLog10ParentAndQ99","phase":"stabilized-pass","startedUtc":"2026-09-09T13:36:32.9123402+00:00","wallSeconds":3.0191402,"testSeconds":0.0643648,"buildSeconds":1.45,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073632-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_LogPearsonTypeIII_GmmRecoversGeneratingLog10ParentAndQ99\\haden_HADEN_2026-09-09_13_36_35.699.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073632-912-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_LogPearsonTypeIII_GmmRecoversGeneratingLog10ParentAndQ99.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073636-449-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_PearsonTypeIII_GmmRecoversGeneratingParentAndQ99","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CSyntheticDataTests.PearsonTypeIII_GmmRecoversGeneratingParentAndQ99","phase":"stabilized-pass","startedUtc":"2026-09-09T13:36:36.4499414+00:00","wallSeconds":3.0207901,"testSeconds":0.0880413,"buildSeconds":1.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073636-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_PearsonTypeIII_GmmRecoversGeneratingParentAndQ99\\haden_HADEN_2026-09-09_13_36_39.255.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073636-449-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_PearsonTypeIII_GmmRecoversGeneratingParentAndQ99.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073639-984-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedExtremeValue_LinearShape_RecoversParentResponses","method":"RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.GeneralizedExtremeValue_LinearShape_RecoversParentResponses","phase":"stabilized-pass","startedUtc":"2026-09-09T13:36:39.9845557+00:00","wallSeconds":65.2781452,"testSeconds":62.2877474,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073640-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedExtremeValue_LinearShape_RecoversParentResponses\\haden_HADEN_2026-09-09_13_37_45.028.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073639-984-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedExtremeValue_LinearShape_RecoversParentResponses.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GeneralizedExtremeValue","GeneralizedPareto","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073745-847-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedPareto_LinearLocationExponentialScale_RecoversParentResponses","method":"RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.GeneralizedPareto_LinearLocationExponentialScale_RecoversParentResponses","phase":"stabilized-pass","startedUtc":"2026-09-09T13:37:45.8472600+00:00","wallSeconds":86.5068115,"testSeconds":83.1756149,"buildSeconds":1.52,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073745-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedPareto_LinearLocationExponentialScale_RecoversParentResponses\\haden_HADEN_2026-09-09_13_39_11.926.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073745-847-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedPareto_LinearLocationExponentialScale_RecoversParentResponses.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GeneralizedExtremeValue","GeneralizedPareto","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-073912-891-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses","method":"RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses","phase":"stabilized-pass","startedUtc":"2026-09-09T13:39:12.8916356+00:00","wallSeconds":128.0340527,"testSeconds":124.7944213,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-073912-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses\\haden_HADEN_2026-09-09_13_41_20.470.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-073912-891-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GeneralizedExtremeValue","GeneralizedPareto","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-074121-469-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_Normal_ReciprocalSigma_RecoversParentResponses","method":"RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.Normal_ReciprocalSigma_RecoversParentResponses","phase":"stabilized-pass","startedUtc":"2026-09-09T13:41:21.4694856+00:00","wallSeconds":26.2376155,"testSeconds":23.1697668,"buildSeconds":1.51,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-074121-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_Normal_ReciprocalSigma_RecoversParentResponses\\haden_HADEN_2026-09-09_13_41_47.475.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-074121-469-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_Normal_ReciprocalSigma_RecoversParentResponses.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GeneralizedExtremeValue","GeneralizedPareto","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-074148-225-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_Normal_SinusoidalSigma_RecoversParentResponses","method":"RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.Normal_SinusoidalSigma_RecoversParentResponses","phase":"stabilized-pass","startedUtc":"2026-09-09T13:41:48.2252066+00:00","wallSeconds":40.2461663,"testSeconds":37.1983253,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-074148-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_Normal_SinusoidalSigma_RecoversParentResponses\\haden_HADEN_2026-09-09_13_42_28.168.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-074148-225-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_Normal_SinusoidalSigma_RecoversParentResponses.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GeneralizedExtremeValue","GeneralizedPareto","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-074229-067-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_LogPearsonTypeIII_RecoversTrueParameters","method":"RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.LogPearsonTypeIII_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T13:42:29.0673734+00:00","wallSeconds":49.6776148,"testSeconds":46.4949531,"buildSeconds":1.53,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-074229-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_LogPearsonTypeIII_RecoversTrueParameters\\haden_HADEN_2026-09-09_13_43_18.488.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-074229-067-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_LogPearsonTypeIII_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-074319-257-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_PearsonTypeIII_RecoversTrueParameters","method":"RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.PearsonTypeIII_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T13:43:19.2571822+00:00","wallSeconds":40.1879109,"testSeconds":37.2438393,"buildSeconds":1.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-074319-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_PearsonTypeIII_RecoversTrueParameters\\haden_HADEN_2026-09-09_13_43_59.214.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-074319-257-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_PearsonTypeIII_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-074359-960-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_Bayesian_Maximum_WeibullGumbelDogLeg_RecoversParent","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskRecoveryTests.Bayesian_Maximum_WeibullGumbelDogLeg_RecoversParent","phase":"stabilized-pass","startedUtc":"2026-09-09T13:43:59.9610203+00:00","wallSeconds":169.2545844,"testSeconds":166.0662515,"buildSeconds":1.45,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-074400-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_Bayesian_Maximum_WeibullGumbelDogLeg_RecoversParent\\haden_HADEN_2026-09-09_13_46_48.770.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-074359-960-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_Bayesian_Maximum_WeibullGumbelDogLeg_RecoversParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Gumbel","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-074649-749-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_Bayesian_Minimum_TwoWeibullDogLeg_RecoversParent","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskRecoveryTests.Bayesian_Minimum_TwoWeibullDogLeg_RecoversParent","phase":"stabilized-pass","startedUtc":"2026-09-09T13:46:49.7492339+00:00","wallSeconds":159.56799,"testSeconds":156.132994,"buildSeconds":1.51,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-074649-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_Bayesian_Minimum_TwoWeibullDogLeg_RecoversParent\\haden_HADEN_2026-09-09_13_49_28.890.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-074649-749-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_Bayesian_Minimum_TwoWeibullDogLeg_RecoversParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Gumbel","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-074929-992-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Maximum_WeibullGumbelDogLeg_RecoversParent","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskRecoveryTests.MLE_Maximum_WeibullGumbelDogLeg_RecoversParent","phase":"stabilized-pass","startedUtc":"2026-09-09T13:49:29.9923945+00:00","wallSeconds":6.7582207,"testSeconds":3.3146165,"buildSeconds":1.7,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-074930-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Maximum_WeibullGumbelDogLeg_RecoversParent\\haden_HADEN_2026-09-09_13_49_36.529.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-074929-992-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Maximum_WeibullGumbelDogLeg_RecoversParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Gumbel","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-074937-317-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskRecoveryTests.MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent","phase":"stabilized-pass","startedUtc":"2026-09-09T13:49:37.3180051+00:00","wallSeconds":3.6675615,"testSeconds":0.481176,"buildSeconds":1.56,"result":"Failed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-074937-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent\\haden_HADEN_2026-09-09_13_49_40.745.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-074937-317-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"C3EB684266ABCBC7027DFD8A9CDD6FF5D502C6970856C33F80A99FDD2532F9DC"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"4E1BFDA0C653DCED738F5909EE3F9FF8150265A04F1105E48FADD009340FC8BC","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Gumbel","Weibull"],"sourceChangedDuringRun":false,"failure":"Assert.IsTrue failed. correlated minimum Weibull(50,1) + Weibull(80,3) dog leg, rho=0.6: MLE failed with status Failure.","note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-074959-327-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskRecoveryTests.MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent","phase":"failure-baseline","startedUtc":"2026-09-09T13:49:59.3294490+00:00","wallSeconds":29.5083139,"testSeconds":26.2508207,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260909-074959-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent\\haden_HADEN_2026-09-09_13_50_28.594.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-074959-327-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"d80bfa8621c48a78cf4ebad7f01326841fac37aa","sourceSha256":"95A9BB4F8927EF0934A13A39B566C1FA30BDB42B2F95AC965746DD122DB39BA0"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"17C77A3DDAE1EF7B39BA3945DB5F6FB61F93F3D1D57FD48AF96248C9A6054F43"},"numericsAssemblySha256":"3E82F983E4795FD50104CBF0F950A7513BDF94540C3397401A8EC5BBF1A6C60F","bestFitAssemblySha256":"67A7E4809110D816C06ED83E4F9068F6E6FA78E490A5486FCEF62A56D7019DC0","verificationAssemblySha256":"542282B3090D914193685DF647EB18960BC31D061A17CACA7E63A2AA92CC9E46","dependencies":["Gumbel","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Current correlated two-Weibull minimum MLE fails immediately with estimator Failure; run unchanged pinned baseline before diagnosis."} +{"runId":"20260909-075114-662-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskRecoveryTests.MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent","phase":"failure-crossover","startedUtc":"2026-09-09T13:51:14.6639376+00:00","wallSeconds":4.9539579,"testSeconds":null,"buildSeconds":4.58,"result":"InfrastructureFailure","trx":null,"log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-075114-662-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent.log","exactIdentityVerified":false,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"d80bfa8621c48a78cf4ebad7f01326841fac37aa","sourceSha256":"95A9BB4F8927EF0934A13A39B566C1FA30BDB42B2F95AC965746DD122DB39BA0"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":null,"bestFitAssemblySha256":null,"verificationAssemblySha256":null,"dependencies":["Gumbel","Weibull"],"sourceChangedDuringRun":false,"failure":"System.Management.Automation.RuntimeException: Verification project build failed.","note":"Pinned baseline passes26.251s; isolate current BestFit integration using baseline Numerics before dependent-density repair."} +{"runId":"20260909-080407-470-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskRecoveryTests.MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent","phase":"stabilized-pass","startedUtc":"2026-09-09T14:04:07.4722619+00:00","wallSeconds":100.4076791,"testSeconds":95.1716477,"buildSeconds":3.31,"result":"Failed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-080407-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent\\haden_HADEN_2026-09-09_14_05_47.447.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-080407-470-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"73FFD013D588110C7393F5E2ED89862D5B1A18F5CCA93B54E3A0927E00A9D954"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"33F3BA8DC437CD3C4CBC02E0D3FF295EC7EFB3CB6FBB3CB883426406ECC36283","bestFitAssemblySha256":"1CE36EE8E87FCCC4B808AE09914C15E1335DE7F931EE9CAAAFA8F55385D82382","verificationAssemblySha256":"C17D1F2BAB95DD4D006565CC68FD2288DB7B95F2163AF8F3F4DECD345011F459","dependencies":["Gumbel","Weibull"],"sourceChangedDuringRun":false,"failure":"Assert.Fail failed. correlated minimum Weibull(50,1) + Weibull(80,3) dog leg, rho=0.6: full-likelihood observed information is not positive definite: Cholesky Decomposition failed. The input matrix is not positive-definite.","note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-082903-209-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskRecoveryTests.MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent","phase":"stabilized-pass","startedUtc":"2026-09-09T14:29:03.2116573+00:00","wallSeconds":36.6443052,"testSeconds":30.2787849,"buildSeconds":4.55,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-082903-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent\\haden_HADEN_2026-09-09_14_29_39.608.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-082903-209-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"0AE4C774841C4AD67E6FCD2473BFB33AFC1461F4A0A64C5F9B6FC5F1441467BF"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"F200E9D1DA7488C3D7AC796172E7034126C27C056CC56E0472B450805F699CCD","bestFitAssemblySha256":"F480E73219FDEB42D859FEA21ADC4E8A4451BE2C16CE1F360326CEF6F5621C92","verificationAssemblySha256":"77A3C9121C4B319E1BB1D1116A8FA67824B371AC4A0E77DF27D299755CF68411","dependencies":["Gumbel","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-082940-635-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_CorrelationMatrixSimulation_MatchesRankDependenceAndCompositeCdf","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskDependencyVerificationTests.Test_CorrelationMatrixSimulation_MatchesRankDependenceAndCompositeCdf","phase":"stabilized-pass","startedUtc":"2026-09-09T14:29:40.6357892+00:00","wallSeconds":3.6418383,"testSeconds":0.1279684,"buildSeconds":1.82,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-082940-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_CorrelationMatrixSimulation_MatchesRankDependenceAndCompositeCdf\\haden_HADEN_2026-09-09_14_29_44.034.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-082940-635-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_CorrelationMatrixSimulation_MatchesRankDependenceAndCompositeCdf.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"0AE4C774841C4AD67E6FCD2473BFB33AFC1461F4A0A64C5F9B6FC5F1441467BF"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"F200E9D1DA7488C3D7AC796172E7034126C27C056CC56E0472B450805F699CCD","bestFitAssemblySha256":"F480E73219FDEB42D859FEA21ADC4E8A4451BE2C16CE1F360326CEF6F5621C92","verificationAssemblySha256":"77A3C9121C4B319E1BB1D1116A8FA67824B371AC4A0E77DF27D299755CF68411","dependencies":["Normal","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-082944-816-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_IndependentSimulation_MatchesRankDependenceAndCompositeCdf","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskDependencyVerificationTests.Test_IndependentSimulation_MatchesRankDependenceAndCompositeCdf","phase":"stabilized-pass","startedUtc":"2026-09-09T14:29:44.8162739+00:00","wallSeconds":2.9636628,"testSeconds":0.0565563,"buildSeconds":1.37,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-082944-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_IndependentSimulation_MatchesRankDependenceAndCompositeCdf\\haden_HADEN_2026-09-09_14_29_47.559.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-082944-816-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_IndependentSimulation_MatchesRankDependenceAndCompositeCdf.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"0AE4C774841C4AD67E6FCD2473BFB33AFC1461F4A0A64C5F9B6FC5F1441467BF"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"F200E9D1DA7488C3D7AC796172E7034126C27C056CC56E0472B450805F699CCD","bestFitAssemblySha256":"F480E73219FDEB42D859FEA21ADC4E8A4451BE2C16CE1F360326CEF6F5621C92","verificationAssemblySha256":"77A3C9121C4B319E1BB1D1116A8FA67824B371AC4A0E77DF27D299755CF68411","dependencies":["Normal","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-082948-331-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_PerfectlyNegativeSimulation_MatchesRankDependenceAndCompositeCdf","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskDependencyVerificationTests.Test_PerfectlyNegativeSimulation_MatchesRankDependenceAndCompositeCdf","phase":"stabilized-pass","startedUtc":"2026-09-09T14:29:48.3315974+00:00","wallSeconds":3.1115231,"testSeconds":0.1078969,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-082948-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_PerfectlyNegativeSimulation_MatchesRankDependenceAndCompositeCdf\\haden_HADEN_2026-09-09_14_29_51.213.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-082948-331-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_PerfectlyNegativeSimulation_MatchesRankDependenceAndCompositeCdf.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"0AE4C774841C4AD67E6FCD2473BFB33AFC1461F4A0A64C5F9B6FC5F1441467BF"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"F200E9D1DA7488C3D7AC796172E7034126C27C056CC56E0472B450805F699CCD","bestFitAssemblySha256":"F480E73219FDEB42D859FEA21ADC4E8A4451BE2C16CE1F360326CEF6F5621C92","verificationAssemblySha256":"77A3C9121C4B319E1BB1D1116A8FA67824B371AC4A0E77DF27D299755CF68411","dependencies":["Normal","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-082951-966-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_PerfectlyPositiveSimulation_MatchesRankDependenceAndCompositeCdf","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskDependencyVerificationTests.Test_PerfectlyPositiveSimulation_MatchesRankDependenceAndCompositeCdf","phase":"stabilized-pass","startedUtc":"2026-09-09T14:29:51.9667021+00:00","wallSeconds":3.1712725,"testSeconds":0.051475,"buildSeconds":1.6,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-082952-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_PerfectlyPositiveSimulation_MatchesRankDependenceAndCompositeCdf\\haden_HADEN_2026-09-09_14_29_54.920.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-082951-966-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_PerfectlyPositiveSimulation_MatchesRankDependenceAndCompositeCdf.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"0AE4C774841C4AD67E6FCD2473BFB33AFC1461F4A0A64C5F9B6FC5F1441467BF"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"F200E9D1DA7488C3D7AC796172E7034126C27C056CC56E0472B450805F699CCD","bestFitAssemblySha256":"F480E73219FDEB42D859FEA21ADC4E8A4451BE2C16CE1F360326CEF6F5621C92","verificationAssemblySha256":"77A3C9121C4B319E1BB1D1116A8FA67824B371AC4A0E77DF27D299755CF68411","dependencies":["Normal","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-082955-699-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_TwoWeibullDogLeg_RecoversParent","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskRecoveryTests.MLE_Minimum_TwoWeibullDogLeg_RecoversParent","phase":"stabilized-pass","startedUtc":"2026-09-09T14:29:55.6993108+00:00","wallSeconds":5.9978409,"testSeconds":2.9816636,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-082955-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_TwoWeibullDogLeg_RecoversParent\\haden_HADEN_2026-09-09_14_30_01.471.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-082955-699-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_TwoWeibullDogLeg_RecoversParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"0AE4C774841C4AD67E6FCD2473BFB33AFC1461F4A0A64C5F9B6FC5F1441467BF"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"F200E9D1DA7488C3D7AC796172E7034126C27C056CC56E0472B450805F699CCD","bestFitAssemblySha256":"F480E73219FDEB42D859FEA21ADC4E8A4451BE2C16CE1F360326CEF6F5621C92","verificationAssemblySha256":"77A3C9121C4B319E1BB1D1116A8FA67824B371AC4A0E77DF27D299755CF68411","dependencies":["Gumbel","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-083002-222-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_EqualWeightModelAverage_EndToEndPredictiveRecovery","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositePredictiveRecoveryTests.EqualWeightModelAverage_EndToEndPredictiveRecovery","phase":"stabilized-pass","startedUtc":"2026-09-09T14:30:02.2222189+00:00","wallSeconds":13.5984348,"testSeconds":10.4877058,"buildSeconds":1.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-083002-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_EqualWeightModelAverage_EndToEndPredictiveRecovery\\haden_HADEN_2026-09-09_14_30_15.589.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-083002-222-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_EqualWeightModelAverage_EndToEndPredictiveRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"0AE4C774841C4AD67E6FCD2473BFB33AFC1461F4A0A64C5F9B6FC5F1441467BF"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"F200E9D1DA7488C3D7AC796172E7034126C27C056CC56E0472B450805F699CCD","bestFitAssemblySha256":"F480E73219FDEB42D859FEA21ADC4E8A4451BE2C16CE1F360326CEF6F5621C92","verificationAssemblySha256":"77A3C9121C4B319E1BB1D1116A8FA67824B371AC4A0E77DF27D299755CF68411","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-083016-340-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MaximumComposite_EndToEndPredictiveRecovery","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositePredictiveRecoveryTests.MaximumComposite_EndToEndPredictiveRecovery","phase":"stabilized-pass","startedUtc":"2026-09-09T14:30:16.3409494+00:00","wallSeconds":14.4258929,"testSeconds":11.2603272,"buildSeconds":1.55,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-083016-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MaximumComposite_EndToEndPredictiveRecovery\\haden_HADEN_2026-09-09_14_30_30.524.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-083016-340-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MaximumComposite_EndToEndPredictiveRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"0AE4C774841C4AD67E6FCD2473BFB33AFC1461F4A0A64C5F9B6FC5F1441467BF"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"F200E9D1DA7488C3D7AC796172E7034126C27C056CC56E0472B450805F699CCD","bestFitAssemblySha256":"F480E73219FDEB42D859FEA21ADC4E8A4451BE2C16CE1F360326CEF6F5621C92","verificationAssemblySha256":"77A3C9121C4B319E1BB1D1116A8FA67824B371AC4A0E77DF27D299755CF68411","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-083031-378-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MinimumComposite_EndToEndPredictiveRecovery","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositePredictiveRecoveryTests.MinimumComposite_EndToEndPredictiveRecovery","phase":"stabilized-pass","startedUtc":"2026-09-09T14:30:31.3781591+00:00","wallSeconds":14.5655193,"testSeconds":11.4228934,"buildSeconds":1.57,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-083031-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MinimumComposite_EndToEndPredictiveRecovery\\haden_HADEN_2026-09-09_14_30_45.712.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-083031-378-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MinimumComposite_EndToEndPredictiveRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"0AE4C774841C4AD67E6FCD2473BFB33AFC1461F4A0A64C5F9B6FC5F1441467BF"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"F200E9D1DA7488C3D7AC796172E7034126C27C056CC56E0472B450805F699CCD","bestFitAssemblySha256":"F480E73219FDEB42D859FEA21ADC4E8A4451BE2C16CE1F360326CEF6F5621C92","verificationAssemblySha256":"77A3C9121C4B319E1BB1D1116A8FA67824B371AC4A0E77DF27D299755CF68411","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-083046-521-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MixtureComposite_EndToEndPredictiveRecovery","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositePredictiveRecoveryTests.MixtureComposite_EndToEndPredictiveRecovery","phase":"stabilized-pass","startedUtc":"2026-09-09T14:30:46.5218492+00:00","wallSeconds":14.984552,"testSeconds":11.8454303,"buildSeconds":1.53,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-083046-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MixtureComposite_EndToEndPredictiveRecovery\\haden_HADEN_2026-09-09_14_31_01.266.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-083046-521-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MixtureComposite_EndToEndPredictiveRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"0AE4C774841C4AD67E6FCD2473BFB33AFC1461F4A0A64C5F9B6FC5F1441467BF"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"F200E9D1DA7488C3D7AC796172E7034126C27C056CC56E0472B450805F699CCD","bestFitAssemblySha256":"F480E73219FDEB42D859FEA21ADC4E8A4451BE2C16CE1F360326CEF6F5621C92","verificationAssemblySha256":"77A3C9121C4B319E1BB1D1116A8FA67824B371AC4A0E77DF27D299755CF68411","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-083102-184-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_Recovery_Parity","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.NormalMixture2D_Recovery_Parity","phase":"stabilized-pass","startedUtc":"2026-09-09T14:31:02.1843370+00:00","wallSeconds":3.5697166,"testSeconds":0.3060004,"buildSeconds":1.62,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-083102-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_Recovery_Parity\\haden_HADEN_2026-09-09_14_31_05.533.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-083102-184-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_Recovery_Parity.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"0AE4C774841C4AD67E6FCD2473BFB33AFC1461F4A0A64C5F9B6FC5F1441467BF"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"F200E9D1DA7488C3D7AC796172E7034126C27C056CC56E0472B450805F699CCD","bestFitAssemblySha256":"F480E73219FDEB42D859FEA21ADC4E8A4451BE2C16CE1F360326CEF6F5621C92","verificationAssemblySha256":"77A3C9121C4B319E1BB1D1116A8FA67824B371AC4A0E77DF27D299755CF68411","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-083125-833-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskRecoveryTests.MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent","phase":"paired-runtime-baseline","startedUtc":"2026-09-09T14:31:25.8355637+00:00","wallSeconds":27.8176611,"testSeconds":24.5044815,"buildSeconds":1.55,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260909-083125-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent\\haden_HADEN_2026-09-09_14_31_53.317.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-083125-833-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"d80bfa8621c48a78cf4ebad7f01326841fac37aa","sourceSha256":"95A9BB4F8927EF0934A13A39B566C1FA30BDB42B2F95AC965746DD122DB39BA0"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"17C77A3DDAE1EF7B39BA3945DB5F6FB61F93F3D1D57FD48AF96248C9A6054F43"},"numericsAssemblySha256":"3E82F983E4795FD50104CBF0F950A7513BDF94540C3397401A8EC5BBF1A6C60F","bestFitAssemblySha256":"67A7E4809110D816C06ED83E4F9068F6E6FA78E490A5486FCEF62A56D7019DC0","verificationAssemblySha256":"542282B3090D914193685DF647EB18960BC31D061A17CACA7E63A2AA92CC9E46","dependencies":["Gumbel","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Adjacent uncontended baseline after narrow correlated-candidate repair; all scientific settings unchanged."} +{"runId":"20260909-083154-469-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskRecoveryTests.MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent","phase":"paired-runtime-current","startedUtc":"2026-09-09T14:31:54.4700244+00:00","wallSeconds":30.8416231,"testSeconds":27.9059894,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-083154-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent\\haden_HADEN_2026-09-09_14_32_25.087.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-083154-469-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"0AE4C774841C4AD67E6FCD2473BFB33AFC1461F4A0A64C5F9B6FC5F1441467BF"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"F200E9D1DA7488C3D7AC796172E7034126C27C056CC56E0472B450805F699CCD","bestFitAssemblySha256":"F480E73219FDEB42D859FEA21ADC4E8A4451BE2C16CE1F360326CEF6F5621C92","verificationAssemblySha256":"77A3C9121C4B319E1BB1D1116A8FA67824B371AC4A0E77DF27D299755CF68411","dependencies":["Gumbel","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Adjacent uncontended current after pinned baseline; retain hardened stencil and restored rejection."} +{"runId":"20260909-084027-571-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskRecoveryTests.MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent","phase":"step-reuse-recovery","startedUtc":"2026-09-09T14:40:27.5729487+00:00","wallSeconds":33.617158,"testSeconds":29.1844105,"buildSeconds":2.63,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-084027-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent\\haden_HADEN_2026-09-09_14_41_00.959.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-084027-571-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"3166383F2ACC81DBDB8EF412DFFC97E374CD6ADD80B0A44DF9B1E881F7C9DCA8"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"3C6EA3891E0A30E58A55532B903E7CE6CB01E5E913ADFE72B14D950A70BDCACF","bestFitAssemblySha256":"388F0F78D1D0FD86718C2C1258711FEA5DAB87B5E7A223B904B64BA6A5A8CCB8","verificationAssemblySha256":"FDBFEA8D97AAC2BF3098094FC9D8DA079F2AC7C5DA3CC5C9C11775F7FD83AC4B","dependencies":["Gumbel","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Exact final recovery after identical density-step reuse; compare adjacent pinned24.504/current-before27.906 seconds."} +{"runId":"20260909-084501-598-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskRecoveryTests.MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent","phase":"internal-allocation-recovery","startedUtc":"2026-09-09T14:45:01.5996586+00:00","wallSeconds":31.6093576,"testSeconds":27.3577403,"buildSeconds":2.47,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-084501-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent\\haden_HADEN_2026-09-09_14_45_32.974.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-084501-598-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"18419F4324D0EB3E448CCD09A96D97A098F54898CD7A17F892865F8BE5E1660F"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"339F00B016F4534EFE5CF98A5A155357A60F6BDB39EE33B348FB5FE078A26C22","bestFitAssemblySha256":"F60E80BCD59183AE3DF89317CC32D35AC2347A401816C0E09E446A80F75709F8","verificationAssemblySha256":"7C876D7E90E3509B7997F818324341AA576161D23E5A13E8924E636076B5EAD8","dependencies":["Gumbel","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Exact recovery after removing internal collection wrappers, preserving all public behavior and evaluation order."} +{"runId":"20260909-084629-957-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskRecoveryTests.MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent","phase":"final-correlated-runtime-baseline","startedUtc":"2026-09-09T14:46:29.9588595+00:00","wallSeconds":25.9248809,"testSeconds":23.0295768,"buildSeconds":1.37,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260909-084630-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent\\haden_HADEN_2026-09-09_14_46_55.645.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-084629-957-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"d80bfa8621c48a78cf4ebad7f01326841fac37aa","sourceSha256":"95A9BB4F8927EF0934A13A39B566C1FA30BDB42B2F95AC965746DD122DB39BA0"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"17C77A3DDAE1EF7B39BA3945DB5F6FB61F93F3D1D57FD48AF96248C9A6054F43"},"numericsAssemblySha256":"3E82F983E4795FD50104CBF0F950A7513BDF94540C3397401A8EC5BBF1A6C60F","bestFitAssemblySha256":"67A7E4809110D816C06ED83E4F9068F6E6FA78E490A5486FCEF62A56D7019DC0","verificationAssemblySha256":"542282B3090D914193685DF647EB18960BC31D061A17CACA7E63A2AA92CC9E46","dependencies":["Gumbel","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Fresh adjacent pinned comparison after internal allocation repair; prior baseline24.504/26.251 seconds."} +{"runId":"20260909-085129-003-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskRecoveryTests.MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent","phase":"stabilized-pass","startedUtc":"2026-09-09T14:51:29.0057947+00:00","wallSeconds":29.9954346,"testSeconds":26.8851221,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-085129-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent\\haden_HADEN_2026-09-09_14_51_58.766.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-085129-003-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"DE8363A344FBF9AF68C5681AC24F13646E6DDC3BE44220A47D2B08E73A0130AF"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"17BCB9722269CBB8ECF0E32BDB4E25F89549A64B8EBBD35FDED8B6A28D64284B","bestFitAssemblySha256":"F60E80BCD59183AE3DF89317CC32D35AC2347A401816C0E09E446A80F75709F8","verificationAssemblySha256":"7C876D7E90E3509B7997F818324341AA576161D23E5A13E8924E636076B5EAD8","dependencies":["Gumbel","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-085159-675-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_CorrelationMatrixSimulation_MatchesRankDependenceAndCompositeCdf","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskDependencyVerificationTests.Test_CorrelationMatrixSimulation_MatchesRankDependenceAndCompositeCdf","phase":"stabilized-pass","startedUtc":"2026-09-09T14:51:59.6753340+00:00","wallSeconds":3.1402052,"testSeconds":0.1266837,"buildSeconds":1.47,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-085159-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_CorrelationMatrixSimulation_MatchesRankDependenceAndCompositeCdf\\haden_HADEN_2026-09-09_14_52_02.600.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-085159-675-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_CorrelationMatrixSimulation_MatchesRankDependenceAndCompositeCdf.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"DE8363A344FBF9AF68C5681AC24F13646E6DDC3BE44220A47D2B08E73A0130AF"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"17BCB9722269CBB8ECF0E32BDB4E25F89549A64B8EBBD35FDED8B6A28D64284B","bestFitAssemblySha256":"F60E80BCD59183AE3DF89317CC32D35AC2347A401816C0E09E446A80F75709F8","verificationAssemblySha256":"7C876D7E90E3509B7997F818324341AA576161D23E5A13E8924E636076B5EAD8","dependencies":["Normal","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-085203-369-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_IndependentSimulation_MatchesRankDependenceAndCompositeCdf","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskDependencyVerificationTests.Test_IndependentSimulation_MatchesRankDependenceAndCompositeCdf","phase":"stabilized-pass","startedUtc":"2026-09-09T14:52:03.3698601+00:00","wallSeconds":3.3659692,"testSeconds":0.071611,"buildSeconds":1.73,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-085203-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_IndependentSimulation_MatchesRankDependenceAndCompositeCdf\\haden_HADEN_2026-09-09_14_52_06.512.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-085203-369-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_IndependentSimulation_MatchesRankDependenceAndCompositeCdf.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"DE8363A344FBF9AF68C5681AC24F13646E6DDC3BE44220A47D2B08E73A0130AF"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"17BCB9722269CBB8ECF0E32BDB4E25F89549A64B8EBBD35FDED8B6A28D64284B","bestFitAssemblySha256":"F60E80BCD59183AE3DF89317CC32D35AC2347A401816C0E09E446A80F75709F8","verificationAssemblySha256":"7C876D7E90E3509B7997F818324341AA576161D23E5A13E8924E636076B5EAD8","dependencies":["Normal","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-085207-276-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_PerfectlyNegativeSimulation_MatchesRankDependenceAndCompositeCdf","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskDependencyVerificationTests.Test_PerfectlyNegativeSimulation_MatchesRankDependenceAndCompositeCdf","phase":"stabilized-pass","startedUtc":"2026-09-09T14:52:07.2763912+00:00","wallSeconds":3.1372732,"testSeconds":0.1337263,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-085207-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_PerfectlyNegativeSimulation_MatchesRankDependenceAndCompositeCdf\\haden_HADEN_2026-09-09_14_52_10.186.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-085207-276-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_PerfectlyNegativeSimulation_MatchesRankDependenceAndCompositeCdf.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"DE8363A344FBF9AF68C5681AC24F13646E6DDC3BE44220A47D2B08E73A0130AF"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"17BCB9722269CBB8ECF0E32BDB4E25F89549A64B8EBBD35FDED8B6A28D64284B","bestFitAssemblySha256":"F60E80BCD59183AE3DF89317CC32D35AC2347A401816C0E09E446A80F75709F8","verificationAssemblySha256":"7C876D7E90E3509B7997F818324341AA576161D23E5A13E8924E636076B5EAD8","dependencies":["Normal","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-085210-961-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_PerfectlyPositiveSimulation_MatchesRankDependenceAndCompositeCdf","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskDependencyVerificationTests.Test_PerfectlyPositiveSimulation_MatchesRankDependenceAndCompositeCdf","phase":"stabilized-pass","startedUtc":"2026-09-09T14:52:10.9618447+00:00","wallSeconds":3.0326371,"testSeconds":0.0639723,"buildSeconds":1.41,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-085211-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_PerfectlyPositiveSimulation_MatchesRankDependenceAndCompositeCdf\\haden_HADEN_2026-09-09_14_52_13.766.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-085210-961-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_PerfectlyPositiveSimulation_MatchesRankDependenceAndCompositeCdf.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"DE8363A344FBF9AF68C5681AC24F13646E6DDC3BE44220A47D2B08E73A0130AF"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"17BCB9722269CBB8ECF0E32BDB4E25F89549A64B8EBBD35FDED8B6A28D64284B","bestFitAssemblySha256":"F60E80BCD59183AE3DF89317CC32D35AC2347A401816C0E09E446A80F75709F8","verificationAssemblySha256":"7C876D7E90E3509B7997F818324341AA576161D23E5A13E8924E636076B5EAD8","dependencies":["Normal","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-085214-553-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_TwoWeibullDogLeg_RecoversParent","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskRecoveryTests.MLE_Minimum_TwoWeibullDogLeg_RecoversParent","phase":"stabilized-pass","startedUtc":"2026-09-09T14:52:14.5531215+00:00","wallSeconds":6.8097196,"testSeconds":3.7420823,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-085214-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_TwoWeibullDogLeg_RecoversParent\\haden_HADEN_2026-09-09_14_52_21.137.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-085214-553-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_TwoWeibullDogLeg_RecoversParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"DE8363A344FBF9AF68C5681AC24F13646E6DDC3BE44220A47D2B08E73A0130AF"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"17BCB9722269CBB8ECF0E32BDB4E25F89549A64B8EBBD35FDED8B6A28D64284B","bestFitAssemblySha256":"F60E80BCD59183AE3DF89317CC32D35AC2347A401816C0E09E446A80F75709F8","verificationAssemblySha256":"7C876D7E90E3509B7997F818324341AA576161D23E5A13E8924E636076B5EAD8","dependencies":["Gumbel","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-085221-908-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_EqualWeightModelAverage_EndToEndPredictiveRecovery","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositePredictiveRecoveryTests.EqualWeightModelAverage_EndToEndPredictiveRecovery","phase":"stabilized-pass","startedUtc":"2026-09-09T14:52:21.9084404+00:00","wallSeconds":13.6707046,"testSeconds":10.5716501,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-085221-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_EqualWeightModelAverage_EndToEndPredictiveRecovery\\haden_HADEN_2026-09-09_14_52_35.347.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-085221-908-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_EqualWeightModelAverage_EndToEndPredictiveRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"DE8363A344FBF9AF68C5681AC24F13646E6DDC3BE44220A47D2B08E73A0130AF"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"17BCB9722269CBB8ECF0E32BDB4E25F89549A64B8EBBD35FDED8B6A28D64284B","bestFitAssemblySha256":"F60E80BCD59183AE3DF89317CC32D35AC2347A401816C0E09E446A80F75709F8","verificationAssemblySha256":"7C876D7E90E3509B7997F818324341AA576161D23E5A13E8924E636076B5EAD8","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-085236-138-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MaximumComposite_EndToEndPredictiveRecovery","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositePredictiveRecoveryTests.MaximumComposite_EndToEndPredictiveRecovery","phase":"stabilized-pass","startedUtc":"2026-09-09T14:52:36.1384382+00:00","wallSeconds":13.0996507,"testSeconds":10.2639872,"buildSeconds":1.3,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-085236-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MaximumComposite_EndToEndPredictiveRecovery\\haden_HADEN_2026-09-09_14_52_49.010.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-085236-138-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MaximumComposite_EndToEndPredictiveRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"DE8363A344FBF9AF68C5681AC24F13646E6DDC3BE44220A47D2B08E73A0130AF"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"17BCB9722269CBB8ECF0E32BDB4E25F89549A64B8EBBD35FDED8B6A28D64284B","bestFitAssemblySha256":"F60E80BCD59183AE3DF89317CC32D35AC2347A401816C0E09E446A80F75709F8","verificationAssemblySha256":"7C876D7E90E3509B7997F818324341AA576161D23E5A13E8924E636076B5EAD8","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-085249-818-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MinimumComposite_EndToEndPredictiveRecovery","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositePredictiveRecoveryTests.MinimumComposite_EndToEndPredictiveRecovery","phase":"stabilized-pass","startedUtc":"2026-09-09T14:52:49.8181329+00:00","wallSeconds":14.7855934,"testSeconds":11.7300834,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-085249-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MinimumComposite_EndToEndPredictiveRecovery\\haden_HADEN_2026-09-09_14_53_04.368.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-085249-818-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MinimumComposite_EndToEndPredictiveRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"DE8363A344FBF9AF68C5681AC24F13646E6DDC3BE44220A47D2B08E73A0130AF"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"17BCB9722269CBB8ECF0E32BDB4E25F89549A64B8EBBD35FDED8B6A28D64284B","bestFitAssemblySha256":"F60E80BCD59183AE3DF89317CC32D35AC2347A401816C0E09E446A80F75709F8","verificationAssemblySha256":"7C876D7E90E3509B7997F818324341AA576161D23E5A13E8924E636076B5EAD8","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-085305-173-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MixtureComposite_EndToEndPredictiveRecovery","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositePredictiveRecoveryTests.MixtureComposite_EndToEndPredictiveRecovery","phase":"stabilized-pass","startedUtc":"2026-09-09T14:53:05.1733548+00:00","wallSeconds":13.8742137,"testSeconds":10.7698368,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-085305-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MixtureComposite_EndToEndPredictiveRecovery\\haden_HADEN_2026-09-09_14_53_18.809.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-085305-173-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MixtureComposite_EndToEndPredictiveRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"DE8363A344FBF9AF68C5681AC24F13646E6DDC3BE44220A47D2B08E73A0130AF"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"17BCB9722269CBB8ECF0E32BDB4E25F89549A64B8EBBD35FDED8B6A28D64284B","bestFitAssemblySha256":"F60E80BCD59183AE3DF89317CC32D35AC2347A401816C0E09E446A80F75709F8","verificationAssemblySha256":"7C876D7E90E3509B7997F818324341AA576161D23E5A13E8924E636076B5EAD8","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-085319-579-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_Recovery_Parity","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.NormalMixture2D_Recovery_Parity","phase":"stabilized-pass","startedUtc":"2026-09-09T14:53:19.5790808+00:00","wallSeconds":3.4874961,"testSeconds":0.253097,"buildSeconds":1.66,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-085319-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_Recovery_Parity\\haden_HADEN_2026-09-09_14_53_22.838.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-085319-579-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_Recovery_Parity.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"DE8363A344FBF9AF68C5681AC24F13646E6DDC3BE44220A47D2B08E73A0130AF"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"17BCB9722269CBB8ECF0E32BDB4E25F89549A64B8EBBD35FDED8B6A28D64284B","bestFitAssemblySha256":"F60E80BCD59183AE3DF89317CC32D35AC2347A401816C0E09E446A80F75709F8","verificationAssemblySha256":"7C876D7E90E3509B7997F818324341AA576161D23E5A13E8924E636076B5EAD8","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-085323-615-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture3D_BayesianRecovery","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.NormalMixture3D_BayesianRecovery","phase":"stabilized-pass","startedUtc":"2026-09-09T14:53:23.6156139+00:00","wallSeconds":320.6347301,"testSeconds":317.3038304,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-085323-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture3D_BayesianRecovery\\haden_HADEN_2026-09-09_14_58_43.829.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-085323-615-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture3D_BayesianRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"DE8363A344FBF9AF68C5681AC24F13646E6DDC3BE44220A47D2B08E73A0130AF"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"17BCB9722269CBB8ECF0E32BDB4E25F89549A64B8EBBD35FDED8B6A28D64284B","bestFitAssemblySha256":"F60E80BCD59183AE3DF89317CC32D35AC2347A401816C0E09E446A80F75709F8","verificationAssemblySha256":"7C876D7E90E3509B7997F818324341AA576161D23E5A13E8924E636076B5EAD8","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-090140-110-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskRecoveryTests.MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent","phase":"stabilized-pass","startedUtc":"2026-09-09T15:01:40.1125226+00:00","wallSeconds":27.2604537,"testSeconds":22.6480765,"buildSeconds":2.8,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-090140-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent\\haden_HADEN_2026-09-09_15_02_07.133.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-090140-110-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-090208-103-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_CorrelationMatrixSimulation_MatchesRankDependenceAndCompositeCdf","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskDependencyVerificationTests.Test_CorrelationMatrixSimulation_MatchesRankDependenceAndCompositeCdf","phase":"stabilized-pass","startedUtc":"2026-09-09T15:02:08.1033086+00:00","wallSeconds":3.0141749,"testSeconds":0.1155679,"buildSeconds":1.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-090208-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_CorrelationMatrixSimulation_MatchesRankDependenceAndCompositeCdf\\haden_HADEN_2026-09-09_15_02_10.897.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-090208-103-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_CorrelationMatrixSimulation_MatchesRankDependenceAndCompositeCdf.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-090211-641-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_IndependentSimulation_MatchesRankDependenceAndCompositeCdf","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskDependencyVerificationTests.Test_IndependentSimulation_MatchesRankDependenceAndCompositeCdf","phase":"stabilized-pass","startedUtc":"2026-09-09T15:02:11.6418551+00:00","wallSeconds":2.7648528,"testSeconds":0.0555132,"buildSeconds":1.25,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-090211-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_IndependentSimulation_MatchesRankDependenceAndCompositeCdf\\haden_HADEN_2026-09-09_15_02_14.186.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-090211-641-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_IndependentSimulation_MatchesRankDependenceAndCompositeCdf.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-090214-936-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_PerfectlyNegativeSimulation_MatchesRankDependenceAndCompositeCdf","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskDependencyVerificationTests.Test_PerfectlyNegativeSimulation_MatchesRankDependenceAndCompositeCdf","phase":"stabilized-pass","startedUtc":"2026-09-09T15:02:14.9361440+00:00","wallSeconds":3.0039398,"testSeconds":0.1121326,"buildSeconds":1.42,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-090214-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_PerfectlyNegativeSimulation_MatchesRankDependenceAndCompositeCdf\\haden_HADEN_2026-09-09_15_02_17.712.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-090214-936-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_PerfectlyNegativeSimulation_MatchesRankDependenceAndCompositeCdf.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-090218-456-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_PerfectlyPositiveSimulation_MatchesRankDependenceAndCompositeCdf","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskDependencyVerificationTests.Test_PerfectlyPositiveSimulation_MatchesRankDependenceAndCompositeCdf","phase":"stabilized-pass","startedUtc":"2026-09-09T15:02:18.4568183+00:00","wallSeconds":2.9312111,"testSeconds":0.0531186,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-090218-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_PerfectlyPositiveSimulation_MatchesRankDependenceAndCompositeCdf\\haden_HADEN_2026-09-09_15_02_21.156.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-090218-456-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_PerfectlyPositiveSimulation_MatchesRankDependenceAndCompositeCdf.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-090221-920-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_TwoWeibullDogLeg_RecoversParent","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskRecoveryTests.MLE_Minimum_TwoWeibullDogLeg_RecoversParent","phase":"stabilized-pass","startedUtc":"2026-09-09T15:02:21.9210556+00:00","wallSeconds":5.2696526,"testSeconds":2.5756382,"buildSeconds":1.23,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-090221-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_TwoWeibullDogLeg_RecoversParent\\haden_HADEN_2026-09-09_15_02_26.970.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-090221-920-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_TwoWeibullDogLeg_RecoversParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-090227-704-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_EqualWeightModelAverage_EndToEndPredictiveRecovery","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositePredictiveRecoveryTests.EqualWeightModelAverage_EndToEndPredictiveRecovery","phase":"stabilized-pass","startedUtc":"2026-09-09T15:02:27.7049933+00:00","wallSeconds":17.0614913,"testSeconds":14.1934005,"buildSeconds":1.38,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-090227-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_EqualWeightModelAverage_EndToEndPredictiveRecovery\\haden_HADEN_2026-09-09_15_02_44.526.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-090227-704-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_EqualWeightModelAverage_EndToEndPredictiveRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-090245-283-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MaximumComposite_EndToEndPredictiveRecovery","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositePredictiveRecoveryTests.MaximumComposite_EndToEndPredictiveRecovery","phase":"stabilized-pass","startedUtc":"2026-09-09T15:02:45.2836605+00:00","wallSeconds":16.9137612,"testSeconds":13.9966753,"buildSeconds":1.42,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-090245-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MaximumComposite_EndToEndPredictiveRecovery\\haden_HADEN_2026-09-09_15_03_01.961.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-090245-283-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MaximumComposite_EndToEndPredictiveRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-090302-831-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MinimumComposite_EndToEndPredictiveRecovery","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositePredictiveRecoveryTests.MinimumComposite_EndToEndPredictiveRecovery","phase":"stabilized-pass","startedUtc":"2026-09-09T15:03:02.8314619+00:00","wallSeconds":17.4335107,"testSeconds":14.50693,"buildSeconds":1.31,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-090302-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MinimumComposite_EndToEndPredictiveRecovery\\haden_HADEN_2026-09-09_15_03_19.996.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-090302-831-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MinimumComposite_EndToEndPredictiveRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-090320-872-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MixtureComposite_EndToEndPredictiveRecovery","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositePredictiveRecoveryTests.MixtureComposite_EndToEndPredictiveRecovery","phase":"stabilized-pass","startedUtc":"2026-09-09T15:03:20.8725831+00:00","wallSeconds":17.2742322,"testSeconds":14.1768836,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-090320-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MixtureComposite_EndToEndPredictiveRecovery\\haden_HADEN_2026-09-09_15_03_37.903.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-090320-872-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MixtureComposite_EndToEndPredictiveRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-090338-751-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_Recovery_Parity","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.NormalMixture2D_Recovery_Parity","phase":"stabilized-pass","startedUtc":"2026-09-09T15:03:38.7518480+00:00","wallSeconds":3.3550834,"testSeconds":0.2930306,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-090338-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_Recovery_Parity\\haden_HADEN_2026-09-09_15_03_41.889.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-090338-751-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_Recovery_Parity.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-090342-660-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture3D_BayesianRecovery","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.NormalMixture3D_BayesianRecovery","phase":"stabilized-pass","startedUtc":"2026-09-09T15:03:42.6609264+00:00","wallSeconds":300.2184082,"testSeconds":296.9759115,"buildSeconds":1.51,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-090342-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture3D_BayesianRecovery\\haden_HADEN_2026-09-09_15_08_42.429.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-090342-660-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture3D_BayesianRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-090843-446-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture3D_Recovery_Parity","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.NormalMixture3D_Recovery_Parity","phase":"stabilized-pass","startedUtc":"2026-09-09T15:08:43.4463784+00:00","wallSeconds":4.6189377,"testSeconds":1.6261329,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-090843-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture3D_Recovery_Parity\\haden_HADEN_2026-09-09_15_08_47.844.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-090843-446-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture3D_Recovery_Parity.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-090848-578-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_ZeroInflatedNormalMixture2D_BayesianRecovery","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.ZeroInflatedNormalMixture2D_BayesianRecovery","phase":"stabilized-pass","startedUtc":"2026-09-09T15:08:48.5783251+00:00","wallSeconds":168.2012009,"testSeconds":165.0718094,"buildSeconds":1.41,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-090848-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_ZeroInflatedNormalMixture2D_BayesianRecovery\\haden_HADEN_2026-09-09_15_11_36.331.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-090848-578-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_ZeroInflatedNormalMixture2D_BayesianRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-091137-319-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_ZeroInflatedNormalMixture2D_Recovery_Parity","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.ZeroInflatedNormalMixture2D_Recovery_Parity","phase":"stabilized-pass","startedUtc":"2026-09-09T15:11:37.3194389+00:00","wallSeconds":3.5517768,"testSeconds":0.3988125,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-091137-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_ZeroInflatedNormalMixture2D_Recovery_Parity\\haden_HADEN_2026-09-09_15_11_40.644.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-091137-319-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_ZeroInflatedNormalMixture2D_Recovery_Parity.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-091141-394-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverAMHCopulaParameters","method":"RMC.BestFit.Verification.Bivariate.BivariateAnalysisParameterRecoveryTests.RecoverAMHCopulaParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:11:41.3949349+00:00","wallSeconds":32.6252156,"testSeconds":29.5058922,"buildSeconds":1.47,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-091141-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverAMHCopulaParameters\\haden_HADEN_2026-09-09_15_12_13.741.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-091141-394-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverAMHCopulaParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-091214-663-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverClaytonCopulaParameters","method":"RMC.BestFit.Verification.Bivariate.BivariateAnalysisParameterRecoveryTests.RecoverClaytonCopulaParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:12:14.6639424+00:00","wallSeconds":51.5748233,"testSeconds":48.4911592,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-091214-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverClaytonCopulaParameters\\haden_HADEN_2026-09-09_15_13_05.987.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-091214-663-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverClaytonCopulaParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-091306-832-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverFrankCopulaParameters","method":"RMC.BestFit.Verification.Bivariate.BivariateAnalysisParameterRecoveryTests.RecoverFrankCopulaParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:13:06.8329540+00:00","wallSeconds":36.014438,"testSeconds":32.9148679,"buildSeconds":1.52,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-091306-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverFrankCopulaParameters\\haden_HADEN_2026-09-09_15_13_42.597.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-091306-832-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverFrankCopulaParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-091343-389-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverGumbelCopulaParameters","method":"RMC.BestFit.Verification.Bivariate.BivariateAnalysisParameterRecoveryTests.RecoverGumbelCopulaParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:13:43.3892231+00:00","wallSeconds":61.664968,"testSeconds":58.5705305,"buildSeconds":1.52,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-091343-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverGumbelCopulaParameters\\haden_HADEN_2026-09-09_15_14_44.792.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-091343-389-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverGumbelCopulaParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-091445-555-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverJoeCopulaParameters","method":"RMC.BestFit.Verification.Bivariate.BivariateAnalysisParameterRecoveryTests.RecoverJoeCopulaParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:14:45.5557370+00:00","wallSeconds":68.6004477,"testSeconds":65.747813,"buildSeconds":1.35,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-091445-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverJoeCopulaParameters\\haden_HADEN_2026-09-09_15_15_53.890.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-091445-555-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverJoeCopulaParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-091554-732-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverNormalCopulaParameters","method":"RMC.BestFit.Verification.Bivariate.BivariateAnalysisParameterRecoveryTests.RecoverNormalCopulaParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:15:54.7326788+00:00","wallSeconds":42.7153662,"testSeconds":39.6527584,"buildSeconds":1.47,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-091554-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverNormalCopulaParameters\\haden_HADEN_2026-09-09_15_16_37.188.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-091554-732-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverNormalCopulaParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-091637-988-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverStudentTCopulaParametersWithMaximumLikelihood","method":"RMC.BestFit.Verification.Bivariate.BivariateAnalysisParameterRecoveryTests.RecoverStudentTCopulaParametersWithMaximumLikelihood","phase":"stabilized-pass","startedUtc":"2026-09-09T15:16:37.9890153+00:00","wallSeconds":30.5202195,"testSeconds":27.5284221,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-091638-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverStudentTCopulaParametersWithMaximumLikelihood\\haden_HADEN_2026-09-09_15_17_08.285.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-091637-988-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverStudentTCopulaParametersWithMaximumLikelihood.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-091709-050-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_ExponentialLinearCombination_ParentResponseInsidePredictiveBands","method":"RMC.BestFit.Verification.Bivariate.CoincidentFrequencyAnalysisTests.ExponentialLinearCombination_ParentResponseInsidePredictiveBands","phase":"stabilized-pass","startedUtc":"2026-09-09T15:17:09.0506596+00:00","wallSeconds":43.1703014,"testSeconds":40.16015,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-091709-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_ExponentialLinearCombination_ParentResponseInsidePredictiveBands\\haden_HADEN_2026-09-09_15_17_51.976.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-091709-050-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_ExponentialLinearCombination_ParentResponseInsidePredictiveBands.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-091752-736-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoNegative_MatchesClosedForm","method":"RMC.BestFit.Verification.Bivariate.CoincidentFrequencyAnalysisTests.SumOfNormals_RhoNegative_MatchesClosedForm","phase":"stabilized-pass","startedUtc":"2026-09-09T15:17:52.7369092+00:00","wallSeconds":42.5061989,"testSeconds":39.5517491,"buildSeconds":1.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-091752-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoNegative_MatchesClosedForm\\haden_HADEN_2026-09-09_15_18_35.006.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-091752-736-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoNegative_MatchesClosedForm.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-091835-809-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoPositive_MatchesClosedForm","method":"RMC.BestFit.Verification.Bivariate.CoincidentFrequencyAnalysisTests.SumOfNormals_RhoPositive_MatchesClosedForm","phase":"stabilized-pass","startedUtc":"2026-09-09T15:18:35.8093253+00:00","wallSeconds":43.6617741,"testSeconds":40.5091593,"buildSeconds":1.54,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-091835-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoPositive_MatchesClosedForm\\haden_HADEN_2026-09-09_15_19_19.236.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-091835-809-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoPositive_MatchesClosedForm.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-091919-962-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoZero_MatchesClosedForm","method":"RMC.BestFit.Verification.Bivariate.CoincidentFrequencyAnalysisTests.SumOfNormals_RhoZero_MatchesClosedForm","phase":"stabilized-pass","startedUtc":"2026-09-09T15:19:19.9621576+00:00","wallSeconds":41.0503532,"testSeconds":38.2383602,"buildSeconds":1.35,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-091920-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoZero_MatchesClosedForm\\haden_HADEN_2026-09-09_15_20_00.753.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-091919-962-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoZero_MatchesClosedForm.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092001-512-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_AliMikhailHaq_InferenceFromMargins_MatchesIndependentOptimum","method":"RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.AliMikhailHaq_InferenceFromMargins_MatchesIndependentOptimum","phase":"stabilized-pass","startedUtc":"2026-09-09T15:20:01.5121278+00:00","wallSeconds":3.0433988,"testSeconds":0.0690916,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092001-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_AliMikhailHaq_InferenceFromMargins_MatchesIndependentOptimum\\haden_HADEN_2026-09-09_15_20_04.327.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092001-512-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_AliMikhailHaq_InferenceFromMargins_MatchesIndependentOptimum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092005-081-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_AliMikhailHaq_PseudoLikelihood_MatchesIndependentOptimum","method":"RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.AliMikhailHaq_PseudoLikelihood_MatchesIndependentOptimum","phase":"stabilized-pass","startedUtc":"2026-09-09T15:20:05.0816449+00:00","wallSeconds":3.0258913,"testSeconds":0.0693629,"buildSeconds":1.45,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092005-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_AliMikhailHaq_PseudoLikelihood_MatchesIndependentOptimum\\haden_HADEN_2026-09-09_15_20_07.884.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092005-081-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_AliMikhailHaq_PseudoLikelihood_MatchesIndependentOptimum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092008-638-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Clayton_InferenceFromMargins_MatchesIndependentOptimum","method":"RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Clayton_InferenceFromMargins_MatchesIndependentOptimum","phase":"stabilized-pass","startedUtc":"2026-09-09T15:20:08.6381138+00:00","wallSeconds":3.2971585,"testSeconds":0.0692415,"buildSeconds":1.67,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092008-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Clayton_InferenceFromMargins_MatchesIndependentOptimum\\haden_HADEN_2026-09-09_15_20_11.709.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092008-638-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Clayton_InferenceFromMargins_MatchesIndependentOptimum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092012-509-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Clayton_PseudoLikelihood_MatchesIndependentOptimum","method":"RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Clayton_PseudoLikelihood_MatchesIndependentOptimum","phase":"stabilized-pass","startedUtc":"2026-09-09T15:20:12.5093754+00:00","wallSeconds":3.184406,"testSeconds":0.0743187,"buildSeconds":1.47,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092012-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Clayton_PseudoLikelihood_MatchesIndependentOptimum\\haden_HADEN_2026-09-09_15_20_15.473.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092012-509-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Clayton_PseudoLikelihood_MatchesIndependentOptimum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092016-282-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Frank_InferenceFromMargins_MatchesIndependentOptimum","method":"RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Frank_InferenceFromMargins_MatchesIndependentOptimum","phase":"stabilized-pass","startedUtc":"2026-09-09T15:20:16.2826723+00:00","wallSeconds":3.1893403,"testSeconds":0.0641398,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092016-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Frank_InferenceFromMargins_MatchesIndependentOptimum\\haden_HADEN_2026-09-09_15_20_19.221.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092016-282-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Frank_InferenceFromMargins_MatchesIndependentOptimum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092019-984-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Frank_PseudoLikelihood_MatchesIndependentOptimum","method":"RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Frank_PseudoLikelihood_MatchesIndependentOptimum","phase":"stabilized-pass","startedUtc":"2026-09-09T15:20:19.9846249+00:00","wallSeconds":3.0933046,"testSeconds":0.0601342,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092020-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Frank_PseudoLikelihood_MatchesIndependentOptimum\\haden_HADEN_2026-09-09_15_20_22.857.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092019-984-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Frank_PseudoLikelihood_MatchesIndependentOptimum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092023-691-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Gumbel_InferenceFromMargins_MatchesIndependentOptimum","method":"RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Gumbel_InferenceFromMargins_MatchesIndependentOptimum","phase":"stabilized-pass","startedUtc":"2026-09-09T15:20:23.6911859+00:00","wallSeconds":2.9904089,"testSeconds":0.0803001,"buildSeconds":1.32,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092023-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Gumbel_InferenceFromMargins_MatchesIndependentOptimum\\haden_HADEN_2026-09-09_15_20_26.465.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092023-691-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Gumbel_InferenceFromMargins_MatchesIndependentOptimum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092027-225-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Gumbel_PseudoLikelihood_MatchesIndependentOptimum","method":"RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Gumbel_PseudoLikelihood_MatchesIndependentOptimum","phase":"stabilized-pass","startedUtc":"2026-09-09T15:20:27.2260384+00:00","wallSeconds":3.1279633,"testSeconds":0.0748705,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092027-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Gumbel_PseudoLikelihood_MatchesIndependentOptimum\\haden_HADEN_2026-09-09_15_20_30.134.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092027-225-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Gumbel_PseudoLikelihood_MatchesIndependentOptimum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092030-881-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Joe_InferenceFromMargins_MatchesIndependentOptimum","method":"RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Joe_InferenceFromMargins_MatchesIndependentOptimum","phase":"stabilized-pass","startedUtc":"2026-09-09T15:20:30.8818793+00:00","wallSeconds":3.1460238,"testSeconds":0.0726345,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092030-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Joe_InferenceFromMargins_MatchesIndependentOptimum\\haden_HADEN_2026-09-09_15_20_33.786.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092030-881-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Joe_InferenceFromMargins_MatchesIndependentOptimum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092034-538-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Joe_PseudoLikelihood_MatchesIndependentOptimum","method":"RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Joe_PseudoLikelihood_MatchesIndependentOptimum","phase":"stabilized-pass","startedUtc":"2026-09-09T15:20:34.5387681+00:00","wallSeconds":3.1247483,"testSeconds":0.0894596,"buildSeconds":1.51,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092034-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Joe_PseudoLikelihood_MatchesIndependentOptimum\\haden_HADEN_2026-09-09_15_20_37.443.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092034-538-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Joe_PseudoLikelihood_MatchesIndependentOptimum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092038-182-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Normal_InferenceFromMargins_MatchesIndependentOptimum","method":"RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Normal_InferenceFromMargins_MatchesIndependentOptimum","phase":"stabilized-pass","startedUtc":"2026-09-09T15:20:38.1829510+00:00","wallSeconds":3.2325334,"testSeconds":0.0780824,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092038-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Normal_InferenceFromMargins_MatchesIndependentOptimum\\haden_HADEN_2026-09-09_15_20_41.035.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092038-182-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Normal_InferenceFromMargins_MatchesIndependentOptimum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092041-970-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Normal_PseudoLikelihood_MatchesIndependentOptimum","method":"RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Normal_PseudoLikelihood_MatchesIndependentOptimum","phase":"stabilized-pass","startedUtc":"2026-09-09T15:20:41.9702178+00:00","wallSeconds":3.2086223,"testSeconds":0.0776467,"buildSeconds":1.54,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092042-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Normal_PseudoLikelihood_MatchesIndependentOptimum\\haden_HADEN_2026-09-09_15_20_44.957.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092041-970-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Normal_PseudoLikelihood_MatchesIndependentOptimum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092045-696-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_StudentT_InferenceFromMargins_MatchesIndependentOptimum","method":"RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.StudentT_InferenceFromMargins_MatchesIndependentOptimum","phase":"stabilized-pass","startedUtc":"2026-09-09T15:20:45.6968960+00:00","wallSeconds":30.6827118,"testSeconds":27.5953519,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092045-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_StudentT_InferenceFromMargins_MatchesIndependentOptimum\\haden_HADEN_2026-09-09_15_21_16.151.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092045-696-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_StudentT_InferenceFromMargins_MatchesIndependentOptimum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092116-891-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_StudentT_PseudoLikelihood_MatchesIndependentOptimum","method":"RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.StudentT_PseudoLikelihood_MatchesIndependentOptimum","phase":"stabilized-pass","startedUtc":"2026-09-09T15:21:16.8914678+00:00","wallSeconds":30.9696843,"testSeconds":28.0226309,"buildSeconds":1.45,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092116-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_StudentT_PseudoLikelihood_MatchesIndependentOptimum\\haden_HADEN_2026-09-09_15_21_47.638.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092116-891-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_StudentT_PseudoLikelihood_MatchesIndependentOptimum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092148-397-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_CriteriaRankingWeightsAndConfiguredOrder_MatchIndependentOracle","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisCriteriaVerificationTests.CriteriaRankingWeightsAndConfiguredOrder_MatchIndependentOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:21:48.3976540+00:00","wallSeconds":2.9647368,"testSeconds":0.0735936,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092148-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_CriteriaRankingWeightsAndConfiguredOrder_MatchIndependentOracle\\haden_HADEN_2026-09-09_15_21_51.136.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092148-397-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_CriteriaRankingWeightsAndConfiguredOrder_MatchIndependentOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Logistic","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092151-942-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_FittedParameters_MatchIndependentOracle","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisCriteriaVerificationTests.FittedParameters_MatchIndependentOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:21:51.9427417+00:00","wallSeconds":2.9282963,"testSeconds":0.076064,"buildSeconds":1.31,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092152-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_FittedParameters_MatchIndependentOracle\\haden_HADEN_2026-09-09_15_21_54.647.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092151-942-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_FittedParameters_MatchIndependentOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Logistic","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092155-398-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Exponential_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Exponential_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T15:21:55.3982579+00:00","wallSeconds":21.5623779,"testSeconds":18.567226,"buildSeconds":1.51,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092155-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Exponential_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_15_22_16.730.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092155-398-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Exponential_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092217-447-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gamma_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Gamma_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T15:22:17.4475406+00:00","wallSeconds":4.2879665,"testSeconds":1.5013355,"buildSeconds":1.35,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092217-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gamma_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_15_22_21.508.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092217-447-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gamma_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092222-233-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedExtremeValue_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.GeneralizedExtremeValue_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T15:22:22.2330952+00:00","wallSeconds":4.419617,"testSeconds":1.7368281,"buildSeconds":1.23,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092222-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedExtremeValue_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_15_22_26.427.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092222-233-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedExtremeValue_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092227-227-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedLogistic_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.GeneralizedLogistic_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T15:22:27.2270881+00:00","wallSeconds":8.8631661,"testSeconds":5.7678648,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092227-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedLogistic_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_15_22_35.864.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092227-227-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedLogistic_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092236-648-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedNormal_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.GeneralizedNormal_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T15:22:36.6481325+00:00","wallSeconds":5.0647066,"testSeconds":1.9850443,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092236-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedNormal_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_15_22_41.485.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092236-648-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedNormal_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092242-196-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedPareto_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.GeneralizedPareto_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T15:22:42.1970046+00:00","wallSeconds":24.5216394,"testSeconds":21.7348345,"buildSeconds":1.34,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092242-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedPareto_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_15_23_06.488.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092242-196-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedPareto_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092349-993-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoPositive_MatchesClosedForm","method":"RMC.BestFit.Verification.Bivariate.CoincidentFrequencyAnalysisTests.SumOfNormals_RhoPositive_MatchesClosedForm","phase":"coincident-runtime-baseline","startedUtc":"2026-09-09T15:23:49.9954438+00:00","wallSeconds":43.1673826,"testSeconds":40.1853053,"buildSeconds":1.42,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260909-092350-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoPositive_MatchesClosedForm\\haden_HADEN_2026-09-09_15_24_32.905.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092349-993-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoPositive_MatchesClosedForm.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"d80bfa8621c48a78cf4ebad7f01326841fac37aa","sourceSha256":"95A9BB4F8927EF0934A13A39B566C1FA30BDB42B2F95AC965746DD122DB39BA0"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"17C77A3DDAE1EF7B39BA3945DB5F6FB61F93F3D1D57FD48AF96248C9A6054F43"},"numericsAssemblySha256":"3E82F983E4795FD50104CBF0F950A7513BDF94540C3397401A8EC5BBF1A6C60F","bestFitAssemblySha256":"67A7E4809110D816C06ED83E4F9068F6E6FA78E490A5486FCEF62A56D7019DC0","verificationAssemblySha256":"542282B3090D914193685DF647EB18960BC31D061A17CACA7E63A2AA92CC9E46","dependencies":["Exponential","LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Current40.509s versus unverified historical34.538s; pinned comparison to assess whether a regression is reproducible."} +{"runId":"20260909-092433-979-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoPositive_MatchesClosedForm","method":"RMC.BestFit.Verification.Bivariate.CoincidentFrequencyAnalysisTests.SumOfNormals_RhoPositive_MatchesClosedForm","phase":"coincident-runtime-current","startedUtc":"2026-09-09T15:24:33.9793788+00:00","wallSeconds":42.077683,"testSeconds":39.0391019,"buildSeconds":1.51,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092434-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoPositive_MatchesClosedForm\\haden_HADEN_2026-09-09_15_25_15.822.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092433-979-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoPositive_MatchesClosedForm.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Unchanged current repeat immediately after pinned baseline; source remains frozen."} +{"runId":"20260909-092536-423-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gumbel_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Gumbel_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T15:25:36.4252306+00:00","wallSeconds":4.3620079,"testSeconds":1.3201043,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092536-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gumbel_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_15_25_40.532.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092536-423-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gumbel_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092541-556-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_KappaFour_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.KappaFour_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T15:25:41.5570300+00:00","wallSeconds":7.7615802,"testSeconds":4.9434147,"buildSeconds":1.36,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092541-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_KappaFour_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_15_25_49.087.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092541-556-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_KappaFour_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092549-987-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Logistic_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Logistic_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T15:25:49.9877515+00:00","wallSeconds":4.6559413,"testSeconds":1.5074834,"buildSeconds":1.47,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092550-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Logistic_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_15_25_54.375.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092549-987-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Logistic_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092555-227-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogNormal_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.LogNormal_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T15:25:55.2280085+00:00","wallSeconds":9.5415984,"testSeconds":6.3752493,"buildSeconds":1.54,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092555-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogNormal_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_15_26_04.539.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092555-227-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogNormal_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092605-405-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Normal_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Normal_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T15:26:05.4059047+00:00","wallSeconds":4.9530657,"testSeconds":1.7787699,"buildSeconds":1.52,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092605-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Normal_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_15_26_10.133.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092605-405-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Normal_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092610-979-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Weibull_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Weibull_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T15:26:10.9794018+00:00","wallSeconds":4.5501819,"testSeconds":1.5084569,"buildSeconds":1.35,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092611-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Weibull_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_15_26_15.311.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092610-979-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Weibull_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092616-033-RMC_BestFit_Verification_DistributionFitting_KappaFourZeroShapeVerificationTests_ZeroShapeNonzeroHondo_PdfMatchesAnalyticalDerivative","method":"RMC.BestFit.Verification.DistributionFitting.KappaFourZeroShapeVerificationTests.ZeroShapeNonzeroHondo_PdfMatchesAnalyticalDerivative","phase":"stabilized-pass","startedUtc":"2026-09-09T15:26:16.0336805+00:00","wallSeconds":2.9099432,"testSeconds":0.0061544,"buildSeconds":1.38,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092616-RMC_BestFit_Verification_DistributionFitting_KappaFourZeroShapeVerificationTests_ZeroShapeNonzeroHondo_PdfMatchesAnalyticalDerivative\\haden_HADEN_2026-09-09_15_26_18.664.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092616-033-RMC_BestFit_Verification_DistributionFitting_KappaFourZeroShapeVerificationTests_ZeroShapeNonzeroHondo_PdfMatchesAnalyticalDerivative.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["KappaFour"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092619-456-RMC_BestFit_Verification_DistributionFitting_KappaFourZeroShapeVerificationTests_ZeroShapeNonzeroHondo_QuantileInvertsCdf","method":"RMC.BestFit.Verification.DistributionFitting.KappaFourZeroShapeVerificationTests.ZeroShapeNonzeroHondo_QuantileInvertsCdf","phase":"stabilized-pass","startedUtc":"2026-09-09T15:26:19.4562189+00:00","wallSeconds":2.9220104,"testSeconds":0.0064371,"buildSeconds":1.36,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092619-RMC_BestFit_Verification_DistributionFitting_KappaFourZeroShapeVerificationTests_ZeroShapeNonzeroHondo_QuantileInvertsCdf\\haden_HADEN_2026-09-09_15_26_22.068.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092619-456-RMC_BestFit_Verification_DistributionFitting_KappaFourZeroShapeVerificationTests_ZeroShapeNonzeroHondo_QuantileInvertsCdf.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["KappaFour"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092622-903-RMC_BestFit_Verification_DistributionFitting_LmomcoDistributionFittingVerificationTests_GeneralizedLogistic_MleAndDistributionFunctionsMatchLmomco","method":"RMC.BestFit.Verification.DistributionFitting.LmomcoDistributionFittingVerificationTests.GeneralizedLogistic_MleAndDistributionFunctionsMatchLmomco","phase":"stabilized-pass","startedUtc":"2026-09-09T15:26:22.9039258+00:00","wallSeconds":2.9764427,"testSeconds":0.08455,"buildSeconds":1.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092622-RMC_BestFit_Verification_DistributionFitting_LmomcoDistributionFittingVerificationTests_GeneralizedLogistic_MleAndDistributionFunctionsMatchLmomco\\haden_HADEN_2026-09-09_15_26_25.659.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092622-903-RMC_BestFit_Verification_DistributionFitting_LmomcoDistributionFittingVerificationTests_GeneralizedLogistic_MleAndDistributionFunctionsMatchLmomco.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["GeneralizedLogistic","GeneralizedNormal","Logistic","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092626-394-RMC_BestFit_Verification_DistributionFitting_LmomcoDistributionFittingVerificationTests_GeneralizedNormal_MleAndDistributionFunctionsMatchLmomco","method":"RMC.BestFit.Verification.DistributionFitting.LmomcoDistributionFittingVerificationTests.GeneralizedNormal_MleAndDistributionFunctionsMatchLmomco","phase":"stabilized-pass","startedUtc":"2026-09-09T15:26:26.3949962+00:00","wallSeconds":2.9339667,"testSeconds":0.0722345,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092626-RMC_BestFit_Verification_DistributionFitting_LmomcoDistributionFittingVerificationTests_GeneralizedNormal_MleAndDistributionFunctionsMatchLmomco\\haden_HADEN_2026-09-09_15_26_29.108.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092626-394-RMC_BestFit_Verification_DistributionFitting_LmomcoDistributionFittingVerificationTests_GeneralizedNormal_MleAndDistributionFunctionsMatchLmomco.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["GeneralizedLogistic","GeneralizedNormal","Logistic","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092629-867-RMC_BestFit_Verification_DistributionFitting_Log10NormalFittingVerificationTests_ClosedFormMle_LikelihoodCdfAndQuantileMatchAnalyticalOracle","method":"RMC.BestFit.Verification.DistributionFitting.Log10NormalFittingVerificationTests.ClosedFormMle_LikelihoodCdfAndQuantileMatchAnalyticalOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:26:29.8676607+00:00","wallSeconds":2.9022259,"testSeconds":0.0493476,"buildSeconds":1.37,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092629-RMC_BestFit_Verification_DistributionFitting_Log10NormalFittingVerificationTests_ClosedFormMle_LikelihoodCdfAndQuantileMatchAnalyticalOracle\\haden_HADEN_2026-09-09_15_26_32.544.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092629-867-RMC_BestFit_Verification_DistributionFitting_Log10NormalFittingVerificationTests_ClosedFormMle_LikelihoodCdfAndQuantileMatchAnalyticalOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092633-280-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Exponential_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Exponential_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T15:26:33.2805469+00:00","wallSeconds":2.8985435,"testSeconds":0.0534508,"buildSeconds":1.37,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092633-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Exponential_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_15_26_35.964.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092633-280-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Exponential_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092636-686-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Gamma_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Gamma_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T15:26:36.6863908+00:00","wallSeconds":2.9443347,"testSeconds":0.0595044,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092636-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Gamma_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_15_26_39.408.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092636-686-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Gamma_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092640-150-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_GeneralizedExtremeValue_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.GeneralizedExtremeValue_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T15:26:40.1505236+00:00","wallSeconds":2.970573,"testSeconds":0.0772253,"buildSeconds":1.42,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092640-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_GeneralizedExtremeValue_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_15_26_42.903.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092640-150-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_GeneralizedExtremeValue_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092643-625-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_GeneralizedPareto_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.GeneralizedPareto_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T15:26:43.6258627+00:00","wallSeconds":2.9554032,"testSeconds":0.0690405,"buildSeconds":1.39,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092643-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_GeneralizedPareto_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_15_26_46.366.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092643-625-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_GeneralizedPareto_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092647-092-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Gumbel_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Gumbel_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T15:26:47.0925479+00:00","wallSeconds":2.9514105,"testSeconds":0.0547981,"buildSeconds":1.42,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092647-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Gumbel_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_15_26_49.826.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092647-092-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Gumbel_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092650-607-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_KappaFour_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.KappaFour_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T15:26:50.6076245+00:00","wallSeconds":3.169002,"testSeconds":0.3024845,"buildSeconds":1.38,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092650-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_KappaFour_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_15_26_53.557.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092650-607-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_KappaFour_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092654-294-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LnNormal_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.LnNormal_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T15:26:54.2942333+00:00","wallSeconds":2.9401288,"testSeconds":0.057011,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092654-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LnNormal_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_15_26_57.017.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092654-294-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LnNormal_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092657-738-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Logistic_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Logistic_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T15:26:57.7386254+00:00","wallSeconds":2.9431591,"testSeconds":0.0527871,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092657-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Logistic_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_15_27_00.463.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092657-738-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Logistic_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092701-194-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogNormal_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.LogNormal_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T15:27:01.1944924+00:00","wallSeconds":2.9867137,"testSeconds":0.0579571,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092701-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogNormal_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_15_27_03.915.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092701-194-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogNormal_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092704-692-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Normal_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Normal_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T15:27:04.6930012+00:00","wallSeconds":2.9817542,"testSeconds":0.054559,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092704-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Normal_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_15_27_07.425.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092704-692-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Normal_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092708-187-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Weibull_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Weibull_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T15:27:08.1880627+00:00","wallSeconds":3.0074858,"testSeconds":0.0554131,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092708-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Weibull_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_15_27_10.963.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092708-187-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Weibull_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092711-714-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Exponential_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_Exponential_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T15:27:11.7145187+00:00","wallSeconds":3.0190491,"testSeconds":0.0511589,"buildSeconds":1.45,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092711-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Exponential_MLE\\haden_HADEN_2026-09-09_15_27_14.503.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092711-714-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Exponential_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092715-239-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GammaDist_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_GammaDist_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T15:27:15.2398318+00:00","wallSeconds":2.9975791,"testSeconds":0.0574447,"buildSeconds":1.42,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092715-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GammaDist_MLE\\haden_HADEN_2026-09-09_15_27_18.012.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092715-239-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GammaDist_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092718-747-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedExtremeValue_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_GeneralizedExtremeValue_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T15:27:18.7479852+00:00","wallSeconds":2.9698268,"testSeconds":0.0769729,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092718-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedExtremeValue_MLE\\haden_HADEN_2026-09-09_15_27_21.493.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092718-747-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedExtremeValue_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092722-236-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedLogistic_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_GeneralizedLogistic_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T15:27:22.2364375+00:00","wallSeconds":3.0035906,"testSeconds":0.0949563,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092722-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedLogistic_MLE\\haden_HADEN_2026-09-09_15_27_25.015.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092722-236-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedLogistic_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092725-759-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedNormal_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_GeneralizedNormal_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T15:27:25.7591320+00:00","wallSeconds":3.0518891,"testSeconds":0.1138458,"buildSeconds":1.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092725-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedNormal_MLE\\haden_HADEN_2026-09-09_15_27_28.590.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092725-759-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedNormal_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092729-322-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedPareto_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_GeneralizedPareto_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T15:27:29.3226710+00:00","wallSeconds":3.0831262,"testSeconds":0.1288437,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092729-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedPareto_MLE\\haden_HADEN_2026-09-09_15_27_32.185.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092729-322-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedPareto_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092732-931-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Gumbel_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_Gumbel_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T15:27:32.9312176+00:00","wallSeconds":2.9801142,"testSeconds":0.0496659,"buildSeconds":1.41,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092732-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Gumbel_MLE\\haden_HADEN_2026-09-09_15_27_35.663.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092732-931-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Gumbel_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092736-422-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Kappa4_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_Kappa4_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T15:27:36.4222863+00:00","wallSeconds":3.5357197,"testSeconds":0.339944,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092736-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Kappa4_MLE\\haden_HADEN_2026-09-09_15_27_39.512.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092736-422-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Kappa4_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092740-487-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LnNormal_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_LnNormal_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T15:27:40.4878020+00:00","wallSeconds":2.9889205,"testSeconds":0.0540518,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092740-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LnNormal_MLE\\haden_HADEN_2026-09-09_15_27_43.249.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092740-487-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LnNormal_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092744-053-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Logistic_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_Logistic_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T15:27:44.0537165+00:00","wallSeconds":3.0586092,"testSeconds":0.0505054,"buildSeconds":1.41,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092744-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Logistic_MLE\\haden_HADEN_2026-09-09_15_27_46.789.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092744-053-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Logistic_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092747-683-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LogNormal_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_LogNormal_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T15:27:47.6834536+00:00","wallSeconds":3.0074435,"testSeconds":0.0564962,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092747-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LogNormal_MLE\\haden_HADEN_2026-09-09_15_27_50.473.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092747-683-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LogNormal_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092751-205-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Normal_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_Normal_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T15:27:51.2053626+00:00","wallSeconds":3.0299776,"testSeconds":0.0504004,"buildSeconds":1.42,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092751-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Normal_MLE\\haden_HADEN_2026-09-09_15_27_54.017.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092751-205-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Normal_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092754-775-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Weibull_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_Weibull_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T15:27:54.7751225+00:00","wallSeconds":3.0417188,"testSeconds":0.053462,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092754-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Weibull_MLE\\haden_HADEN_2026-09-09_15_27_57.599.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092754-775-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Weibull_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092758-339-RMC_BestFit_Verification_ModelEstimation_BayesianAnalysisRecoveryTests_Test_Estimate_PosteriorMean_NearTrueValue","method":"RMC.BestFit.Verification.ModelEstimation.BayesianAnalysisRecoveryTests.Test_Estimate_PosteriorMean_NearTrueValue","phase":"stabilized-pass","startedUtc":"2026-09-09T15:27:58.3391113+00:00","wallSeconds":9.0328541,"testSeconds":6.009082,"buildSeconds":1.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092758-RMC_BestFit_Verification_ModelEstimation_BayesianAnalysisRecoveryTests_Test_Estimate_PosteriorMean_NearTrueValue\\haden_HADEN_2026-09-09_15_28_07.073.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092758-339-RMC_BestFit_Verification_ModelEstimation_BayesianAnalysisRecoveryTests_Test_Estimate_PosteriorMean_NearTrueValue.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092807-980-RMC_BestFit_Verification_ModelEstimation_BayesianAnalysisRecoveryTests_Test_N1000_DefaultPosteriorIntervals_ContainGeneratingParameters","method":"RMC.BestFit.Verification.ModelEstimation.BayesianAnalysisRecoveryTests.Test_N1000_DefaultPosteriorIntervals_ContainGeneratingParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:28:07.9810417+00:00","wallSeconds":9.5082593,"testSeconds":6.3868207,"buildSeconds":1.53,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092808-RMC_BestFit_Verification_ModelEstimation_BayesianAnalysisRecoveryTests_Test_N1000_DefaultPosteriorIntervals_ContainGeneratingParameters\\haden_HADEN_2026-09-09_15_28_17.248.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092807-980-RMC_BestFit_Verification_ModelEstimation_BayesianAnalysisRecoveryTests_Test_N1000_DefaultPosteriorIntervals_ContainGeneratingParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092817-989-RMC_BestFit_Verification_ModelEstimation_BayesianConjugateOracleVerificationTests_InformativePrior_NormalNormalConjugatePosterior_MatchesClosedForm","method":"RMC.BestFit.Verification.ModelEstimation.BayesianConjugateOracleVerificationTests.InformativePrior_NormalNormalConjugatePosterior_MatchesClosedForm","phase":"stabilized-pass","startedUtc":"2026-09-09T15:28:17.9895439+00:00","wallSeconds":4.6424549,"testSeconds":1.739471,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092818-RMC_BestFit_Verification_ModelEstimation_BayesianConjugateOracleVerificationTests_InformativePrior_NormalNormalConjugatePosterior_MatchesClosedForm\\haden_HADEN_2026-09-09_15_28_22.404.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092817-989-RMC_BestFit_Verification_ModelEstimation_BayesianConjugateOracleVerificationTests_InformativePrior_NormalNormalConjugatePosterior_MatchesClosedForm.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092823-195-RMC_BestFit_Verification_ModelEstimation_GmmInfluenceDiagnosticsVerificationTests_Log10NormalObservationInfluence_MatchesRGmmOracle","method":"RMC.BestFit.Verification.ModelEstimation.GmmInfluenceDiagnosticsVerificationTests.Log10NormalObservationInfluence_MatchesRGmmOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:28:23.1958749+00:00","wallSeconds":3.1004822,"testSeconds":0.0649833,"buildSeconds":1.47,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092823-RMC_BestFit_Verification_ModelEstimation_GmmInfluenceDiagnosticsVerificationTests_Log10NormalObservationInfluence_MatchesRGmmOracle\\haden_HADEN_2026-09-09_15_28_26.074.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092823-195-RMC_BestFit_Verification_ModelEstimation_GmmInfluenceDiagnosticsVerificationTests_Log10NormalObservationInfluence_MatchesRGmmOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092826-844-RMC_BestFit_Verification_ModelEstimation_GmmInfluenceDiagnosticsVerificationTests_VanishingCenteredPenalty_PreservesObservationInfluenceScale","method":"RMC.BestFit.Verification.ModelEstimation.GmmInfluenceDiagnosticsVerificationTests.VanishingCenteredPenalty_PreservesObservationInfluenceScale","phase":"stabilized-pass","startedUtc":"2026-09-09T15:28:26.8448613+00:00","wallSeconds":3.1153927,"testSeconds":0.0673443,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092826-RMC_BestFit_Verification_ModelEstimation_GmmInfluenceDiagnosticsVerificationTests_VanishingCenteredPenalty_PreservesObservationInfluenceScale\\haden_HADEN_2026-09-09_15_28_29.740.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092826-844-RMC_BestFit_Verification_ModelEstimation_GmmInfluenceDiagnosticsVerificationTests_VanishingCenteredPenalty_PreservesObservationInfluenceScale.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092830-544-RMC_BestFit_Verification_ModelEstimation_InformationCriterionOracleTests_DIC_MatchesRBayesianToolsOracle","method":"RMC.BestFit.Verification.ModelEstimation.InformationCriterionOracleTests.DIC_MatchesRBayesianToolsOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:28:30.5445086+00:00","wallSeconds":3.2897636,"testSeconds":0.1556094,"buildSeconds":1.52,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092830-RMC_BestFit_Verification_ModelEstimation_InformationCriterionOracleTests_DIC_MatchesRBayesianToolsOracle\\haden_HADEN_2026-09-09_15_28_33.562.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092830-544-RMC_BestFit_Verification_ModelEstimation_InformationCriterionOracleTests_DIC_MatchesRBayesianToolsOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092834-405-RMC_BestFit_Verification_ModelEstimation_InformationCriterionOracleTests_WAIC_MatchesRLooOracle","method":"RMC.BestFit.Verification.ModelEstimation.InformationCriterionOracleTests.WAIC_MatchesRLooOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:28:34.4055907+00:00","wallSeconds":3.1853461,"testSeconds":0.1465398,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092834-RMC_BestFit_Verification_ModelEstimation_InformationCriterionOracleTests_WAIC_MatchesRLooOracle\\haden_HADEN_2026-09-09_15_28_37.370.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092834-405-RMC_BestFit_Verification_ModelEstimation_InformationCriterionOracleTests_WAIC_MatchesRLooOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092838-133-RMC_BestFit_Verification_ModelEstimation_JointPriorSamplingVerificationTests_SampleFromPriors_SoftJointPrior_CurrentlyDrawsIndependentMarginals","method":"RMC.BestFit.Verification.ModelEstimation.JointPriorSamplingVerificationTests.SampleFromPriors_SoftJointPrior_CurrentlyDrawsIndependentMarginals","phase":"stabilized-pass","startedUtc":"2026-09-09T15:28:38.1332016+00:00","wallSeconds":3.1217848,"testSeconds":0.0239796,"buildSeconds":1.52,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092838-RMC_BestFit_Verification_ModelEstimation_JointPriorSamplingVerificationTests_SampleFromPriors_SoftJointPrior_CurrentlyDrawsIndependentMarginals\\haden_HADEN_2026-09-09_15_28_41.010.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092838-133-RMC_BestFit_Verification_ModelEstimation_JointPriorSamplingVerificationTests_SampleFromPriors_SoftJointPrior_CurrentlyDrawsIndependentMarginals.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092841-808-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_FlatPriorMap_MatchesClosedFormLog10NormalMle","method":"RMC.BestFit.Verification.ModelEstimation.Log10NormalEstimationEquivalenceTests.FlatPriorMap_MatchesClosedFormLog10NormalMle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:28:41.8086421+00:00","wallSeconds":3.0814349,"testSeconds":0.0572852,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092841-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_FlatPriorMap_MatchesClosedFormLog10NormalMle\\haden_HADEN_2026-09-09_15_28_44.663.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092841-808-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_FlatPriorMap_MatchesClosedFormLog10NormalMle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092845-440-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_GmmMuPenaltyRegimes_MatchAnalyticalFitAndVarianceInfluence","method":"RMC.BestFit.Verification.ModelEstimation.Log10NormalEstimationEquivalenceTests.GmmMuPenaltyRegimes_MatchAnalyticalFitAndVarianceInfluence","phase":"stabilized-pass","startedUtc":"2026-09-09T15:28:45.4407702+00:00","wallSeconds":3.0734424,"testSeconds":0.0573531,"buildSeconds":1.47,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092845-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_GmmMuPenaltyRegimes_MatchAnalyticalFitAndVarianceInfluence\\haden_HADEN_2026-09-09_15_28_48.288.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092845-440-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_GmmMuPenaltyRegimes_MatchAnalyticalFitAndVarianceInfluence.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092849-086-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_MapAndGmmMuPosterior_InverseVarianceReferenceInsideCentral95Intervals","method":"RMC.BestFit.Verification.ModelEstimation.Log10NormalEstimationEquivalenceTests.MapAndGmmMuPosterior_InverseVarianceReferenceInsideCentral95Intervals","phase":"stabilized-pass","startedUtc":"2026-09-09T15:28:49.0869124+00:00","wallSeconds":3.1885535,"testSeconds":0.0803414,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092849-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_MapAndGmmMuPosterior_InverseVarianceReferenceInsideCentral95Intervals\\haden_HADEN_2026-09-09_15_28_52.004.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092849-086-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_MapAndGmmMuPosterior_InverseVarianceReferenceInsideCentral95Intervals.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092852-873-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_MapMuPriorRegimes_MatchAnalyticalFitAndVarianceInfluence","method":"RMC.BestFit.Verification.ModelEstimation.Log10NormalEstimationEquivalenceTests.MapMuPriorRegimes_MatchAnalyticalFitAndVarianceInfluence","phase":"stabilized-pass","startedUtc":"2026-09-09T15:28:52.8732967+00:00","wallSeconds":3.131656,"testSeconds":0.0656107,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092852-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_MapMuPriorRegimes_MatchAnalyticalFitAndVarianceInfluence\\haden_HADEN_2026-09-09_15_28_55.783.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092852-873-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_MapMuPriorRegimes_MatchAnalyticalFitAndVarianceInfluence.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092856-545-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_UnpenalizedB17CGmm_MatchesExactSampleMoments","method":"RMC.BestFit.Verification.ModelEstimation.Log10NormalEstimationEquivalenceTests.UnpenalizedB17CGmm_MatchesExactSampleMoments","phase":"stabilized-pass","startedUtc":"2026-09-09T15:28:56.5458604+00:00","wallSeconds":3.1515668,"testSeconds":0.0432561,"buildSeconds":1.52,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092856-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_UnpenalizedB17CGmm_MatchesExactSampleMoments\\haden_HADEN_2026-09-09_15_28_59.464.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092856-545-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_UnpenalizedB17CGmm_MatchesExactSampleMoments.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092900-236-RMC_BestFit_Verification_ModelEstimation_Log10NormalInfluenceVerificationTests_GmmObservationInfluence_CurrentMagnitudeDiffersFromExactDeletionScale","method":"RMC.BestFit.Verification.ModelEstimation.Log10NormalInfluenceVerificationTests.GmmObservationInfluence_CurrentMagnitudeDiffersFromExactDeletionScale","phase":"stabilized-pass","startedUtc":"2026-09-09T15:29:00.2366175+00:00","wallSeconds":3.155547,"testSeconds":0.0490237,"buildSeconds":1.53,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092900-RMC_BestFit_Verification_ModelEstimation_Log10NormalInfluenceVerificationTests_GmmObservationInfluence_CurrentMagnitudeDiffersFromExactDeletionScale\\haden_HADEN_2026-09-09_15_29_03.167.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092900-236-RMC_BestFit_Verification_ModelEstimation_Log10NormalInfluenceVerificationTests_GmmObservationInfluence_CurrentMagnitudeDiffersFromExactDeletionScale.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092903-980-RMC_BestFit_Verification_ModelEstimation_MaximumAPosterioriInformationCriteriaOracleTests_AIC_UsesDataLikelihoodAtMap_ExcludesPriorDensity","method":"RMC.BestFit.Verification.ModelEstimation.MaximumAPosterioriInformationCriteriaOracleTests.AIC_UsesDataLikelihoodAtMap_ExcludesPriorDensity","phase":"stabilized-pass","startedUtc":"2026-09-09T15:29:03.9809383+00:00","wallSeconds":3.2305647,"testSeconds":0.0447649,"buildSeconds":1.54,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092904-RMC_BestFit_Verification_ModelEstimation_MaximumAPosterioriInformationCriteriaOracleTests_AIC_UsesDataLikelihoodAtMap_ExcludesPriorDensity\\haden_HADEN_2026-09-09_15_29_06.993.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092903-980-RMC_BestFit_Verification_ModelEstimation_MaximumAPosterioriInformationCriteriaOracleTests_AIC_UsesDataLikelihoodAtMap_ExcludesPriorDensity.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092907-796-RMC_BestFit_Verification_ModelEstimation_MaximumAPosterioriInformationCriteriaOracleTests_BIC_UsesDataLikelihoodAtMap_ExcludesPriorDensity","method":"RMC.BestFit.Verification.ModelEstimation.MaximumAPosterioriInformationCriteriaOracleTests.BIC_UsesDataLikelihoodAtMap_ExcludesPriorDensity","phase":"stabilized-pass","startedUtc":"2026-09-09T15:29:07.7962975+00:00","wallSeconds":3.0831315,"testSeconds":0.0370876,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092907-RMC_BestFit_Verification_ModelEstimation_MaximumAPosterioriInformationCriteriaOracleTests_BIC_UsesDataLikelihoodAtMap_ExcludesPriorDensity\\haden_HADEN_2026-09-09_15_29_10.662.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092907-796-RMC_BestFit_Verification_ModelEstimation_MaximumAPosterioriInformationCriteriaOracleTests_BIC_UsesDataLikelihoodAtMap_ExcludesPriorDensity.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092911-462-RMC_BestFit_Verification_ModelEstimation_MaximumAPosterioriRecoveryTests_Test_Estimate_NormalData_FindsMode","method":"RMC.BestFit.Verification.ModelEstimation.MaximumAPosterioriRecoveryTests.Test_Estimate_NormalData_FindsMode","phase":"stabilized-pass","startedUtc":"2026-09-09T15:29:11.4624556+00:00","wallSeconds":3.1384234,"testSeconds":0.0442408,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092911-RMC_BestFit_Verification_ModelEstimation_MaximumAPosterioriRecoveryTests_Test_Estimate_NormalData_FindsMode\\haden_HADEN_2026-09-09_15_29_14.336.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092911-462-RMC_BestFit_Verification_ModelEstimation_MaximumAPosterioriRecoveryTests_Test_Estimate_NormalData_FindsMode.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092915-175-RMC_BestFit_Verification_ModelEstimation_McmcNumericalVerificationTests_Arwmh_BestFitWiringRecordsEveryRealizedStateInAdaptiveCovariance","method":"RMC.BestFit.Verification.ModelEstimation.McmcNumericalVerificationTests.Arwmh_BestFitWiringRecordsEveryRealizedStateInAdaptiveCovariance","phase":"stabilized-pass","startedUtc":"2026-09-09T15:29:15.1751375+00:00","wallSeconds":3.0974277,"testSeconds":0.028235,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092915-RMC_BestFit_Verification_ModelEstimation_McmcNumericalVerificationTests_Arwmh_BestFitWiringRecordsEveryRealizedStateInAdaptiveCovariance\\haden_HADEN_2026-09-09_15_29_18.054.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092915-175-RMC_BestFit_Verification_ModelEstimation_McmcNumericalVerificationTests_Arwmh_BestFitWiringRecordsEveryRealizedStateInAdaptiveCovariance.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092918-821-RMC_BestFit_Verification_ModelEstimation_McmcNumericalVerificationTests_ConservativeEss_MatchesRPosteriorBulkAndTailOracle","method":"RMC.BestFit.Verification.ModelEstimation.McmcNumericalVerificationTests.ConservativeEss_MatchesRPosteriorBulkAndTailOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:29:18.8212165+00:00","wallSeconds":2.9487691,"testSeconds":0.0360042,"buildSeconds":1.35,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092918-RMC_BestFit_Verification_ModelEstimation_McmcNumericalVerificationTests_ConservativeEss_MatchesRPosteriorBulkAndTailOracle\\haden_HADEN_2026-09-09_15_29_21.546.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092918-821-RMC_BestFit_Verification_ModelEstimation_McmcNumericalVerificationTests_ConservativeEss_MatchesRPosteriorBulkAndTailOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092922-335-RMC_BestFit_Verification_ModelEstimation_McmcNumericalVerificationTests_Nuts_BestFitNumericalGradientMatchesPosteriorGradient","method":"RMC.BestFit.Verification.ModelEstimation.McmcNumericalVerificationTests.Nuts_BestFitNumericalGradientMatchesPosteriorGradient","phase":"stabilized-pass","startedUtc":"2026-09-09T15:29:22.3358770+00:00","wallSeconds":3.08466,"testSeconds":0.0162434,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092922-RMC_BestFit_Verification_ModelEstimation_McmcNumericalVerificationTests_Nuts_BestFitNumericalGradientMatchesPosteriorGradient\\haden_HADEN_2026-09-09_15_29_25.170.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092922-335-RMC_BestFit_Verification_ModelEstimation_McmcNumericalVerificationTests_Nuts_BestFitNumericalGradientMatchesPosteriorGradient.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092926-003-RMC_BestFit_Verification_ModelEstimation_McmcNumericalVerificationTests_RankNormalizedRhat_MatchesRPosteriorOracle","method":"RMC.BestFit.Verification.ModelEstimation.McmcNumericalVerificationTests.RankNormalizedRhat_MatchesRPosteriorOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:29:26.0031250+00:00","wallSeconds":3.0943858,"testSeconds":0.0239001,"buildSeconds":1.51,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092926-RMC_BestFit_Verification_ModelEstimation_McmcNumericalVerificationTests_RankNormalizedRhat_MatchesRPosteriorOracle\\haden_HADEN_2026-09-09_15_29_28.867.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092926-003-RMC_BestFit_Verification_ModelEstimation_McmcNumericalVerificationTests_RankNormalizedRhat_MatchesRPosteriorOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092929-658-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Exponential_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_Exponential_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:29:29.6589951+00:00","wallSeconds":3.0212443,"testSeconds":0.1280158,"buildSeconds":1.34,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092929-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Exponential_RecoversTrueParameters\\haden_HADEN_2026-09-09_15_29_32.453.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092929-658-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Exponential_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092933-238-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Gamma_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_Gamma_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:29:33.2386689+00:00","wallSeconds":3.3163917,"testSeconds":0.2329225,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092933-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Gamma_RecoversTrueParameters\\haden_HADEN_2026-09-09_15_29_36.317.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092933-238-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Gamma_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092937-081-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_GeneralizedLogistic_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_GeneralizedLogistic_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:29:37.0813352+00:00","wallSeconds":4.0900287,"testSeconds":1.0664296,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092937-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_GeneralizedLogistic_RecoversTrueParameters\\haden_HADEN_2026-09-09_15_29_40.953.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092937-081-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_GeneralizedLogistic_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092941-716-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_GeneralizedNormal_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_GeneralizedNormal_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:29:41.7162467+00:00","wallSeconds":3.9862333,"testSeconds":0.9012082,"buildSeconds":1.51,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092941-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_GeneralizedNormal_RecoversTrueParameters\\haden_HADEN_2026-09-09_15_29_45.474.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092941-716-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_GeneralizedNormal_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092946-235-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_GeneralizedPareto_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_GeneralizedPareto_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:29:46.2352980+00:00","wallSeconds":3.2666356,"testSeconds":0.3454441,"buildSeconds":1.33,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092946-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_GeneralizedPareto_RecoversTrueParameters\\haden_HADEN_2026-09-09_15_29_49.285.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092946-235-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_GeneralizedPareto_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092950-061-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_GEV_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_GEV_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:29:50.0618839+00:00","wallSeconds":3.4217932,"testSeconds":0.3854859,"buildSeconds":1.47,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092950-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_GEV_RecoversTrueParameters\\haden_HADEN_2026-09-09_15_29_53.253.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092950-061-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_GEV_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092954-020-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Gumbel_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_Gumbel_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:29:54.0207299+00:00","wallSeconds":3.2312903,"testSeconds":0.134728,"buildSeconds":1.55,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092954-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Gumbel_RecoversTrueParameters\\haden_HADEN_2026-09-09_15_29_57.031.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092954-020-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Gumbel_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-092957-851-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LnNormal_MatchesClosedFormLogSampleMLE","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_LnNormal_MatchesClosedFormLogSampleMLE","phase":"stabilized-pass","startedUtc":"2026-09-09T15:29:57.8512014+00:00","wallSeconds":3.1634521,"testSeconds":0.125298,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-092957-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LnNormal_MatchesClosedFormLogSampleMLE\\haden_HADEN_2026-09-09_15_30_00.790.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-092957-851-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LnNormal_MatchesClosedFormLogSampleMLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-093001-575-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LnNormal_RecoversGeneratingParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_LnNormal_RecoversGeneratingParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:30:01.5754207+00:00","wallSeconds":3.1643994,"testSeconds":0.1190266,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-093001-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LnNormal_RecoversGeneratingParameters\\haden_HADEN_2026-09-09_15_30_04.516.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-093001-575-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LnNormal_RecoversGeneratingParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-093005-280-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Logistic_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_Logistic_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:30:05.2802260+00:00","wallSeconds":3.1738511,"testSeconds":0.1490415,"buildSeconds":1.47,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-093005-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Logistic_RecoversTrueParameters\\haden_HADEN_2026-09-09_15_30_08.218.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-093005-280-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Logistic_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-093009-017-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Normal_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_Normal_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:30:09.0171531+00:00","wallSeconds":3.19405,"testSeconds":0.1256954,"buildSeconds":1.51,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-093009-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Normal_RecoversTrueParameters\\haden_HADEN_2026-09-09_15_30_11.980.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-093009-017-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Normal_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-093012-794-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Weibull_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_Weibull_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:30:12.7948197+00:00","wallSeconds":3.2782967,"testSeconds":0.1641179,"buildSeconds":1.56,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-093012-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Weibull_RecoversTrueParameters\\haden_HADEN_2026-09-09_15_30_15.851.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-093012-794-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Weibull_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-093016-649-RMC_BestFit_Verification_ModelEstimation_PosteriorResamplingVerificationTests_CoincidentFrequencyPosteriorResampling_MatchesIndependentClosedFormOracle","method":"RMC.BestFit.Verification.ModelEstimation.PosteriorResamplingVerificationTests.CoincidentFrequencyPosteriorResampling_MatchesIndependentClosedFormOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:30:16.6491902+00:00","wallSeconds":4.6284184,"testSeconds":1.6141955,"buildSeconds":1.47,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-093016-RMC_BestFit_Verification_ModelEstimation_PosteriorResamplingVerificationTests_CoincidentFrequencyPosteriorResampling_MatchesIndependentClosedFormOracle\\haden_HADEN_2026-09-09_15_30_21.047.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-093016-649-RMC_BestFit_Verification_ModelEstimation_PosteriorResamplingVerificationTests_CoincidentFrequencyPosteriorResampling_MatchesIndependentClosedFormOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-093021-837-RMC_BestFit_Verification_ModelEstimation_PosteriorResamplingVerificationTests_CompositePosteriorResampling_MatchesIndependentCartesianOracle","method":"RMC.BestFit.Verification.ModelEstimation.PosteriorResamplingVerificationTests.CompositePosteriorResampling_MatchesIndependentCartesianOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:30:21.8379038+00:00","wallSeconds":5.2410928,"testSeconds":2.1173564,"buildSeconds":1.51,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-093021-RMC_BestFit_Verification_ModelEstimation_PosteriorResamplingVerificationTests_CompositePosteriorResampling_MatchesIndependentCartesianOracle\\haden_HADEN_2026-09-09_15_30_26.841.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-093021-837-RMC_BestFit_Verification_ModelEstimation_PosteriorResamplingVerificationTests_CompositePosteriorResampling_MatchesIndependentCartesianOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-093027-630-RMC_BestFit_Verification_ModelEstimation_PsisLooOracleVerificationTests_ParetoInfluence_UsesRloo210DiagnosticThreshold","method":"RMC.BestFit.Verification.ModelEstimation.PsisLooOracleVerificationTests.ParetoInfluence_UsesRloo210DiagnosticThreshold","phase":"stabilized-pass","startedUtc":"2026-09-09T15:30:27.6309882+00:00","wallSeconds":3.2973759,"testSeconds":0.1655352,"buildSeconds":1.51,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-093027-RMC_BestFit_Verification_ModelEstimation_PsisLooOracleVerificationTests_ParetoInfluence_UsesRloo210DiagnosticThreshold\\haden_HADEN_2026-09-09_15_30_30.652.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-093027-630-RMC_BestFit_Verification_ModelEstimation_PsisLooOracleVerificationTests_ParetoInfluence_UsesRloo210DiagnosticThreshold.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-093031-516-RMC_BestFit_Verification_ModelEstimation_PsisLooOracleVerificationTests_PSISLOO_MatchesRLooOracle","method":"RMC.BestFit.Verification.ModelEstimation.PsisLooOracleVerificationTests.PSISLOO_MatchesRLooOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:30:31.5165731+00:00","wallSeconds":3.2644647,"testSeconds":0.1587628,"buildSeconds":1.53,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-093031-RMC_BestFit_Verification_ModelEstimation_PsisLooOracleVerificationTests_PSISLOO_MatchesRLooOracle\\haden_HADEN_2026-09-09_15_30_34.557.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-093031-516-RMC_BestFit_Verification_ModelEstimation_PsisLooOracleVerificationTests_PSISLOO_MatchesRLooOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-093035-384-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_SingleSegment_Default","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveBayesianRecoveryTests.Test_EstimateParameters_SingleSegment_Default","phase":"stabilized-pass","startedUtc":"2026-09-09T15:30:35.3848950+00:00","wallSeconds":29.038805,"testSeconds":25.9510849,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-093035-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_SingleSegment_Default\\haden_HADEN_2026-09-09_15_31_04.155.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-093035-384-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_SingleSegment_Default.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-093105-056-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_SingleSegment_LowNoise","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveBayesianRecoveryTests.Test_EstimateParameters_SingleSegment_LowNoise","phase":"stabilized-pass","startedUtc":"2026-09-09T15:31:05.0563552+00:00","wallSeconds":25.6378897,"testSeconds":22.7182464,"buildSeconds":1.42,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-093105-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_SingleSegment_LowNoise\\haden_HADEN_2026-09-09_15_31_30.467.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-093105-056-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_SingleSegment_LowNoise.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-093131-213-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_SingleSegment_WideRange","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveBayesianRecoveryTests.Test_EstimateParameters_SingleSegment_WideRange","phase":"stabilized-pass","startedUtc":"2026-09-09T15:31:31.2136488+00:00","wallSeconds":26.6442877,"testSeconds":23.7036245,"buildSeconds":1.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-093131-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_SingleSegment_WideRange\\haden_HADEN_2026-09-09_15_31_57.612.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-093131-213-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_SingleSegment_WideRange.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-093158-384-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_ThreeSegment_MultipleControl","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveBayesianRecoveryTests.Test_EstimateParameters_ThreeSegment_MultipleControl","phase":"stabilized-pass","startedUtc":"2026-09-09T15:31:58.3841461+00:00","wallSeconds":96.3923585,"testSeconds":93.3833243,"buildSeconds":1.3,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-093158-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_ThreeSegment_MultipleControl\\haden_HADEN_2026-09-09_15_33_34.345.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-093158-384-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_ThreeSegment_MultipleControl.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-093335-382-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_TwoSegment_BankfullTransition","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveBayesianRecoveryTests.Test_EstimateParameters_TwoSegment_BankfullTransition","phase":"stabilized-pass","startedUtc":"2026-09-09T15:33:35.3829584+00:00","wallSeconds":59.3509014,"testSeconds":56.0964176,"buildSeconds":1.57,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-093335-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_TwoSegment_BankfullTransition\\haden_HADEN_2026-09-09_15_34_34.472.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-093335-382-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_TwoSegment_BankfullTransition.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-093435-239-RMC_BestFit_Verification_RatingCurve_RatingCurveExampleRecoveryTests_Mle_OneSegment_RecoversExampleCurve","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveExampleRecoveryTests.Mle_OneSegment_RecoversExampleCurve","phase":"stabilized-pass","startedUtc":"2026-09-09T15:34:35.2396578+00:00","wallSeconds":3.3538144,"testSeconds":0.4626976,"buildSeconds":1.39,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-093435-RMC_BestFit_Verification_RatingCurve_RatingCurveExampleRecoveryTests_Mle_OneSegment_RecoversExampleCurve\\haden_HADEN_2026-09-09_15_34_38.342.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-093435-239-RMC_BestFit_Verification_RatingCurve_RatingCurveExampleRecoveryTests_Mle_OneSegment_RecoversExampleCurve.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-093439-099-RMC_BestFit_Verification_RatingCurve_RatingCurveExampleRecoveryTests_Mle_ThreeSegment_RecoversExampleCurve","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveExampleRecoveryTests.Mle_ThreeSegment_RecoversExampleCurve","phase":"stabilized-pass","startedUtc":"2026-09-09T15:34:39.0994344+00:00","wallSeconds":11.9694508,"testSeconds":9.0242697,"buildSeconds":1.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-093439-RMC_BestFit_Verification_RatingCurve_RatingCurveExampleRecoveryTests_Mle_ThreeSegment_RecoversExampleCurve\\haden_HADEN_2026-09-09_15_34_50.822.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-093439-099-RMC_BestFit_Verification_RatingCurve_RatingCurveExampleRecoveryTests_Mle_ThreeSegment_RecoversExampleCurve.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-093451-569-RMC_BestFit_Verification_RatingCurve_RatingCurveExampleRecoveryTests_Mle_TwoSegment_RecoversExampleCurve","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveExampleRecoveryTests.Mle_TwoSegment_RecoversExampleCurve","phase":"stabilized-pass","startedUtc":"2026-09-09T15:34:51.5699679+00:00","wallSeconds":6.0785679,"testSeconds":3.3162169,"buildSeconds":1.26,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-093451-RMC_BestFit_Verification_RatingCurve_RatingCurveExampleRecoveryTests_Mle_TwoSegment_RecoversExampleCurve\\haden_HADEN_2026-09-09_15_34_57.395.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-093451-569-RMC_BestFit_Verification_RatingCurve_RatingCurveExampleRecoveryTests_Mle_TwoSegment_RecoversExampleCurve.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-093458-154-RMC_BestFit_Verification_RatingCurve_RatingCurveLikelihoodOracleTests_OneSegment_DataLogLikelihood_IsDischargeSpaceDensity","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveLikelihoodOracleTests.OneSegment_DataLogLikelihood_IsDischargeSpaceDensity","phase":"stabilized-pass","startedUtc":"2026-09-09T15:34:58.1547273+00:00","wallSeconds":3.0742107,"testSeconds":0.0450958,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-093458-RMC_BestFit_Verification_RatingCurve_RatingCurveLikelihoodOracleTests_OneSegment_DataLogLikelihood_IsDischargeSpaceDensity\\haden_HADEN_2026-09-09_15_35_00.838.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-093458-154-RMC_BestFit_Verification_RatingCurve_RatingCurveLikelihoodOracleTests_OneSegment_DataLogLikelihood_IsDischargeSpaceDensity.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-093501-738-RMC_BestFit_Verification_RatingCurve_RatingCurveLikelihoodOracleTests_ThreeSegment_DataLogLikelihood_IsDischargeSpaceDensity","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveLikelihoodOracleTests.ThreeSegment_DataLogLikelihood_IsDischargeSpaceDensity","phase":"stabilized-pass","startedUtc":"2026-09-09T15:35:01.7383189+00:00","wallSeconds":3.1705642,"testSeconds":0.0361356,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-093501-RMC_BestFit_Verification_RatingCurve_RatingCurveLikelihoodOracleTests_ThreeSegment_DataLogLikelihood_IsDischargeSpaceDensity\\haden_HADEN_2026-09-09_15_35_04.509.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-093501-738-RMC_BestFit_Verification_RatingCurve_RatingCurveLikelihoodOracleTests_ThreeSegment_DataLogLikelihood_IsDischargeSpaceDensity.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-093505-429-RMC_BestFit_Verification_RatingCurve_RatingCurveLikelihoodOracleTests_TwoSegment_DataLogLikelihood_IsDischargeSpaceDensity","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveLikelihoodOracleTests.TwoSegment_DataLogLikelihood_IsDischargeSpaceDensity","phase":"stabilized-pass","startedUtc":"2026-09-09T15:35:05.4300224+00:00","wallSeconds":2.8745295,"testSeconds":0.0360769,"buildSeconds":1.3,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-093505-RMC_BestFit_Verification_RatingCurve_RatingCurveLikelihoodOracleTests_TwoSegment_DataLogLikelihood_IsDischargeSpaceDensity\\haden_HADEN_2026-09-09_15_35_08.052.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-093505-429-RMC_BestFit_Verification_RatingCurve_RatingCurveLikelihoodOracleTests_TwoSegment_DataLogLikelihood_IsDischargeSpaceDensity.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-093508-817-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_SingleSegment_Default","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveMLERecoveryTests.Test_EstimateParameters_SingleSegment_Default","phase":"stabilized-pass","startedUtc":"2026-09-09T15:35:08.8180259+00:00","wallSeconds":3.4629728,"testSeconds":0.564786,"buildSeconds":1.42,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-093508-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_SingleSegment_Default\\haden_HADEN_2026-09-09_15_35_12.055.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-093508-817-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_SingleSegment_Default.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-093512-783-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_SingleSegment_LowNoise","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveMLERecoveryTests.Test_EstimateParameters_SingleSegment_LowNoise","phase":"stabilized-pass","startedUtc":"2026-09-09T15:35:12.7839039+00:00","wallSeconds":3.4540114,"testSeconds":0.5899718,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-093512-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_SingleSegment_LowNoise\\haden_HADEN_2026-09-09_15_35_16.013.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-093512-783-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_SingleSegment_LowNoise.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-093516-738-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_SingleSegment_WideRange","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveMLERecoveryTests.Test_EstimateParameters_SingleSegment_WideRange","phase":"stabilized-pass","startedUtc":"2026-09-09T15:35:16.7389266+00:00","wallSeconds":3.3626129,"testSeconds":0.5581685,"buildSeconds":1.27,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-093516-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_SingleSegment_WideRange\\haden_HADEN_2026-09-09_15_35_19.849.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-093516-738-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_SingleSegment_WideRange.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-093520-597-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_ThreeSegment_MultipleControl","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveMLERecoveryTests.Test_EstimateParameters_ThreeSegment_MultipleControl","phase":"stabilized-pass","startedUtc":"2026-09-09T15:35:20.5972521+00:00","wallSeconds":13.1275835,"testSeconds":10.241091,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-093520-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_ThreeSegment_MultipleControl\\haden_HADEN_2026-09-09_15_35_33.494.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-093520-597-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_ThreeSegment_MultipleControl.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-093534-231-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_TwoSegment_BankfullTransition","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveMLERecoveryTests.Test_EstimateParameters_TwoSegment_BankfullTransition","phase":"stabilized-pass","startedUtc":"2026-09-09T15:35:34.2315173+00:00","wallSeconds":5.6238704,"testSeconds":2.7679617,"buildSeconds":1.38,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-093534-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_TwoSegment_BankfullTransition\\haden_HADEN_2026-09-09_15_35_39.621.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-093534-231-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_TwoSegment_BankfullTransition.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-093540-355-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_BasicHomogeneous_RecoversParameters","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVBayesianRecoveryTests.Bayesian_BasicHomogeneous_RecoversParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:35:40.3552575+00:00","wallSeconds":59.4956685,"testSeconds":56.5108799,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-093540-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_BasicHomogeneous_RecoversParameters\\haden_HADEN_2026-09-09_15_36_39.590.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-093540-355-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_BasicHomogeneous_RecoversParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","Gumbel"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-093640-375-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithCopula_RecoversParameters","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVBayesianRecoveryTests.Bayesian_WithCopula_RecoversParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:36:40.3757332+00:00","wallSeconds":160.6721166,"testSeconds":157.5245847,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-093640-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithCopula_RecoversParameters\\haden_HADEN_2026-09-09_15_39_20.613.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-093640-375-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithCopula_RecoversParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","Gumbel"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-093921-559-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithLocationRegression_RecoversParameters","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVBayesianRecoveryTests.Bayesian_WithLocationRegression_RecoversParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:39:21.5596949+00:00","wallSeconds":106.5278219,"testSeconds":103.4358889,"buildSeconds":1.41,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-093921-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithLocationRegression_RecoversParameters\\haden_HADEN_2026-09-09_15_41_07.666.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-093921-559-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithLocationRegression_RecoversParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","Gumbel"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094108-619-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithNegativeShape_RecoversParameters","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVBayesianRecoveryTests.Bayesian_WithNegativeShape_RecoversParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:41:08.6196027+00:00","wallSeconds":61.8947813,"testSeconds":58.9094701,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094108-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithNegativeShape_RecoversParameters\\haden_HADEN_2026-09-09_15_42_10.273.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094108-619-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithNegativeShape_RecoversParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","Gumbel"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094211-016-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithPositiveShape_RecoversParameters","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVBayesianRecoveryTests.Bayesian_WithPositiveShape_RecoversParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:42:11.0162097+00:00","wallSeconds":59.7299418,"testSeconds":56.841253,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094211-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithPositiveShape_RecoversParameters\\haden_HADEN_2026-09-09_15_43_10.507.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094211-016-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithPositiveShape_RecoversParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","Gumbel"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094311-392-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithZeroShape_RecoversParameters","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVBayesianRecoveryTests.Bayesian_WithZeroShape_RecoversParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:43:11.3923416+00:00","wallSeconds":59.6995271,"testSeconds":56.5702502,"buildSeconds":1.57,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094311-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithZeroShape_RecoversParameters\\haden_HADEN_2026-09-09_15_44_10.858.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094311-392-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithZeroShape_RecoversParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","Gumbel"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094411-687-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_GodambeSensitivityVariabilityAndSandwich_MatchIndependentOracle","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVChunk14OracleTests.GodambeSensitivityVariabilityAndSandwich_MatchIndependentOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:44:11.6873889+00:00","wallSeconds":3.1931428,"testSeconds":0.0416496,"buildSeconds":1.51,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094411-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_GodambeSensitivityVariabilityAndSandwich_MatchIndependentOracle\\haden_HADEN_2026-09-09_15_44_14.575.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094411-687-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_GodambeSensitivityVariabilityAndSandwich_MatchIndependentOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094415-410-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_HeldOutCopulaFold_MatchesIndependentFittedOracle","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVChunk14OracleTests.HeldOutCopulaFold_MatchesIndependentFittedOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:44:15.4107989+00:00","wallSeconds":3.648081,"testSeconds":0.6766389,"buildSeconds":1.35,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094415-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_HeldOutCopulaFold_MatchesIndependentFittedOracle\\haden_HADEN_2026-09-09_15_44_18.830.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094415-410-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_HeldOutCopulaFold_MatchesIndependentFittedOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094419-598-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_RegionalFixedDrawAggregation_MatchesIndependentPosteriorOracle","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVChunk14OracleTests.RegionalFixedDrawAggregation_MatchesIndependentPosteriorOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:44:19.5989958+00:00","wallSeconds":3.1892498,"testSeconds":0.1125977,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094419-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_RegionalFixedDrawAggregation_MatchesIndependentPosteriorOracle\\haden_HADEN_2026-09-09_15_44_22.563.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094419-598-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_RegionalFixedDrawAggregation_MatchesIndependentPosteriorOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094423-297-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_TemporalBlockBootstrap_MatchesIndependentWholeRowOracle","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVChunk14OracleTests.TemporalBlockBootstrap_MatchesIndependentWholeRowOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:44:23.2974291+00:00","wallSeconds":3.4735762,"testSeconds":0.4307245,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094423-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_TemporalBlockBootstrap_MatchesIndependentWholeRowOracle\\haden_HADEN_2026-09-09_15_44_26.546.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094423-297-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_TemporalBlockBootstrap_MatchesIndependentWholeRowOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094427-350-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_VarianceInflation_UsesExactIndependentAnalyticalTransformation","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVChunk14OracleTests.VarianceInflation_UsesExactIndependentAnalyticalTransformation","phase":"stabilized-pass","startedUtc":"2026-09-09T15:44:27.3508486+00:00","wallSeconds":3.1228954,"testSeconds":0.104843,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094427-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_VarianceInflation_UsesExactIndependentAnalyticalTransformation\\haden_HADEN_2026-09-09_15_44_30.245.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094427-350-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_VarianceInflation_UsesExactIndependentAnalyticalTransformation.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094431-013-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVInformationCriteriaTests_MissingSiteModel_InformationCriteria_UseRowYearBlocks","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVInformationCriteriaTests.MissingSiteModel_InformationCriteria_UseRowYearBlocks","phase":"stabilized-pass","startedUtc":"2026-09-09T15:44:31.0136785+00:00","wallSeconds":17.0287809,"testSeconds":13.8980147,"buildSeconds":1.55,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094431-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVInformationCriteriaTests_MissingSiteModel_InformationCriteria_UseRowYearBlocks\\haden_HADEN_2026-09-09_15_44_47.804.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094431-013-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVInformationCriteriaTests_MissingSiteModel_InformationCriteria_UseRowYearBlocks.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094448-642-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_CompleteRows_CopulaLikelihood_MatchesIndependentOracle","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVLikelihoodOracleTests.CompleteRows_CopulaLikelihood_MatchesIndependentOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:44:48.6426875+00:00","wallSeconds":3.222324,"testSeconds":0.0321341,"buildSeconds":1.55,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094448-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_CompleteRows_CopulaLikelihood_MatchesIndependentOracle\\haden_HADEN_2026-09-09_15_44_51.632.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094448-642-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_CompleteRows_CopulaLikelihood_MatchesIndependentOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094452-479-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_LocationErrorModel_DataLogLikelihood_ExcludesProcessDensity","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVLikelihoodOracleTests.LocationErrorModel_DataLogLikelihood_ExcludesProcessDensity","phase":"stabilized-pass","startedUtc":"2026-09-09T15:44:52.4792766+00:00","wallSeconds":3.3236837,"testSeconds":0.0378353,"buildSeconds":1.6,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094452-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_LocationErrorModel_DataLogLikelihood_ExcludesProcessDensity\\haden_HADEN_2026-09-09_15_44_55.547.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094452-479-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_LocationErrorModel_DataLogLikelihood_ExcludesProcessDensity.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094456-402-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_LocationErrorModel_PosteriorKernel_IsInvariantToTheDecomposition","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVLikelihoodOracleTests.LocationErrorModel_PosteriorKernel_IsInvariantToTheDecomposition","phase":"stabilized-pass","startedUtc":"2026-09-09T15:44:56.4027677+00:00","wallSeconds":3.2544596,"testSeconds":0.0378647,"buildSeconds":1.56,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094456-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_LocationErrorModel_PosteriorKernel_IsInvariantToTheDecomposition\\haden_HADEN_2026-09-09_15_44_59.425.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094456-402-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_LocationErrorModel_PosteriorKernel_IsInvariantToTheDecomposition.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094500-269-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_LocationErrorModel_ScalarAndPointwiseDecompositionsAgree","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVLikelihoodOracleTests.LocationErrorModel_ScalarAndPointwiseDecompositionsAgree","phase":"stabilized-pass","startedUtc":"2026-09-09T15:45:00.2694568+00:00","wallSeconds":3.3166378,"testSeconds":0.0429481,"buildSeconds":1.6,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094500-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_LocationErrorModel_ScalarAndPointwiseDecompositionsAgree\\haden_HADEN_2026-09-09_15_45_03.344.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094500-269-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_LocationErrorModel_ScalarAndPointwiseDecompositionsAgree.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094504-194-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_LocationErrorModel_ScalarAndPointwiseGradientsAgree","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVLikelihoodOracleTests.LocationErrorModel_ScalarAndPointwiseGradientsAgree","phase":"stabilized-pass","startedUtc":"2026-09-09T15:45:04.1942582+00:00","wallSeconds":3.304359,"testSeconds":0.041534,"buildSeconds":1.61,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094504-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_LocationErrorModel_ScalarAndPointwiseGradientsAgree\\haden_HADEN_2026-09-09_15_45_07.264.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094504-194-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_LocationErrorModel_ScalarAndPointwiseGradientsAgree.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094508-089-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_MarginalOnly_WithoutCopula_MatchesIndependentOracle","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVLikelihoodOracleTests.MarginalOnly_WithoutCopula_MatchesIndependentOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:45:08.0890946+00:00","wallSeconds":3.3380033,"testSeconds":0.0286093,"buildSeconds":1.56,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094508-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_MarginalOnly_WithoutCopula_MatchesIndependentOracle\\haden_HADEN_2026-09-09_15_45_11.200.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094508-089-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_MarginalOnly_WithoutCopula_MatchesIndependentOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094512-028-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_MissingSites_DataLogLikelihood_UsesObservedSiteCopulaSubmatrix","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVLikelihoodOracleTests.MissingSites_DataLogLikelihood_UsesObservedSiteCopulaSubmatrix","phase":"stabilized-pass","startedUtc":"2026-09-09T15:45:12.0285247+00:00","wallSeconds":3.2159281,"testSeconds":0.0330146,"buildSeconds":1.55,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094512-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_MissingSites_DataLogLikelihood_UsesObservedSiteCopulaSubmatrix\\haden_HADEN_2026-09-09_15_45_15.011.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094512-028-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_MissingSites_DataLogLikelihood_UsesObservedSiteCopulaSubmatrix.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094515-846-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_MissingSites_PointwiseRows_MatchObservedSubsetOracle","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVLikelihoodOracleTests.MissingSites_PointwiseRows_MatchObservedSubsetOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:45:15.8464344+00:00","wallSeconds":3.3854282,"testSeconds":0.0343252,"buildSeconds":1.56,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094515-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_MissingSites_PointwiseRows_MatchObservedSubsetOracle\\haden_HADEN_2026-09-09_15_45_18.844.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094515-846-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_MissingSites_PointwiseRows_MatchObservedSubsetOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094519-818-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVMLERecoveryTests_MLE_BasicHomogeneous_RecoversParameters","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVMLERecoveryTests.MLE_BasicHomogeneous_RecoversParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:45:19.8188071+00:00","wallSeconds":3.6698662,"testSeconds":0.5840611,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094519-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVMLERecoveryTests_MLE_BasicHomogeneous_RecoversParameters\\haden_HADEN_2026-09-09_15_45_23.259.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094519-818-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVMLERecoveryTests_MLE_BasicHomogeneous_RecoversParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094524-083-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVMLERecoveryTests_MLE_WithCopula_RecoversParameters","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVMLERecoveryTests.MLE_WithCopula_RecoversParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:45:24.0838714+00:00","wallSeconds":5.2700262,"testSeconds":2.1123091,"buildSeconds":1.55,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094524-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVMLERecoveryTests_MLE_WithCopula_RecoversParameters\\haden_HADEN_2026-09-09_15_45_29.122.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094524-083-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVMLERecoveryTests_MLE_WithCopula_RecoversParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094529-920-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVSimulationVerificationTests_GenerateRandomValues_WithCopula_ReproducesTheFittedIntersiteDependence","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVSimulationVerificationTests.GenerateRandomValues_WithCopula_ReproducesTheFittedIntersiteDependence","phase":"stabilized-pass","startedUtc":"2026-09-09T15:45:29.9205879+00:00","wallSeconds":3.2578799,"testSeconds":0.0947151,"buildSeconds":1.56,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094529-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVSimulationVerificationTests_GenerateRandomValues_WithCopula_ReproducesTheFittedIntersiteDependence\\haden_HADEN_2026-09-09_15_45_32.952.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094529-920-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVSimulationVerificationTests_GenerateRandomValues_WithCopula_ReproducesTheFittedIntersiteDependence.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GeneralizedExtremeValue","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094533-770-RMC_BestFit_Verification_TimeSeriesAnalysis_ARAnalysisTests_Test_EstimateParameters_AR1","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.ARAnalysisTests.Test_EstimateParameters_AR1","phase":"stabilized-pass","startedUtc":"2026-09-09T15:45:33.7708227+00:00","wallSeconds":22.5165855,"testSeconds":19.5174062,"buildSeconds":1.35,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094533-RMC_BestFit_Verification_TimeSeriesAnalysis_ARAnalysisTests_Test_EstimateParameters_AR1\\haden_HADEN_2026-09-09_15_45_56.041.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094533-770-RMC_BestFit_Verification_TimeSeriesAnalysis_ARAnalysisTests_Test_EstimateParameters_AR1.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094556-825-RMC_BestFit_Verification_TimeSeriesAnalysis_AutoRegressiveMLERecoveryTests_Test_EstimateParameters_AR1","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.AutoRegressiveMLERecoveryTests.Test_EstimateParameters_AR1","phase":"stabilized-pass","startedUtc":"2026-09-09T15:45:56.8259811+00:00","wallSeconds":3.1428407,"testSeconds":0.1123621,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094556-RMC_BestFit_Verification_TimeSeriesAnalysis_AutoRegressiveMLERecoveryTests_Test_EstimateParameters_AR1\\haden_HADEN_2026-09-09_15_45_59.744.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094556-825-RMC_BestFit_Verification_TimeSeriesAnalysis_AutoRegressiveMLERecoveryTests_Test_EstimateParameters_AR1.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094600-530-RMC_BestFit_Verification_TimeSeriesAnalysis_MAAnalysisTests_Test_EstimateParameters_MA1","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.MAAnalysisTests.Test_EstimateParameters_MA1","phase":"stabilized-pass","startedUtc":"2026-09-09T15:46:00.5301783+00:00","wallSeconds":20.1449789,"testSeconds":17.0469637,"buildSeconds":1.52,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094600-RMC_BestFit_Verification_TimeSeriesAnalysis_MAAnalysisTests_Test_EstimateParameters_MA1\\haden_HADEN_2026-09-09_15_46_20.440.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094600-530-RMC_BestFit_Verification_TimeSeriesAnalysis_MAAnalysisTests_Test_EstimateParameters_MA1.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094621-192-RMC_BestFit_Verification_TimeSeriesAnalysis_MovingAverageMLERecoveryTests_Test_EstimateParameters_MA1","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.MovingAverageMLERecoveryTests.Test_EstimateParameters_MA1","phase":"stabilized-pass","startedUtc":"2026-09-09T15:46:21.1928831+00:00","wallSeconds":3.0692465,"testSeconds":0.1080048,"buildSeconds":1.47,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094621-RMC_BestFit_Verification_TimeSeriesAnalysis_MovingAverageMLERecoveryTests_Test_EstimateParameters_MA1\\haden_HADEN_2026-09-09_15_46_24.040.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094621-192-RMC_BestFit_Verification_TimeSeriesAnalysis_MovingAverageMLERecoveryTests_Test_EstimateParameters_MA1.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094624-775-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesChunk13OracleTests_ArimaxTrendSeasonalityAndCovariatesMatchIndependentPythonOracle","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesChunk13OracleTests.ArimaxTrendSeasonalityAndCovariatesMatchIndependentPythonOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:46:24.7759330+00:00","wallSeconds":2.9543471,"testSeconds":0.0403432,"buildSeconds":1.41,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094624-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesChunk13OracleTests_ArimaxTrendSeasonalityAndCovariatesMatchIndependentPythonOracle\\haden_HADEN_2026-09-09_15_46_27.502.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094624-775-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesChunk13OracleTests_ArimaxTrendSeasonalityAndCovariatesMatchIndependentPythonOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094628-237-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesChunk13OracleTests_FirstOrderConditionalObjectivesMatchIndependentPythonOracle","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesChunk13OracleTests.FirstOrderConditionalObjectivesMatchIndependentPythonOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:46:28.2371983+00:00","wallSeconds":2.991024,"testSeconds":0.0287677,"buildSeconds":1.41,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094628-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesChunk13OracleTests_FirstOrderConditionalObjectivesMatchIndependentPythonOracle\\haden_HADEN_2026-09-09_15_46_30.949.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094628-237-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesChunk13OracleTests_FirstOrderConditionalObjectivesMatchIndependentPythonOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094631-746-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesChunk13OracleTests_HigherOrderArAndMaResponsesMatchIndependentPythonOracle","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesChunk13OracleTests.HigherOrderArAndMaResponsesMatchIndependentPythonOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:46:31.7467164+00:00","wallSeconds":2.9858998,"testSeconds":0.0260521,"buildSeconds":1.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094631-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesChunk13OracleTests_HigherOrderArAndMaResponsesMatchIndependentPythonOracle\\haden_HADEN_2026-09-09_15_46_34.490.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094631-746-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesChunk13OracleTests_HigherOrderArAndMaResponsesMatchIndependentPythonOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094635-249-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesChunk13OracleTests_PureArAndMaThroughArimaMatchIndependentPythonOracle","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesChunk13OracleTests.PureArAndMaThroughArimaMatchIndependentPythonOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:46:35.2499081+00:00","wallSeconds":2.9868442,"testSeconds":0.0231956,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094635-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesChunk13OracleTests_PureArAndMaThroughArimaMatchIndependentPythonOracle\\haden_HADEN_2026-09-09_15_46_38.018.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094635-249-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesChunk13OracleTests_PureArAndMaThroughArimaMatchIndependentPythonOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094638-758-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArAndMaTransformedGeneratorsMatchIndependentOracle","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.ArAndMaTransformedGeneratorsMatchIndependentOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:46:38.7582551+00:00","wallSeconds":3.0363205,"testSeconds":0.0158559,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094638-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArAndMaTransformedGeneratorsMatchIndependentOracle\\haden_HADEN_2026-09-09_15_46_41.513.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094638-758-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArAndMaTransformedGeneratorsMatchIndependentOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094642-318-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArimaAndArimaxPredictionUncertaintyBeginsAtForecastBoundary","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.ArimaAndArimaxPredictionUncertaintyBeginsAtForecastBoundary","phase":"stabilized-pass","startedUtc":"2026-09-09T15:46:42.3180550+00:00","wallSeconds":2.870945,"testSeconds":0.0299951,"buildSeconds":1.3,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094642-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArimaAndArimaxPredictionUncertaintyBeginsAtForecastBoundary\\haden_HADEN_2026-09-09_15_46_44.944.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094642-318-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArimaAndArimaxPredictionUncertaintyBeginsAtForecastBoundary.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094645-709-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArimaDifferencedTransformedGeneratorMatchesIndependentOracle","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.ArimaDifferencedTransformedGeneratorMatchesIndependentOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:46:45.7096021+00:00","wallSeconds":3.0673485,"testSeconds":0.0213437,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094645-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArimaDifferencedTransformedGeneratorMatchesIndependentOracle\\haden_HADEN_2026-09-09_15_46_48.503.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094645-709-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArimaDifferencedTransformedGeneratorMatchesIndependentOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094649-297-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArimaxDifferencedLikelihoodMatchesDateIndexedIndependentOracle","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.ArimaxDifferencedLikelihoodMatchesDateIndexedIndependentOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:46:49.2976286+00:00","wallSeconds":3.0224656,"testSeconds":0.0402901,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094649-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArimaxDifferencedLikelihoodMatchesDateIndexedIndependentOracle\\haden_HADEN_2026-09-09_15_46_52.099.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094649-297-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArimaxDifferencedLikelihoodMatchesDateIndexedIndependentOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094652-838-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArimaxTransformedDifferencedGeneratorMatchesIndependentOracle","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.ArimaxTransformedDifferencedGeneratorMatchesIndependentOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:46:52.8387138+00:00","wallSeconds":3.0312625,"testSeconds":0.0313852,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094652-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArimaxTransformedDifferencedGeneratorMatchesIndependentOracle\\haden_HADEN_2026-09-09_15_46_55.647.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094652-838-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArimaxTransformedDifferencedGeneratorMatchesIndependentOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094656-430-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_InformationCriteriaUseDataLikelihoodAtMapAndExcludePrior","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.InformationCriteriaUseDataLikelihoodAtMapAndExcludePrior","phase":"stabilized-pass","startedUtc":"2026-09-09T15:46:56.4308607+00:00","wallSeconds":3.0983516,"testSeconds":0.1272558,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094656-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_InformationCriteriaUseDataLikelihoodAtMapAndExcludePrior\\haden_HADEN_2026-09-09_15_46_59.304.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094656-430-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_InformationCriteriaUseDataLikelihoodAtMapAndExcludePrior.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094700-052-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_InvalidScaleBehaviorMatchesScalarAndPointwiseOracle","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.InvalidScaleBehaviorMatchesScalarAndPointwiseOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:47:00.0529937+00:00","wallSeconds":3.0102528,"testSeconds":0.0433801,"buildSeconds":1.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094700-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_InvalidScaleBehaviorMatchesScalarAndPointwiseOracle\\haden_HADEN_2026-09-09_15_47_02.832.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094700-052-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_InvalidScaleBehaviorMatchesScalarAndPointwiseOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094703-583-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ManualTransformLambdaRebuildMatchesIndependentLikelihoodOracle","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.ManualTransformLambdaRebuildMatchesIndependentLikelihoodOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:47:03.5835816+00:00","wallSeconds":3.0252232,"testSeconds":0.0496551,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094703-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ManualTransformLambdaRebuildMatchesIndependentLikelihoodOracle\\haden_HADEN_2026-09-09_15_47_06.385.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094703-583-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ManualTransformLambdaRebuildMatchesIndependentLikelihoodOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094707-138-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_TransformedArimaAndArimaxForecastsMatchModelScaleOracle","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.TransformedArimaAndArimaxForecastsMatchModelScaleOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:47:07.1386371+00:00","wallSeconds":3.1464684,"testSeconds":0.0373606,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094707-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_TransformedArimaAndArimaxForecastsMatchModelScaleOracle\\haden_HADEN_2026-09-09_15_47_10.007.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094707-138-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_TransformedArimaAndArimaxForecastsMatchModelScaleOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094710-831-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_TransformLambdaMatchesIndependentTrainingOnlyOracle","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.TransformLambdaMatchesIndependentTrainingOnlyOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:47:10.8318102+00:00","wallSeconds":3.0907902,"testSeconds":0.0485628,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094710-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_TransformLambdaMatchesIndependentTrainingOnlyOracle\\haden_HADEN_2026-09-09_15_47_13.689.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094710-831-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_TransformLambdaMatchesIndependentTrainingOnlyOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094714-458-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentRecoveryTests_BayesianArima111LogD1RecoversGeneratingParameters","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentRecoveryTests.BayesianArima111LogD1RecoversGeneratingParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:47:14.4588981+00:00","wallSeconds":27.0614513,"testSeconds":23.9219815,"buildSeconds":1.52,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094714-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentRecoveryTests_BayesianArima111LogD1RecoversGeneratingParameters\\haden_HADEN_2026-09-09_15_47_41.238.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094714-458-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentRecoveryTests_BayesianArima111LogD1RecoversGeneratingParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094742-118-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentRecoveryTests_BayesianArimax10D1LevelCovariateRecoversGeneratingParameters","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentRecoveryTests.BayesianArimax10D1LevelCovariateRecoversGeneratingParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:47:42.1187459+00:00","wallSeconds":31.7763841,"testSeconds":28.5369382,"buildSeconds":1.58,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094742-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentRecoveryTests_BayesianArimax10D1LevelCovariateRecoversGeneratingParameters\\haden_HADEN_2026-09-09_15_48_13.653.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094742-118-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentRecoveryTests_BayesianArimax10D1LevelCovariateRecoversGeneratingParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094814-407-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentRecoveryTests_MleArima111LogD1RecoversGeneratingParameters","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentRecoveryTests.MleArima111LogD1RecoversGeneratingParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:48:14.4072735+00:00","wallSeconds":3.0161187,"testSeconds":0.1196554,"buildSeconds":1.41,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094814-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentRecoveryTests_MleArima111LogD1RecoversGeneratingParameters\\haden_HADEN_2026-09-09_15_48_17.196.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094814-407-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentRecoveryTests_MleArima111LogD1RecoversGeneratingParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094817-930-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentRecoveryTests_MleArimax10D1LevelCovariateRecoversGeneratingParameters","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentRecoveryTests.MleArimax10D1LevelCovariateRecoversGeneratingParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T15:48:17.9306539+00:00","wallSeconds":3.3526555,"testSeconds":0.3664726,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094817-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentRecoveryTests_MleArimax10D1LevelCovariateRecoversGeneratingParameters\\haden_HADEN_2026-09-09_15_48_21.056.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094817-930-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentRecoveryTests_MleArimax10D1LevelCovariateRecoversGeneratingParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094821-797-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example1","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CExampleTests.Test_Example1","phase":"stabilized-pass","startedUtc":"2026-09-09T15:48:21.7976966+00:00","wallSeconds":3.0521215,"testSeconds":0.133414,"buildSeconds":1.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094821-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example1\\haden_HADEN_2026-09-09_15_48_24.625.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094821-797-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example1.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094825-353-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example2","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CExampleTests.Test_Example2","phase":"stabilized-pass","startedUtc":"2026-09-09T15:48:25.3537467+00:00","wallSeconds":3.0299029,"testSeconds":0.1218349,"buildSeconds":1.41,"result":"Failed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-094825-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example2\\haden_HADEN_2026-09-09_15_48_28.151.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094825-353-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example2.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":"Assert.AreEqual failed. Expected a difference no greater than <0.001> between expected value <3.02266304070359> and actual value <0.0017102440398818303>. Parameter[0] mismatch for Example 2 (Orestimba Creek).","note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-094850-405-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example2","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CExampleTests.Test_Example2","phase":"b17c-example2-baseline","startedUtc":"2026-09-09T15:48:50.4078563+00:00","wallSeconds":3.3822533,"testSeconds":0.1319459,"buildSeconds":1.52,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260909-094850-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example2\\haden_HADEN_2026-09-09_15_48_53.535.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094850-405-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example2.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"d80bfa8621c48a78cf4ebad7f01326841fac37aa","sourceSha256":"95A9BB4F8927EF0934A13A39B566C1FA30BDB42B2F95AC965746DD122DB39BA0"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"17C77A3DDAE1EF7B39BA3945DB5F6FB61F93F3D1D57FD48AF96248C9A6054F43"},"numericsAssemblySha256":"3E82F983E4795FD50104CBF0F950A7513BDF94540C3397401A8EC5BBF1A6C60F","bestFitAssemblySha256":"67A7E4809110D816C06ED83E4F9068F6E6FA78E490A5486FCEF62A56D7019DC0","verificationAssemblySha256":"542282B3090D914193685DF647EB18960BC31D061A17CACA7E63A2AA92CC9E46","dependencies":["LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Immediate unchanged pinned-baseline comparison after current fitted Mu 0.0017102440398818303 fails reference 3.02266304070359."} +{"runId":"20260909-094914-274-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example2","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CExampleTests.Test_Example2","phase":"b17c-example2-old-integration-new-numerics","startedUtc":"2026-09-09T15:49:14.2768305+00:00","wallSeconds":9.8802979,"testSeconds":0.1285302,"buildSeconds":7.86,"result":"Failed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260909-094914-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example2\\haden_HADEN_2026-09-09_15_49_23.900.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-094914-274-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example2.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"B1D945F9892138878B65FDBDF30D36DB1CD5ED4EB3BD23843CE9B61D6AB4695C"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"17C77A3DDAE1EF7B39BA3945DB5F6FB61F93F3D1D57FD48AF96248C9A6054F43"},"numericsAssemblySha256":"DFD7DA9BBCBA1A128E01EAD5AC298EA7C159049A04DE2AFA4A0C846380D6A933","bestFitAssemblySha256":"B1F1D8998D61E4C2904B91943A84CEEA2BE653CEBA43D5F335C29ECD8465C91D","verificationAssemblySha256":"50ADDBDFCE9466BC0402A38A6A890F39418B4E5AF8126FF3BA74DA0F437564DA","dependencies":["LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":"Assert.AreEqual failed. Expected a difference no greater than <0.001> between expected value <3.02266304070359> and actual value <0.0017102440398818303>. Parameter[0] mismatch for Example 2 (Orestimba Creek).","note":"Factor isolation: unchanged pre-integration BestFit with repaired Numerics."} +{"runId":"20260909-095520-121-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example2","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CExampleTests.Test_Example2","phase":"b17c-example2-restored-constraints","startedUtc":"2026-09-09T15:55:20.1241090+00:00","wallSeconds":3.4372326,"testSeconds":0.1346806,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095520-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example2\\haden_HADEN_2026-09-09_15_55_23.069.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095520-121-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example2.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F45369C584C531E32F53923AFA3FD7D9DEBAE107691C4743ED5666A6CC58CE70"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"19622C534061D8D0ACFA29E9843AB71BD697D51865681584BDE9BD1DDE2B18F4","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Restore legacy nonpositive sample initialization/constraints, including LP3 0.01 and LogNormal 0.1 substitutions; no verification assertions changed."} +{"runId":"20260909-095732-480-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example2","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CExampleTests.Test_Example2","phase":"stabilized-pass","startedUtc":"2026-09-09T15:57:32.4829844+00:00","wallSeconds":4.1338602,"testSeconds":0.1403082,"buildSeconds":2.39,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095732-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example2\\haden_HADEN_2026-09-09_15_57_36.373.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095732-480-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example2.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095737-384-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example3","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CExampleTests.Test_Example3","phase":"stabilized-pass","startedUtc":"2026-09-09T15:57:37.3842690+00:00","wallSeconds":3.0532337,"testSeconds":0.0929715,"buildSeconds":1.42,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095737-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example3\\haden_HADEN_2026-09-09_15_57_40.221.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095737-384-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example3.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095740-981-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example4","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CExampleTests.Test_Example4","phase":"stabilized-pass","startedUtc":"2026-09-09T15:57:40.9816948+00:00","wallSeconds":3.2860378,"testSeconds":0.2376134,"buildSeconds":1.51,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095741-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example4\\haden_HADEN_2026-09-09_15_57_44.042.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095740-981-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example4.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095744-925-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example5","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CExampleTests.Test_Example5","phase":"stabilized-pass","startedUtc":"2026-09-09T15:57:44.9256294+00:00","wallSeconds":3.0669634,"testSeconds":0.0808763,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095744-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example5\\haden_HADEN_2026-09-09_15_57_47.777.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095744-925-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example5.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095748-619-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example6","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CExampleTests.Test_Example6","phase":"stabilized-pass","startedUtc":"2026-09-09T15:57:48.6195672+00:00","wallSeconds":3.2185904,"testSeconds":0.184693,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095748-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example6\\haden_HADEN_2026-09-09_15_57_51.623.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095748-619-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example6.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095752-409-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example7","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CExampleTests.Test_Example7","phase":"stabilized-pass","startedUtc":"2026-09-09T15:57:52.4094469+00:00","wallSeconds":3.2632843,"testSeconds":0.3564724,"buildSeconds":1.29,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095752-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example7\\haden_HADEN_2026-09-09_15_57_55.445.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095752-409-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example7.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095756-301-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMu_N100","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogNormal_PenalityOnMu_N100","phase":"stabilized-pass","startedUtc":"2026-09-09T15:57:56.3016152+00:00","wallSeconds":3.4758693,"testSeconds":0.0509706,"buildSeconds":1.75,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095756-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMu_N100\\haden_HADEN_2026-09-09_15_57_59.531.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095756-301-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMu_N100.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095800-411-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMu_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogNormal_PenalityOnMu_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T15:58:00.4114687+00:00","wallSeconds":3.1330825,"testSeconds":0.0508698,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095800-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMu_N25\\haden_HADEN_2026-09-09_15_58_03.324.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095800-411-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMu_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095804-139-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMuAndSigma_N100","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogNormal_PenalityOnMuAndSigma_N100","phase":"stabilized-pass","startedUtc":"2026-09-09T15:58:04.1392088+00:00","wallSeconds":3.046432,"testSeconds":0.0939367,"buildSeconds":1.37,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095804-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMuAndSigma_N100\\haden_HADEN_2026-09-09_15_58_06.952.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095804-139-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMuAndSigma_N100.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095807-774-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMuAndSigma_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogNormal_PenalityOnMuAndSigma_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T15:58:07.7749788+00:00","wallSeconds":3.1319398,"testSeconds":0.0516745,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095807-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMuAndSigma_N25\\haden_HADEN_2026-09-09_15_58_10.683.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095807-774-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMuAndSigma_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095811-521-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMuSigmaAndQ99_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogNormal_PenalityOnMuSigmaAndQ99_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T15:58:11.5215269+00:00","wallSeconds":3.1548204,"testSeconds":0.0560084,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095811-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMuSigmaAndQ99_N25\\haden_HADEN_2026-09-09_15_58_14.441.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095811-521-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMuSigmaAndQ99_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095815-255-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnQ99_N100","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogNormal_PenalityOnQ99_N100","phase":"stabilized-pass","startedUtc":"2026-09-09T15:58:15.2557517+00:00","wallSeconds":3.0071602,"testSeconds":0.0598315,"buildSeconds":1.32,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095815-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnQ99_N100\\haden_HADEN_2026-09-09_15_58_17.987.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095815-255-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnQ99_N100.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095818-841-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnQ99_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogNormal_PenalityOnQ99_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T15:58:18.8416498+00:00","wallSeconds":3.0659928,"testSeconds":0.052504,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095818-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnQ99_N25\\haden_HADEN_2026-09-09_15_58_21.692.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095818-841-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnQ99_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095822-481-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnSigma_N100","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogNormal_PenalityOnSigma_N100","phase":"stabilized-pass","startedUtc":"2026-09-09T15:58:22.4816539+00:00","wallSeconds":3.1049582,"testSeconds":0.0532836,"buildSeconds":1.51,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095822-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnSigma_N100\\haden_HADEN_2026-09-09_15_58_25.335.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095822-481-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnSigma_N100.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095826-152-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnSigma_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogNormal_PenalityOnSigma_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T15:58:26.1523963+00:00","wallSeconds":2.8934221,"testSeconds":0.0484823,"buildSeconds":1.31,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095826-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnSigma_N25\\haden_HADEN_2026-09-09_15_58_28.807.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095826-152-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnSigma_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095829-630-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_Exponential_GmmRecoversGeneratingParent","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CSyntheticDataTests.Exponential_GmmRecoversGeneratingParent","phase":"stabilized-pass","startedUtc":"2026-09-09T15:58:29.6305627+00:00","wallSeconds":3.0494157,"testSeconds":0.0851852,"buildSeconds":1.47,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095829-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_Exponential_GmmRecoversGeneratingParent\\haden_HADEN_2026-09-09_15_58_32.462.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095829-630-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_Exponential_GmmRecoversGeneratingParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095833-282-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_Gamma_GmmRecoversGeneratingParent","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CSyntheticDataTests.Gamma_GmmRecoversGeneratingParent","phase":"stabilized-pass","startedUtc":"2026-09-09T15:58:33.2826428+00:00","wallSeconds":3.1655219,"testSeconds":0.0533527,"buildSeconds":1.58,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095833-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_Gamma_GmmRecoversGeneratingParent\\haden_HADEN_2026-09-09_15_58_36.217.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095833-282-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_Gamma_GmmRecoversGeneratingParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095837-056-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_LogNormal_GmmRecoversGeneratingLog10Parent","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CSyntheticDataTests.LogNormal_GmmRecoversGeneratingLog10Parent","phase":"stabilized-pass","startedUtc":"2026-09-09T15:58:37.0561610+00:00","wallSeconds":3.0313597,"testSeconds":0.0524122,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095837-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_LogNormal_GmmRecoversGeneratingLog10Parent\\haden_HADEN_2026-09-09_15_58_39.854.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095837-056-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_LogNormal_GmmRecoversGeneratingLog10Parent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095840-718-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_Normal_GmmRecoversGeneratingParent","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CSyntheticDataTests.Normal_GmmRecoversGeneratingParent","phase":"stabilized-pass","startedUtc":"2026-09-09T15:58:40.7184553+00:00","wallSeconds":3.1914701,"testSeconds":0.0611473,"buildSeconds":1.55,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095840-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_Normal_GmmRecoversGeneratingParent\\haden_HADEN_2026-09-09_15_58_43.678.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095840-718-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_Normal_GmmRecoversGeneratingParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095844-472-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_CombinationRules_SatisfyTheoreticalBracketingAndRemainDistinct","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.CombinationRules_SatisfyTheoreticalBracketingAndRemainDistinct","phase":"stabilized-pass","startedUtc":"2026-09-09T15:58:44.4725765+00:00","wallSeconds":3.544226,"testSeconds":0.3880561,"buildSeconds":1.57,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095844-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_CombinationRules_SatisfyTheoreticalBracketingAndRemainDistinct\\haden_HADEN_2026-09-09_15_58_47.799.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095844-472-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_CombinationRules_SatisfyTheoreticalBracketingAndRemainDistinct.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095848-606-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_CorrelationMatrix_MinimumAndMaximumMatchBivariateNormalOrthants","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.CorrelationMatrix_MinimumAndMaximumMatchBivariateNormalOrthants","phase":"stabilized-pass","startedUtc":"2026-09-09T15:58:48.6061201+00:00","wallSeconds":3.4512943,"testSeconds":0.1596569,"buildSeconds":1.66,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095848-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_CorrelationMatrix_MinimumAndMaximumMatchBivariateNormalOrthants\\haden_HADEN_2026-09-09_15_58_51.821.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095848-606-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_CorrelationMatrix_MinimumAndMaximumMatchBivariateNormalOrthants.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095852-622-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MaximumComposite_MatchesIndependentAndComonotonicClosedForms","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.MaximumComposite_MatchesIndependentAndComonotonicClosedForms","phase":"stabilized-pass","startedUtc":"2026-09-09T15:58:52.6227659+00:00","wallSeconds":3.4472394,"testSeconds":0.2857131,"buildSeconds":1.56,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095852-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MaximumComposite_MatchesIndependentAndComonotonicClosedForms\\haden_HADEN_2026-09-09_15_58_55.838.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095852-622-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MaximumComposite_MatchesIndependentAndComonotonicClosedForms.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095856-652-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MaximumPosterior_MatchesCompleteCartesianOracle","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.MaximumPosterior_MatchesCompleteCartesianOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:58:56.6524767+00:00","wallSeconds":7.2993695,"testSeconds":4.0849096,"buildSeconds":1.59,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095856-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MaximumPosterior_MatchesCompleteCartesianOracle\\haden_HADEN_2026-09-09_15_59_03.724.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095856-652-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MaximumPosterior_MatchesCompleteCartesianOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095904-513-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MinimumComposite_MatchesIndependentAndComonotonicClosedForms","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.MinimumComposite_MatchesIndependentAndComonotonicClosedForms","phase":"stabilized-pass","startedUtc":"2026-09-09T15:59:04.5132807+00:00","wallSeconds":3.2495346,"testSeconds":0.2729653,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095904-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MinimumComposite_MatchesIndependentAndComonotonicClosedForms\\haden_HADEN_2026-09-09_15_59_07.541.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095904-513-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MinimumComposite_MatchesIndependentAndComonotonicClosedForms.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095908-304-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MinimumPosterior_MatchesCompleteCartesianOracle","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.MinimumPosterior_MatchesCompleteCartesianOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:59:08.3048227+00:00","wallSeconds":7.2332019,"testSeconds":4.2666112,"buildSeconds":1.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095908-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MinimumPosterior_MatchesCompleteCartesianOracle\\haden_HADEN_2026-09-09_15_59_15.293.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095908-304-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MinimumPosterior_MatchesCompleteCartesianOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095916-106-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MixtureCdf_MatchesExactWeightedNormalSum","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.MixtureCdf_MatchesExactWeightedNormalSum","phase":"stabilized-pass","startedUtc":"2026-09-09T15:59:16.1063282+00:00","wallSeconds":3.3302605,"testSeconds":0.1773878,"buildSeconds":1.53,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095916-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MixtureCdf_MatchesExactWeightedNormalSum\\haden_HADEN_2026-09-09_15_59_19.216.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095916-106-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MixtureCdf_MatchesExactWeightedNormalSum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095920-039-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MixturePosterior_MatchesCompleteCartesianOracle","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.MixturePosterior_MatchesCompleteCartesianOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T15:59:20.0397053+00:00","wallSeconds":7.1638178,"testSeconds":3.8709554,"buildSeconds":1.56,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095920-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MixturePosterior_MatchesCompleteCartesianOracle\\haden_HADEN_2026-09-09_15_59_26.901.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095920-039-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MixturePosterior_MatchesCompleteCartesianOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095927-705-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MixtureQuantiles_InvertAnalyticWeightedNormalCdf","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.MixtureQuantiles_InvertAnalyticWeightedNormalCdf","phase":"stabilized-pass","startedUtc":"2026-09-09T15:59:27.7060024+00:00","wallSeconds":3.146184,"testSeconds":0.1706014,"buildSeconds":1.38,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095927-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MixtureQuantiles_InvertAnalyticWeightedNormalCdf\\haden_HADEN_2026-09-09_15_59_30.505.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095927-705-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MixtureQuantiles_InvertAnalyticWeightedNormalCdf.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095931-362-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MixtureQuantiles_MatchPublishedRMistrTable45","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.MixtureQuantiles_MatchPublishedRMistrTable45","phase":"stabilized-pass","startedUtc":"2026-09-09T15:59:31.3627419+00:00","wallSeconds":3.037764,"testSeconds":0.1670676,"buildSeconds":1.39,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095931-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MixtureQuantiles_MatchPublishedRMistrTable45\\haden_HADEN_2026-09-09_15_59_34.176.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095931-362-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MixtureQuantiles_MatchPublishedRMistrTable45.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095934-907-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureExternalPackageOracleTests_OrdinaryNormalMixture2D_MatchesScikitLearnArtifact","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureExternalPackageOracleTests.OrdinaryNormalMixture2D_MatchesScikitLearnArtifact","phase":"stabilized-pass","startedUtc":"2026-09-09T15:59:34.9071060+00:00","wallSeconds":3.1228225,"testSeconds":0.2734552,"buildSeconds":1.38,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095934-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureExternalPackageOracleTests_OrdinaryNormalMixture2D_MatchesScikitLearnArtifact\\haden_HADEN_2026-09-09_15_59_37.815.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095934-907-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureExternalPackageOracleTests_OrdinaryNormalMixture2D_MatchesScikitLearnArtifact.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-095938-549-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessExternalPackageOracleTests_StationaryPoissonGpa_MatchesSciPyArtifact","method":"RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessExternalPackageOracleTests.StationaryPoissonGpa_MatchesSciPyArtifact","phase":"stabilized-pass","startedUtc":"2026-09-09T15:59:38.5492718+00:00","wallSeconds":2.9711169,"testSeconds":0.0526163,"buildSeconds":1.41,"result":"Failed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-095938-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessExternalPackageOracleTests_StationaryPoissonGpa_MatchesSciPyArtifact\\haden_HADEN_2026-09-09_15_59_41.277.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-095938-549-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessExternalPackageOracleTests_StationaryPoissonGpa_MatchesSciPyArtifact.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"AC32168F55B26942F0A073CF8E4B2D6C42589048FBA0ADAFCB7BBAC0070D5166"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"C567C9542699D18B679F8A91E9A8D0462E94F7C6A749ACC3C65F00C3EA1B8BB7","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":"Test method RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessExternalPackageOracleTests.StationaryPoissonGpa_MatchesSciPyArtifact threw exception: \nSystem.ArgumentException: The length of the parameter list is incorrect. (Parameter 'parameters')","note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-100004-659-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessExternalPackageOracleTests_StationaryPoissonGpa_MatchesSciPyArtifact","method":"RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessExternalPackageOracleTests.StationaryPoissonGpa_MatchesSciPyArtifact","phase":"point-process-scipy-baseline","startedUtc":"2026-09-09T16:00:04.6612986+00:00","wallSeconds":5.0461361,"testSeconds":0.0452986,"buildSeconds":3.07,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260909-100004-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessExternalPackageOracleTests_StationaryPoissonGpa_MatchesSciPyArtifact\\haden_HADEN_2026-09-09_16_00_09.458.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-100004-659-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessExternalPackageOracleTests_StationaryPoissonGpa_MatchesSciPyArtifact.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"d80bfa8621c48a78cf4ebad7f01326841fac37aa","sourceSha256":"95A9BB4F8927EF0934A13A39B566C1FA30BDB42B2F95AC965746DD122DB39BA0"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"17C77A3DDAE1EF7B39BA3945DB5F6FB61F93F3D1D57FD48AF96248C9A6054F43"},"numericsAssemblySha256":"3E82F983E4795FD50104CBF0F950A7513BDF94540C3397401A8EC5BBF1A6C60F","bestFitAssemblySha256":"67A7E4809110D816C06ED83E4F9068F6E6FA78E490A5486FCEF62A56D7019DC0","verificationAssemblySha256":"542282B3090D914193685DF647EB18960BC31D061A17CACA7E63A2AA92CC9E46","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Compare unchanged oracle test after current parameter-count initialization failure."} +{"runId":"20260909-100036-025-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessExternalPackageOracleTests_StationaryPoissonGpa_MatchesSciPyArtifact","method":"RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessExternalPackageOracleTests.StationaryPoissonGpa_MatchesSciPyArtifact","phase":"point-process-scipy-old-integration-new-numerics","startedUtc":"2026-09-09T16:00:36.0270730+00:00","wallSeconds":5.1627435,"testSeconds":0.0477283,"buildSeconds":3.18,"result":"Failed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260909-100036-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessExternalPackageOracleTests_StationaryPoissonGpa_MatchesSciPyArtifact\\haden_HADEN_2026-09-09_16_00_40.938.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-100036-025-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessExternalPackageOracleTests_StationaryPoissonGpa_MatchesSciPyArtifact.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"17C77A3DDAE1EF7B39BA3945DB5F6FB61F93F3D1D57FD48AF96248C9A6054F43"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"B1F1D8998D61E4C2904B91943A84CEEA2BE653CEBA43D5F335C29ECD8465C91D","verificationAssemblySha256":"50ADDBDFCE9466BC0402A38A6A890F39418B4E5AF8126FF3BA74DA0F437564DA","dependencies":[],"sourceChangedDuringRun":false,"failure":"Test method RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessExternalPackageOracleTests.StationaryPoissonGpa_MatchesSciPyArtifact threw exception: \nSystem.ArgumentOutOfRangeException: Insufficient observations to initialize the distribution. (Parameter 'sample')","note":"Factor isolation for point-process default parameter count regression."} +{"runId":"20260909-100532-189-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessExternalPackageOracleTests_StationaryPoissonGpa_MatchesSciPyArtifact","method":"RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessExternalPackageOracleTests.StationaryPoissonGpa_MatchesSciPyArtifact","phase":"point-process-explicit-parameter-repair","startedUtc":"2026-09-09T16:05:32.1912193+00:00","wallSeconds":3.1544693,"testSeconds":0.0376296,"buildSeconds":1.21,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-100532-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessExternalPackageOracleTests_StationaryPoissonGpa_MatchesSciPyArtifact\\haden_HADEN_2026-09-09_16_05_35.016.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-100532-189-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessExternalPackageOracleTests_StationaryPoissonGpa_MatchesSciPyArtifact.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Restore component parameter slots for explicit assignment after automatic initialization fails; keep automatic-prior validation error and all scientific settings."} +{"runId":"20260909-100541-825-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessLikelihoodOracleTests_NonseasonalMixedObservations_MatchIndependentLikelihood","method":"RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessLikelihoodOracleTests.NonseasonalMixedObservations_MatchIndependentLikelihood","phase":"stabilized-pass","startedUtc":"2026-09-09T16:05:41.8273343+00:00","wallSeconds":2.9144964,"testSeconds":0.0440543,"buildSeconds":1.37,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-100541-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessLikelihoodOracleTests_NonseasonalMixedObservations_MatchIndependentLikelihood\\haden_HADEN_2026-09-09_16_05_44.519.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-100541-825-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessLikelihoodOracleTests_NonseasonalMixedObservations_MatchIndependentLikelihood.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-100545-491-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessLikelihoodOracleTests_SeasonalMixedObservations_MatchIndependentAnnualMaximumLikelihood","method":"RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessLikelihoodOracleTests.SeasonalMixedObservations_MatchIndependentAnnualMaximumLikelihood","phase":"stabilized-pass","startedUtc":"2026-09-09T16:05:45.4920167+00:00","wallSeconds":2.9817258,"testSeconds":0.0468286,"buildSeconds":1.47,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-100545-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessLikelihoodOracleTests_SeasonalMixedObservations_MatchIndependentAnnualMaximumLikelihood\\haden_HADEN_2026-09-09_16_05_48.250.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-100545-491-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessLikelihoodOracleTests_SeasonalMixedObservations_MatchIndependentAnnualMaximumLikelihood.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-100549-025-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessPriorTests_Test_CalendarYearPertHistogram_MatchesIndependentPriorPlacementOracle","method":"RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessPriorTests.Test_CalendarYearPertHistogram_MatchesIndependentPriorPlacementOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T16:05:49.0257518+00:00","wallSeconds":3.1059206,"testSeconds":0.360723,"buildSeconds":1.26,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-100549-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessPriorTests_Test_CalendarYearPertHistogram_MatchesIndependentPriorPlacementOracle\\haden_HADEN_2026-09-09_16_05_51.902.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-100549-025-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessPriorTests_Test_CalendarYearPertHistogram_MatchesIndependentPriorPlacementOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-100552-735-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessPriorTests_Test_WaterYearPertHistogram_MatchesIndependentPriorPlacementOracle","method":"RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessPriorTests.Test_WaterYearPertHistogram_MatchesIndependentPriorPlacementOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T16:05:52.7352438+00:00","wallSeconds":3.520955,"testSeconds":0.2992321,"buildSeconds":1.54,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-100552-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessPriorTests_Test_WaterYearPertHistogram_MatchesIndependentPriorPlacementOracle\\haden_HADEN_2026-09-09_16_05_55.973.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-100552-735-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessPriorTests_Test_WaterYearPertHistogram_MatchesIndependentPriorPlacementOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-100556-858-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessRecoveryTests_Test_CalendarYearUniformSeasonality_AutomaticPriorsRecoverParentAndBothChangePoints","method":"RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessRecoveryTests.Test_CalendarYearUniformSeasonality_AutomaticPriorsRecoverParentAndBothChangePoints","phase":"stabilized-pass","startedUtc":"2026-09-09T16:05:56.8587669+00:00","wallSeconds":34.5103692,"testSeconds":31.3023119,"buildSeconds":1.58,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-100556-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessRecoveryTests_Test_CalendarYearUniformSeasonality_AutomaticPriorsRecoverParentAndBothChangePoints\\haden_HADEN_2026-09-09_16_06_31.110.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-100556-858-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessRecoveryTests_Test_CalendarYearUniformSeasonality_AutomaticPriorsRecoverParentAndBothChangePoints.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-100631-907-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessRecoveryTests_Test_NonSeasonalProductionGenerator_RecoversParent","method":"RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessRecoveryTests.Test_NonSeasonalProductionGenerator_RecoversParent","phase":"stabilized-pass","startedUtc":"2026-09-09T16:06:31.9079595+00:00","wallSeconds":10.6734439,"testSeconds":7.7005369,"buildSeconds":1.42,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-100631-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessRecoveryTests_Test_NonSeasonalProductionGenerator_RecoversParent\\haden_HADEN_2026-09-09_16_06_42.341.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-100631-907-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessRecoveryTests_Test_NonSeasonalProductionGenerator_RecoversParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["GeneralizedPareto","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-100643-220-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessRecoveryTests_Test_NonSeasonalSimulation_MatchesPoissonRateAndConditionalTail","method":"RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessRecoveryTests.Test_NonSeasonalSimulation_MatchesPoissonRateAndConditionalTail","phase":"stabilized-pass","startedUtc":"2026-09-09T16:06:43.2206483+00:00","wallSeconds":3.3877056,"testSeconds":0.0597171,"buildSeconds":1.63,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-100643-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessRecoveryTests_Test_NonSeasonalSimulation_MatchesPoissonRateAndConditionalTail\\haden_HADEN_2026-09-09_16_06_46.379.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-100643-220-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessRecoveryTests_Test_NonSeasonalSimulation_MatchesPoissonRateAndConditionalTail.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["GeneralizedPareto","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-100647-207-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessRecoveryTests_Test_SeasonalProductionGenerator_RecoversParentAndBothChangePoints","method":"RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessRecoveryTests.Test_SeasonalProductionGenerator_RecoversParentAndBothChangePoints","phase":"stabilized-pass","startedUtc":"2026-09-09T16:06:47.2074378+00:00","wallSeconds":37.4136403,"testSeconds":34.2137637,"buildSeconds":1.55,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-100647-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessRecoveryTests_Test_SeasonalProductionGenerator_RecoversParentAndBothChangePoints\\haden_HADEN_2026-09-09_16_07_24.385.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-100647-207-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessRecoveryTests_Test_SeasonalProductionGenerator_RecoversParentAndBothChangePoints.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["GeneralizedPareto","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-100725-189-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessRecoveryTests_Test_SeasonalProductionGenerator_WithUnequalIntensities_RecoversParentAndBothChangePoints","method":"RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessRecoveryTests.Test_SeasonalProductionGenerator_WithUnequalIntensities_RecoversParentAndBothChangePoints","phase":"stabilized-pass","startedUtc":"2026-09-09T16:07:25.1892809+00:00","wallSeconds":36.095641,"testSeconds":33.0715159,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-100725-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessRecoveryTests_Test_SeasonalProductionGenerator_WithUnequalIntensities_RecoversParentAndBothChangePoints\\haden_HADEN_2026-09-09_16_08_01.031.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-100725-189-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessRecoveryTests_Test_SeasonalProductionGenerator_WithUnequalIntensities_RecoversParentAndBothChangePoints.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["GeneralizedPareto","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-100801-846-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessRecoveryTests_Test_SeasonalSimulation_MatchesSeasonRatesAssignmentsAndConditionalTails","method":"RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessRecoveryTests.Test_SeasonalSimulation_MatchesSeasonRatesAssignmentsAndConditionalTails","phase":"stabilized-pass","startedUtc":"2026-09-09T16:08:01.8469566+00:00","wallSeconds":3.0619709,"testSeconds":0.0947115,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-100801-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessRecoveryTests_Test_SeasonalSimulation_MatchesSeasonRatesAssignmentsAndConditionalTails\\haden_HADEN_2026-09-09_16_08_04.682.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-100801-846-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessRecoveryTests_Test_SeasonalSimulation_MatchesSeasonRatesAssignmentsAndConditionalTails.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["GeneralizedPareto","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-100805-461-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessRecoveryTests_Test_SeasonalSimulation_WithUnequalIntensities_MatchesSeasonRatesAssignmentsAndConditionalTails","method":"RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessRecoveryTests.Test_SeasonalSimulation_WithUnequalIntensities_MatchesSeasonRatesAssignmentsAndConditionalTails","phase":"stabilized-pass","startedUtc":"2026-09-09T16:08:05.4619510+00:00","wallSeconds":3.0592544,"testSeconds":0.0945351,"buildSeconds":1.45,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-100805-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessRecoveryTests_Test_SeasonalSimulation_WithUnequalIntensities_MatchesSeasonRatesAssignmentsAndConditionalTails\\haden_HADEN_2026-09-09_16_08_08.295.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-100805-461-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessRecoveryTests_Test_SeasonalSimulation_WithUnequalIntensities_MatchesSeasonRatesAssignmentsAndConditionalTails.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["GeneralizedPareto","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-100809-048-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessRecoveryTests_Test_WaterYearUniformSeasonality_AutomaticPriorsRecoverParentAndBothChangePoints","method":"RMC.BestFit.Verification.Univariate.PointProcessTests.PointProcessRecoveryTests.Test_WaterYearUniformSeasonality_AutomaticPriorsRecoverParentAndBothChangePoints","phase":"stabilized-pass","startedUtc":"2026-09-09T16:08:09.0484987+00:00","wallSeconds":33.0982807,"testSeconds":30.0931777,"buildSeconds":1.45,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-100809-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessRecoveryTests_Test_WaterYearUniformSeasonality_AutomaticPriorsRecoverParentAndBothChangePoints\\haden_HADEN_2026-09-09_16_08_41.900.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-100809-048-RMC_BestFit_Verification_Univariate_PointProcessTests_PointProcessRecoveryTests_Test_WaterYearUniformSeasonality_AutomaticPriorsRecoverParentAndBothChangePoints.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-100842-682-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryValidationTests_Nonstationary_ConstantTrend_RecoversTrueParameters","method":"RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryValidationTests.Nonstationary_ConstantTrend_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T16:08:42.6823261+00:00","wallSeconds":23.4436971,"testSeconds":20.4628867,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-100842-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryValidationTests_Nonstationary_ConstantTrend_RecoversTrueParameters\\haden_HADEN_2026-09-09_16_09_05.883.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-100842-682-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryValidationTests_Nonstationary_ConstantTrend_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","Logistic","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-100906-674-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryValidationTests_Nonstationary_ReciprocalMeanTrend_RecoversParentResponses","method":"RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryValidationTests.Nonstationary_ReciprocalMeanTrend_RecoversParentResponses","phase":"stabilized-pass","startedUtc":"2026-09-09T16:09:06.6749615+00:00","wallSeconds":34.4657892,"testSeconds":31.3675767,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-100906-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryValidationTests_Nonstationary_ReciprocalMeanTrend_RecoversParentResponses\\haden_HADEN_2026-09-09_16_09_40.905.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-100906-674-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryValidationTests_Nonstationary_ReciprocalMeanTrend_RecoversParentResponses.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","Logistic","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-100941-706-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_Exponential_RecoversTrueParameters","method":"RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.Exponential_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T16:09:41.7070225+00:00","wallSeconds":9.6280378,"testSeconds":6.6268654,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-100941-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_Exponential_RecoversTrueParameters\\haden_HADEN_2026-09-09_16_09_51.091.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-100941-706-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_Exponential_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-100951-946-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_Gamma_RecoversTrueParameters","method":"RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.Gamma_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T16:09:51.9461605+00:00","wallSeconds":23.7715179,"testSeconds":20.7202105,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-100952-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_Gamma_RecoversTrueParameters\\haden_HADEN_2026-09-09_16_10_15.477.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-100951-946-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_Gamma_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101016-254-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_GeneralizedExtremeValue_RecoversTrueParameters","method":"RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.GeneralizedExtremeValue_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T16:10:16.2546207+00:00","wallSeconds":32.1475403,"testSeconds":29.2630353,"buildSeconds":1.39,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101016-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_GeneralizedExtremeValue_RecoversTrueParameters\\haden_HADEN_2026-09-09_16_10_48.158.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101016-254-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_GeneralizedExtremeValue_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101048-915-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_GeneralizedLogistic_RecoversTrueParameters","method":"RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.GeneralizedLogistic_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T16:10:48.9155936+00:00","wallSeconds":44.1385765,"testSeconds":41.1704816,"buildSeconds":1.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101048-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_GeneralizedLogistic_RecoversTrueParameters\\haden_HADEN_2026-09-09_16_11_32.796.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101048-915-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_GeneralizedLogistic_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101133-649-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_GeneralizedNormal_RecoversTrueParameters","method":"RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.GeneralizedNormal_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T16:11:33.6493878+00:00","wallSeconds":32.7264231,"testSeconds":29.6382959,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101133-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_GeneralizedNormal_RecoversTrueParameters\\haden_HADEN_2026-09-09_16_12_06.135.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101133-649-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_GeneralizedNormal_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101206-876-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_GeneralizedPareto_RecoversTrueParameters","method":"RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.GeneralizedPareto_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T16:12:06.8764779+00:00","wallSeconds":23.9934967,"testSeconds":21.2308849,"buildSeconds":1.26,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101206-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_GeneralizedPareto_RecoversTrueParameters\\haden_HADEN_2026-09-09_16_12_30.604.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101206-876-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_GeneralizedPareto_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101231-466-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_Gumbel_RecoversTrueParameters","method":"RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.Gumbel_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T16:12:31.4664235+00:00","wallSeconds":12.2139403,"testSeconds":9.1475661,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101231-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_Gumbel_RecoversTrueParameters\\haden_HADEN_2026-09-09_16_12_43.437.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101231-466-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_Gumbel_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101244-205-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_KappaFour_RecoversTrueParameters","method":"RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.KappaFour_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T16:12:44.2057929+00:00","wallSeconds":63.1463471,"testSeconds":60.4087617,"buildSeconds":1.24,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101244-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_KappaFour_RecoversTrueParameters\\haden_HADEN_2026-09-09_16_13_47.107.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101244-205-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_KappaFour_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101347-956-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_LnNormal_RecoversTrueParameters","method":"RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.LnNormal_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T16:13:47.9565595+00:00","wallSeconds":12.9746789,"testSeconds":9.8676469,"buildSeconds":1.52,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101348-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_LnNormal_RecoversTrueParameters\\haden_HADEN_2026-09-09_16_14_00.693.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101347-956-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_LnNormal_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101401-485-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_Logistic_RecoversTrueParameters","method":"RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.Logistic_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T16:14:01.4855885+00:00","wallSeconds":15.2479383,"testSeconds":12.1390317,"buildSeconds":1.51,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101401-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_Logistic_RecoversTrueParameters\\haden_HADEN_2026-09-09_16_14_16.489.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101401-485-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_Logistic_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101417-281-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_LogNormal_RecoversTrueParameters","method":"RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.LogNormal_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T16:14:17.2814614+00:00","wallSeconds":15.955695,"testSeconds":12.7705993,"buildSeconds":1.52,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101417-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_LogNormal_RecoversTrueParameters\\haden_HADEN_2026-09-09_16_14_32.952.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101417-281-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_LogNormal_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101433-741-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_Normal_RecoversTrueParameters","method":"RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.Normal_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T16:14:33.7419882+00:00","wallSeconds":8.9646808,"testSeconds":6.181619,"buildSeconds":1.27,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101433-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_Normal_RecoversTrueParameters\\haden_HADEN_2026-09-09_16_14_42.447.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101433-741-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_Normal_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101443-269-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_Weibull_RecoversTrueParameters","method":"RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.Weibull_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T16:14:43.2695963+00:00","wallSeconds":16.3936958,"testSeconds":13.3309117,"buildSeconds":1.52,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101443-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_Weibull_RecoversTrueParameters\\haden_HADEN_2026-09-09_16_14_59.424.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101443-269-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_Weibull_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101500-225-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test1_ExactData_1951_2001","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ViglioneEtAlTests.Test1_ExactData_1951_2001","phase":"stabilized-pass","startedUtc":"2026-09-09T16:15:00.2253002+00:00","wallSeconds":5.3501652,"testSeconds":2.154506,"buildSeconds":1.57,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101500-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test1_ExactData_1951_2001\\haden_HADEN_2026-09-09_16_15_05.341.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101500-225-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test1_ExactData_1951_2001.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["GeneralizedExtremeValue"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101506-073-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test2_ExactData_1951_2005","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ViglioneEtAlTests.Test2_ExactData_1951_2005","phase":"stabilized-pass","startedUtc":"2026-09-09T16:15:06.0731844+00:00","wallSeconds":5.1152137,"testSeconds":2.210853,"buildSeconds":1.42,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101506-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test2_ExactData_1951_2005\\haden_HADEN_2026-09-09_16_15_10.956.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101506-073-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test2_ExactData_1951_2005.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["GeneralizedExtremeValue"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101511-751-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test3_ExactData_1951_2001_TemporalExpansion","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ViglioneEtAlTests.Test3_ExactData_1951_2001_TemporalExpansion","phase":"stabilized-pass","startedUtc":"2026-09-09T16:15:11.7518935+00:00","wallSeconds":5.7802629,"testSeconds":2.4596916,"buildSeconds":1.54,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101511-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test3_ExactData_1951_2001_TemporalExpansion\\haden_HADEN_2026-09-09_16_15_17.102.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101511-751-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test3_ExactData_1951_2001_TemporalExpansion.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["GeneralizedExtremeValue"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101518-130-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test4_ExactData_1951_2005_TemporalExpansion","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ViglioneEtAlTests.Test4_ExactData_1951_2005_TemporalExpansion","phase":"stabilized-pass","startedUtc":"2026-09-09T16:15:18.1305026+00:00","wallSeconds":5.4406455,"testSeconds":2.4680651,"buildSeconds":1.45,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101518-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test4_ExactData_1951_2005_TemporalExpansion\\haden_HADEN_2026-09-09_16_15_23.345.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101518-130-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test4_ExactData_1951_2005_TemporalExpansion.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["GeneralizedExtremeValue"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101524-090-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test5_ExactData_1951_2001_CausalExpansion","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ViglioneEtAlTests.Test5_ExactData_1951_2001_CausalExpansion","phase":"stabilized-pass","startedUtc":"2026-09-09T16:15:24.0909626+00:00","wallSeconds":5.2251779,"testSeconds":2.3090214,"buildSeconds":1.41,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101524-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test5_ExactData_1951_2001_CausalExpansion\\haden_HADEN_2026-09-09_16_15_29.078.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101524-090-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test5_ExactData_1951_2001_CausalExpansion.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["GeneralizedExtremeValue"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101529-873-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test6_ExactData_1951_2005_CausalExpansion","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ViglioneEtAlTests.Test6_ExactData_1951_2005_CausalExpansion","phase":"stabilized-pass","startedUtc":"2026-09-09T16:15:29.8731109+00:00","wallSeconds":5.3542994,"testSeconds":2.2506403,"buildSeconds":1.52,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101529-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test6_ExactData_1951_2005_CausalExpansion\\haden_HADEN_2026-09-09_16_15_34.994.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101529-873-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test6_ExactData_1951_2005_CausalExpansion.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["GeneralizedExtremeValue"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101535-793-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test7_ExactData_1951_2001_TemporalAndCausalExpansion","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ViglioneEtAlTests.Test7_ExactData_1951_2001_TemporalAndCausalExpansion","phase":"stabilized-pass","startedUtc":"2026-09-09T16:15:35.7934484+00:00","wallSeconds":5.7120272,"testSeconds":2.5686604,"buildSeconds":1.54,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101535-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test7_ExactData_1951_2001_TemporalAndCausalExpansion\\haden_HADEN_2026-09-09_16_15_41.256.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101535-793-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test7_ExactData_1951_2001_TemporalAndCausalExpansion.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["GeneralizedExtremeValue"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101542-073-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test8_ExactData_1951_2005_TemporalAndCausalExpansion","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ViglioneEtAlTests.Test8_ExactData_1951_2005_TemporalAndCausalExpansion","phase":"stabilized-pass","startedUtc":"2026-09-09T16:15:42.0735581+00:00","wallSeconds":5.8015441,"testSeconds":2.8530544,"buildSeconds":1.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101542-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test8_ExactData_1951_2005_TemporalAndCausalExpansion\\haden_HADEN_2026-09-09_16_15_47.644.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101542-073-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test8_ExactData_1951_2005_TemporalAndCausalExpansion.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["GeneralizedExtremeValue"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101548-459-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test9_ExactData_1951_2001_CausalExpansion_TheePriors","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ViglioneEtAlTests.Test9_ExactData_1951_2001_CausalExpansion_TheePriors","phase":"stabilized-pass","startedUtc":"2026-09-09T16:15:48.4599556+00:00","wallSeconds":5.7768718,"testSeconds":2.6249047,"buildSeconds":1.57,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101548-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test9_ExactData_1951_2001_CausalExpansion_TheePriors\\haden_HADEN_2026-09-09_16_15_54.003.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101548-459-RMC_BestFit_Verification_Univariate_VerificationReportTests_ViglioneEtAlTests_Test9_ExactData_1951_2001_CausalExpansion_TheePriors.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["GeneralizedExtremeValue"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101554-745-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T16:15:54.7455909+00:00","wallSeconds":4.734794,"testSeconds":1.7591727,"buildSeconds":1.45,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101554-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_16_15_59.257.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101554-745-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LnNormal_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101559-988-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T16:15:59.9885937+00:00","wallSeconds":4.4850713,"testSeconds":1.5055007,"buildSeconds":1.42,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101600-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_16_16_04.240.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101559-988-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogPearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101605-106-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T16:16:05.1069948+00:00","wallSeconds":4.6950914,"testSeconds":1.5211644,"buildSeconds":1.55,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101605-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_16_16_09.573.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101605-106-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_PearsonTypeIII_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101610-386-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogPearsonTypeIII_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.LogPearsonTypeIII_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T16:16:10.3870089+00:00","wallSeconds":3.2627494,"testSeconds":0.0973671,"buildSeconds":1.55,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101610-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogPearsonTypeIII_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_16_16_13.417.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101610-386-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogPearsonTypeIII_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101614-278-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_PearsonTypeIII_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.PearsonTypeIII_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T16:16:14.2781378+00:00","wallSeconds":3.3398012,"testSeconds":0.0939181,"buildSeconds":1.59,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101614-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_PearsonTypeIII_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_16_16_17.386.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101614-278-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_PearsonTypeIII_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101618-234-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LogPearsonTypeIII_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_LogPearsonTypeIII_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T16:16:18.2345663+00:00","wallSeconds":3.3620058,"testSeconds":0.1154388,"buildSeconds":1.59,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101618-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LogPearsonTypeIII_MLE\\haden_HADEN_2026-09-09_16_16_21.365.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101618-234-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LogPearsonTypeIII_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101622-224-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_PearsonTypeIII_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_PearsonTypeIII_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T16:16:22.2247024+00:00","wallSeconds":3.3597325,"testSeconds":0.0994474,"buildSeconds":1.59,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101622-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_PearsonTypeIII_MLE\\haden_HADEN_2026-09-09_16_16_25.355.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101622-224-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_PearsonTypeIII_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101626-213-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LogPearsonTypeIII_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_LogPearsonTypeIII_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T16:16:26.2132825+00:00","wallSeconds":3.8729104,"testSeconds":0.7010644,"buildSeconds":1.54,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101626-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LogPearsonTypeIII_RecoversTrueParameters\\haden_HADEN_2026-09-09_16_16_29.852.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101626-213-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LogPearsonTypeIII_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101630-677-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_PearsonTypeIII_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_PearsonTypeIII_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T16:16:30.6776211+00:00","wallSeconds":3.7435752,"testSeconds":0.5618187,"buildSeconds":1.58,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101630-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_PearsonTypeIII_RecoversTrueParameters\\haden_HADEN_2026-09-09_16_16_34.188.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101630-677-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_PearsonTypeIII_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101634-983-RMC_BestFit_Verification_ModelEstimation_ProfileQRecoveryTests_TrueProfile_LP3Q99_RecoversGeneratingResponse","method":"RMC.BestFit.Verification.ModelEstimation.ProfileQRecoveryTests.TrueProfile_LP3Q99_RecoversGeneratingResponse","phase":"stabilized-pass","startedUtc":"2026-09-09T16:16:34.9839653+00:00","wallSeconds":4.5006544,"testSeconds":1.4069798,"buildSeconds":1.51,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101635-RMC_BestFit_Verification_ModelEstimation_ProfileQRecoveryTests_TrueProfile_LP3Q99_RecoversGeneratingResponse\\haden_HADEN_2026-09-09_16_16_39.253.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101634-983-RMC_BestFit_Verification_ModelEstimation_ProfileQRecoveryTests_TrueProfile_LP3Q99_RecoversGeneratingResponse.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101640-082-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Exponential_Covariance_N100","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.Exponential_Covariance_N100","phase":"stabilized-pass","startedUtc":"2026-09-09T16:16:40.0827673+00:00","wallSeconds":3.2249744,"testSeconds":0.0758875,"buildSeconds":1.52,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101640-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Exponential_Covariance_N100\\haden_HADEN_2026-09-09_16_16_43.079.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101640-082-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Exponential_Covariance_N100.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101643-918-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Exponential_Covariance_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.Exponential_Covariance_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T16:16:43.9184757+00:00","wallSeconds":3.2641451,"testSeconds":0.070508,"buildSeconds":1.55,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101643-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Exponential_Covariance_N25\\haden_HADEN_2026-09-09_16_16_46.957.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101643-918-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Exponential_Covariance_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101647-794-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Gamma_Covariance_N100","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.Gamma_Covariance_N100","phase":"stabilized-pass","startedUtc":"2026-09-09T16:16:47.7944412+00:00","wallSeconds":3.3259994,"testSeconds":0.071333,"buildSeconds":1.57,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101647-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Gamma_Covariance_N100\\haden_HADEN_2026-09-09_16_16_50.883.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101647-794-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Gamma_Covariance_N100.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101651-729-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Gamma_Covariance_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.Gamma_Covariance_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T16:16:51.7300243+00:00","wallSeconds":3.3548837,"testSeconds":0.0722649,"buildSeconds":1.58,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101651-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Gamma_Covariance_N25\\haden_HADEN_2026-09-09_16_16_54.835.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101651-729-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Gamma_Covariance_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101655-697-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogNormal_Covariance_N100","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.LogNormal_Covariance_N100","phase":"stabilized-pass","startedUtc":"2026-09-09T16:16:55.6974939+00:00","wallSeconds":3.2908027,"testSeconds":0.0694706,"buildSeconds":1.56,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101655-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogNormal_Covariance_N100\\haden_HADEN_2026-09-09_16_16_58.755.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101655-697-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogNormal_Covariance_N100.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101659-597-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogNormal_Covariance_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.LogNormal_Covariance_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T16:16:59.5975402+00:00","wallSeconds":3.3580987,"testSeconds":0.0698355,"buildSeconds":1.63,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101659-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogNormal_Covariance_N25\\haden_HADEN_2026-09-09_16_17_02.726.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101659-597-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogNormal_Covariance_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101703-594-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogPearsonTypeIII_Covariance_Example1","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.LogPearsonTypeIII_Covariance_Example1","phase":"stabilized-pass","startedUtc":"2026-09-09T16:17:03.5949453+00:00","wallSeconds":3.5129545,"testSeconds":0.1895139,"buildSeconds":1.6,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101703-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogPearsonTypeIII_Covariance_Example1\\haden_HADEN_2026-09-09_16_17_06.886.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101703-594-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogPearsonTypeIII_Covariance_Example1.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101707-719-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogPearsonTypeIII_Covariance_N100","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.LogPearsonTypeIII_Covariance_N100","phase":"stabilized-pass","startedUtc":"2026-09-09T16:17:07.7199039+00:00","wallSeconds":3.2871125,"testSeconds":0.072623,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101707-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogPearsonTypeIII_Covariance_N100\\haden_HADEN_2026-09-09_16_17_10.777.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101707-719-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogPearsonTypeIII_Covariance_N100.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101711-644-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogPearsonTypeIII_Covariance_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.LogPearsonTypeIII_Covariance_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T16:17:11.6441946+00:00","wallSeconds":3.4198104,"testSeconds":0.0765813,"buildSeconds":1.61,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101711-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogPearsonTypeIII_Covariance_N25\\haden_HADEN_2026-09-09_16_17_14.811.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101711-644-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_LogPearsonTypeIII_Covariance_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101715-720-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Normal_Covariance_N100","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.Normal_Covariance_N100","phase":"stabilized-pass","startedUtc":"2026-09-09T16:17:15.7205896+00:00","wallSeconds":3.4600025,"testSeconds":0.073513,"buildSeconds":1.62,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101715-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Normal_Covariance_N100\\haden_HADEN_2026-09-09_16_17_18.939.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101715-720-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Normal_Covariance_N100.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101719-811-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Normal_Covariance_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.Normal_Covariance_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T16:17:19.8119666+00:00","wallSeconds":3.3331349,"testSeconds":0.0686671,"buildSeconds":1.57,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101719-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Normal_Covariance_N25\\haden_HADEN_2026-09-09_16_17_22.917.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101719-811-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_Normal_Covariance_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101723-768-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_PearsonTypeIII_Covariance_N100","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.PearsonTypeIII_Covariance_N100","phase":"stabilized-pass","startedUtc":"2026-09-09T16:17:23.7690295+00:00","wallSeconds":3.3530076,"testSeconds":0.0829298,"buildSeconds":1.59,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101723-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_PearsonTypeIII_Covariance_N100\\haden_HADEN_2026-09-09_16_17_26.892.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101723-768-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_PearsonTypeIII_Covariance_N100.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101727-745-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_PearsonTypeIII_Covariance_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CCovarianceTests.PearsonTypeIII_Covariance_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T16:17:27.7458718+00:00","wallSeconds":3.4902367,"testSeconds":0.0805595,"buildSeconds":1.6,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101727-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_PearsonTypeIII_Covariance_N25\\haden_HADEN_2026-09-09_16_17_30.883.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101727-745-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CCovarianceTests_PearsonTypeIII_Covariance_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101731-810-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogPearsonTypeIII_PenalityOnAllParams_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogPearsonTypeIII_PenalityOnAllParams_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T16:17:31.8104124+00:00","wallSeconds":3.0868543,"testSeconds":0.0958459,"buildSeconds":1.33,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101731-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogPearsonTypeIII_PenalityOnAllParams_N25\\haden_HADEN_2026-09-09_16_17_34.665.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101731-810-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogPearsonTypeIII_PenalityOnAllParams_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101735-490-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogPearsonTypeIII_PenalityOnAllParamsAndQ99_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogPearsonTypeIII_PenalityOnAllParamsAndQ99_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T16:17:35.4903839+00:00","wallSeconds":3.4301912,"testSeconds":0.1177414,"buildSeconds":1.6,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101735-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogPearsonTypeIII_PenalityOnAllParamsAndQ99_N25\\haden_HADEN_2026-09-09_16_17_38.675.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101735-490-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogPearsonTypeIII_PenalityOnAllParamsAndQ99_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101739-489-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogPearsonTypeIII_PenalityOnMuAndGamma_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogPearsonTypeIII_PenalityOnMuAndGamma_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T16:17:39.4895919+00:00","wallSeconds":3.4369645,"testSeconds":0.093093,"buildSeconds":1.63,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101739-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogPearsonTypeIII_PenalityOnMuAndGamma_N25\\haden_HADEN_2026-09-09_16_17_42.628.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101739-489-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogPearsonTypeIII_PenalityOnMuAndGamma_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101743-517-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_LogPearsonTypeIII_GmmRecoversGeneratingLog10ParentAndQ99","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CSyntheticDataTests.LogPearsonTypeIII_GmmRecoversGeneratingLog10ParentAndQ99","phase":"stabilized-pass","startedUtc":"2026-09-09T16:17:43.5179072+00:00","wallSeconds":3.3551635,"testSeconds":0.0734127,"buildSeconds":1.61,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101743-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_LogPearsonTypeIII_GmmRecoversGeneratingLog10ParentAndQ99\\haden_HADEN_2026-09-09_16_17_46.631.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101743-517-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_LogPearsonTypeIII_GmmRecoversGeneratingLog10ParentAndQ99.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101747-443-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_PearsonTypeIII_GmmRecoversGeneratingParentAndQ99","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CSyntheticDataTests.PearsonTypeIII_GmmRecoversGeneratingParentAndQ99","phase":"stabilized-pass","startedUtc":"2026-09-09T16:17:47.4439112+00:00","wallSeconds":3.3763084,"testSeconds":0.1033084,"buildSeconds":1.6,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101747-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_PearsonTypeIII_GmmRecoversGeneratingParentAndQ99\\haden_HADEN_2026-09-09_16_17_50.584.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101747-443-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_PearsonTypeIII_GmmRecoversGeneratingParentAndQ99.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-101751-388-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_Bayesian_Maximum_WeibullGumbelDogLeg_RecoversParent","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskRecoveryTests.Bayesian_Maximum_WeibullGumbelDogLeg_RecoversParent","phase":"stabilized-pass","startedUtc":"2026-09-09T16:17:51.3887635+00:00","wallSeconds":191.9769808,"testSeconds":188.5364646,"buildSeconds":1.58,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-101751-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_Bayesian_Maximum_WeibullGumbelDogLeg_RecoversParent\\haden_HADEN_2026-09-09_16_21_02.949.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-101751-388-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_Bayesian_Maximum_WeibullGumbelDogLeg_RecoversParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-102104-000-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_Bayesian_Minimum_TwoWeibullDogLeg_RecoversParent","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskRecoveryTests.Bayesian_Minimum_TwoWeibullDogLeg_RecoversParent","phase":"stabilized-pass","startedUtc":"2026-09-09T16:21:04.0008054+00:00","wallSeconds":169.302729,"testSeconds":165.8047628,"buildSeconds":1.58,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-102104-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_Bayesian_Minimum_TwoWeibullDogLeg_RecoversParent\\haden_HADEN_2026-09-09_16_23_52.862.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-102104-000-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_Bayesian_Minimum_TwoWeibullDogLeg_RecoversParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-102353-970-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Maximum_WeibullGumbelDogLeg_RecoversParent","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskRecoveryTests.MLE_Maximum_WeibullGumbelDogLeg_RecoversParent","phase":"stabilized-pass","startedUtc":"2026-09-09T16:23:53.9707029+00:00","wallSeconds":6.5505631,"testSeconds":3.0462913,"buildSeconds":1.73,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-102354-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Maximum_WeibullGumbelDogLeg_RecoversParent\\haden_HADEN_2026-09-09_16_24_00.259.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-102353-970-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Maximum_WeibullGumbelDogLeg_RecoversParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-102401-117-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.NormalMixture2D_BayesianRecovery","phase":"stabilized-pass","startedUtc":"2026-09-09T16:24:01.1172290+00:00","wallSeconds":142.7367294,"testSeconds":139.3480757,"buildSeconds":1.57,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-102401-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery\\haden_HADEN_2026-09-09_16_26_23.436.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-102401-117-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-102624-441-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedExtremeValue_LinearShape_RecoversParentResponses","method":"RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.GeneralizedExtremeValue_LinearShape_RecoversParentResponses","phase":"stabilized-pass","startedUtc":"2026-09-09T16:26:24.4414809+00:00","wallSeconds":73.4676554,"testSeconds":70.1463679,"buildSeconds":1.61,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-102624-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedExtremeValue_LinearShape_RecoversParentResponses\\haden_HADEN_2026-09-09_16_27_37.669.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-102624-441-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedExtremeValue_LinearShape_RecoversParentResponses.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GeneralizedExtremeValue","GeneralizedPareto","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-102804-871-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.NormalMixture2D_BayesianRecovery","phase":"mixture2-final-runtime-baseline","startedUtc":"2026-09-09T16:28:04.8726273+00:00","wallSeconds":141.335416,"testSeconds":131.6479174,"buildSeconds":7.59,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260909-102804-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery\\haden_HADEN_2026-09-09_16_30_25.758.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-102804-871-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"d80bfa8621c48a78cf4ebad7f01326841fac37aa","sourceSha256":"95A9BB4F8927EF0934A13A39B566C1FA30BDB42B2F95AC965746DD122DB39BA0"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"17C77A3DDAE1EF7B39BA3945DB5F6FB61F93F3D1D57FD48AF96248C9A6054F43"},"numericsAssemblySha256":"3E82F983E4795FD50104CBF0F950A7513BDF94540C3397401A8EC5BBF1A6C60F","bestFitAssemblySha256":"67A7E4809110D816C06ED83E4F9068F6E6FA78E490A5486FCEF62A56D7019DC0","verificationAssemblySha256":"542282B3090D914193685DF647EB18960BC31D061A17CACA7E63A2AA92CC9E46","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Adjacent paired repeat after final current139.348s versus earlier baseline105.939s."} +{"runId":"20260909-103027-080-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.NormalMixture2D_BayesianRecovery","phase":"mixture2-final-runtime-current","startedUtc":"2026-09-09T16:30:27.0806769+00:00","wallSeconds":140.2395211,"testSeconds":137.0565094,"buildSeconds":1.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-103027-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery\\haden_HADEN_2026-09-09_16_32_46.892.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-103027-080-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_BayesianRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Adjacent final-source repeat immediately after pinned baseline; no simultaneous CPU workloads or source changes."} +{"runId":"20260909-103322-530-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedPareto_LinearLocationExponentialScale_RecoversParentResponses","method":"RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.GeneralizedPareto_LinearLocationExponentialScale_RecoversParentResponses","phase":"stabilized-pass","startedUtc":"2026-09-09T16:33:22.5321958+00:00","wallSeconds":97.3845905,"testSeconds":94.1100626,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-103322-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedPareto_LinearLocationExponentialScale_RecoversParentResponses\\haden_HADEN_2026-09-09_16_34_59.475.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-103322-530-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_GeneralizedPareto_LinearLocationExponentialScale_RecoversParentResponses.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GeneralizedExtremeValue","GeneralizedPareto","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-103500-928-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses","method":"RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses","phase":"stabilized-pass","startedUtc":"2026-09-09T16:35:00.9283625+00:00","wallSeconds":146.4736077,"testSeconds":142.4768205,"buildSeconds":1.71,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-103501-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses\\haden_HADEN_2026-09-09_16_37_26.534.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-103500-928-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_LogPearsonTypeIII_LinearLogMeanExponentialLogScale_RecoversParentResponses.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GeneralizedExtremeValue","GeneralizedPareto","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-103728-009-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_Normal_ReciprocalSigma_RecoversParentResponses","method":"RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.Normal_ReciprocalSigma_RecoversParentResponses","phase":"stabilized-pass","startedUtc":"2026-09-09T16:37:28.0097911+00:00","wallSeconds":36.3160186,"testSeconds":33.1849502,"buildSeconds":1.52,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-103728-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_Normal_ReciprocalSigma_RecoversParentResponses\\haden_HADEN_2026-09-09_16_38_04.073.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-103728-009-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_Normal_ReciprocalSigma_RecoversParentResponses.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GeneralizedExtremeValue","GeneralizedPareto","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-103804-864-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_Normal_SinusoidalSigma_RecoversParentResponses","method":"RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.Normal_SinusoidalSigma_RecoversParentResponses","phase":"stabilized-pass","startedUtc":"2026-09-09T16:38:04.8646845+00:00","wallSeconds":40.349128,"testSeconds":37.3738927,"buildSeconds":1.45,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-103804-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_Normal_SinusoidalSigma_RecoversParentResponses\\haden_HADEN_2026-09-09_16_38_44.958.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-103804-864-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_Normal_SinusoidalSigma_RecoversParentResponses.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GeneralizedExtremeValue","GeneralizedPareto","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-103845-804-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_LogPearsonTypeIII_RecoversTrueParameters","method":"RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.LogPearsonTypeIII_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T16:38:45.8049353+00:00","wallSeconds":68.5293943,"testSeconds":65.6633841,"buildSeconds":1.31,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-103845-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_LogPearsonTypeIII_RecoversTrueParameters\\haden_HADEN_2026-09-09_16_39_54.086.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-103845-804-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_LogPearsonTypeIII_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-103954-856-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_PearsonTypeIII_RecoversTrueParameters","method":"RMC.BestFit.Verification.Univariate.ValidationTests.UnivariateValidationTests.PearsonTypeIII_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T16:39:54.8566328+00:00","wallSeconds":48.1062879,"testSeconds":44.9729224,"buildSeconds":1.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-103954-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_PearsonTypeIII_RecoversTrueParameters\\haden_HADEN_2026-09-09_16_40_42.525.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-103954-856-RMC_BestFit_Verification_Univariate_ValidationTests_UnivariateValidationTests_PearsonTypeIII_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-104043-511-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample3","phase":"stabilized-pass","startedUtc":"2026-09-09T16:40:43.5111203+00:00","wallSeconds":5.8786445,"testSeconds":2.8909236,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-104043-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3\\haden_HADEN_2026-09-09_16_40_49.152.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-104043-511-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample3.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-104049-914-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample4","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample4","phase":"stabilized-pass","startedUtc":"2026-09-09T16:40:49.9147985+00:00","wallSeconds":6.3354265,"testSeconds":3.3638919,"buildSeconds":1.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-104049-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample4\\haden_HADEN_2026-09-09_16_40_55.991.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-104049-914-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample4.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-104056-828-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample5","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample5","phase":"stabilized-pass","startedUtc":"2026-09-09T16:40:56.8288650+00:00","wallSeconds":5.9687924,"testSeconds":3.1589898,"buildSeconds":1.28,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-104056-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample5\\haden_HADEN_2026-09-09_16_41_02.542.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-104056-828-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample5.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-104103-330-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6a","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample6a","phase":"stabilized-pass","startedUtc":"2026-09-09T16:41:03.3303345+00:00","wallSeconds":5.128164,"testSeconds":2.1879486,"buildSeconds":1.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-104103-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6a\\haden_HADEN_2026-09-09_16_41_08.224.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-104103-330-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6a.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-104108-993-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6b","method":"RMC.BestFit.Verification.Univariate.VerificationReportTests.ArrFlikeTests.TestExample6b","phase":"stabilized-pass","startedUtc":"2026-09-09T16:41:08.9938664+00:00","wallSeconds":5.0789136,"testSeconds":2.1065859,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-104109-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6b\\haden_HADEN_2026-09-09_16_41_13.841.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-104108-993-RMC_BestFit_Verification_Univariate_VerificationReportTests_ArrFlikeTests_TestExample6b.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["GeneralizedExtremeValue","LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-104114-579-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskRecoveryTests.MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent","phase":"stabilized-pass","startedUtc":"2026-09-09T16:41:14.5793496+00:00","wallSeconds":26.7049782,"testSeconds":23.715814,"buildSeconds":1.47,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-104114-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent\\haden_HADEN_2026-09-09_16_41_41.058.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-104114-579-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_CorrelatedTwoWeibullDogLeg_RecoversParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-104141-805-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_CorrelationMatrixSimulation_MatchesRankDependenceAndCompositeCdf","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskDependencyVerificationTests.Test_CorrelationMatrixSimulation_MatchesRankDependenceAndCompositeCdf","phase":"stabilized-pass","startedUtc":"2026-09-09T16:41:41.8056268+00:00","wallSeconds":2.897067,"testSeconds":0.1120154,"buildSeconds":1.26,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-104141-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_CorrelationMatrixSimulation_MatchesRankDependenceAndCompositeCdf\\haden_HADEN_2026-09-09_16_41_44.483.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-104141-805-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_CorrelationMatrixSimulation_MatchesRankDependenceAndCompositeCdf.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-104145-209-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_IndependentSimulation_MatchesRankDependenceAndCompositeCdf","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskDependencyVerificationTests.Test_IndependentSimulation_MatchesRankDependenceAndCompositeCdf","phase":"stabilized-pass","startedUtc":"2026-09-09T16:41:45.2095562+00:00","wallSeconds":2.9700802,"testSeconds":0.0544106,"buildSeconds":1.41,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-104145-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_IndependentSimulation_MatchesRankDependenceAndCompositeCdf\\haden_HADEN_2026-09-09_16_41_47.942.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-104145-209-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_IndependentSimulation_MatchesRankDependenceAndCompositeCdf.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-104148-682-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_PerfectlyNegativeSimulation_MatchesRankDependenceAndCompositeCdf","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskDependencyVerificationTests.Test_PerfectlyNegativeSimulation_MatchesRankDependenceAndCompositeCdf","phase":"stabilized-pass","startedUtc":"2026-09-09T16:41:48.6826024+00:00","wallSeconds":3.0507589,"testSeconds":0.1111047,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-104148-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_PerfectlyNegativeSimulation_MatchesRankDependenceAndCompositeCdf\\haden_HADEN_2026-09-09_16_41_51.504.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-104148-682-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_PerfectlyNegativeSimulation_MatchesRankDependenceAndCompositeCdf.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-104152-275-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_PerfectlyPositiveSimulation_MatchesRankDependenceAndCompositeCdf","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskDependencyVerificationTests.Test_PerfectlyPositiveSimulation_MatchesRankDependenceAndCompositeCdf","phase":"stabilized-pass","startedUtc":"2026-09-09T16:41:52.2756489+00:00","wallSeconds":3.1704737,"testSeconds":0.0531927,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-104152-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_PerfectlyPositiveSimulation_MatchesRankDependenceAndCompositeCdf\\haden_HADEN_2026-09-09_16_41_55.208.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-104152-275-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskDependencyVerificationTests_Test_PerfectlyPositiveSimulation_MatchesRankDependenceAndCompositeCdf.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-104156-028-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_TwoWeibullDogLeg_RecoversParent","method":"RMC.BestFit.Verification.Univariate.CompetingRiskTests.CompetingRiskRecoveryTests.MLE_Minimum_TwoWeibullDogLeg_RecoversParent","phase":"stabilized-pass","startedUtc":"2026-09-09T16:41:56.0284275+00:00","wallSeconds":5.5660086,"testSeconds":2.596611,"buildSeconds":1.42,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-104156-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_TwoWeibullDogLeg_RecoversParent\\haden_HADEN_2026-09-09_16_42_01.347.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-104156-028-RMC_BestFit_Verification_Univariate_CompetingRiskTests_CompetingRiskRecoveryTests_MLE_Minimum_TwoWeibullDogLeg_RecoversParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-104202-122-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_EqualWeightModelAverage_EndToEndPredictiveRecovery","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositePredictiveRecoveryTests.EqualWeightModelAverage_EndToEndPredictiveRecovery","phase":"stabilized-pass","startedUtc":"2026-09-09T16:42:02.1225844+00:00","wallSeconds":16.8634173,"testSeconds":13.903241,"buildSeconds":1.45,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-104202-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_EqualWeightModelAverage_EndToEndPredictiveRecovery\\haden_HADEN_2026-09-09_16_42_18.744.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-104202-122-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_EqualWeightModelAverage_EndToEndPredictiveRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-104219-557-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MaximumComposite_EndToEndPredictiveRecovery","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositePredictiveRecoveryTests.MaximumComposite_EndToEndPredictiveRecovery","phase":"stabilized-pass","startedUtc":"2026-09-09T16:42:19.5578202+00:00","wallSeconds":18.3027303,"testSeconds":15.1591829,"buildSeconds":1.56,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-104219-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MaximumComposite_EndToEndPredictiveRecovery\\haden_HADEN_2026-09-09_16_42_37.623.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-104219-557-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MaximumComposite_EndToEndPredictiveRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-104258-770-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_Normal_ReciprocalSigma_RecoversParentResponses","method":"RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.Normal_ReciprocalSigma_RecoversParentResponses","phase":"reciprocal-sigma-runtime-baseline","startedUtc":"2026-09-09T16:42:58.7723573+00:00","wallSeconds":36.4712914,"testSeconds":33.2960248,"buildSeconds":1.52,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260909-104258-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_Normal_ReciprocalSigma_RecoversParentResponses\\haden_HADEN_2026-09-09_16_43_34.987.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-104258-770-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_Normal_ReciprocalSigma_RecoversParentResponses.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"d80bfa8621c48a78cf4ebad7f01326841fac37aa","sourceSha256":"95A9BB4F8927EF0934A13A39B566C1FA30BDB42B2F95AC965746DD122DB39BA0"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"17C77A3DDAE1EF7B39BA3945DB5F6FB61F93F3D1D57FD48AF96248C9A6054F43"},"numericsAssemblySha256":"3E82F983E4795FD50104CBF0F950A7513BDF94540C3397401A8EC5BBF1A6C60F","bestFitAssemblySha256":"67A7E4809110D816C06ED83E4F9068F6E6FA78E490A5486FCEF62A56D7019DC0","verificationAssemblySha256":"542282B3090D914193685DF647EB18960BC31D061A17CACA7E63A2AA92CC9E46","dependencies":["Exponential","GeneralizedExtremeValue","GeneralizedPareto","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Compare current33.185s to pinned baseline after historical27.848s timing hint."} +{"runId":"20260909-104336-088-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_Normal_ReciprocalSigma_RecoversParentResponses","method":"RMC.BestFit.Verification.Univariate.ValidationTests.NonstationaryParentTrendCoverageTests.Normal_ReciprocalSigma_RecoversParentResponses","phase":"reciprocal-sigma-runtime-current","startedUtc":"2026-09-09T16:43:36.0887936+00:00","wallSeconds":33.0634586,"testSeconds":29.9456961,"buildSeconds":1.56,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-104336-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_Normal_ReciprocalSigma_RecoversParentResponses\\haden_HADEN_2026-09-09_16_44_08.905.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-104336-088-RMC_BestFit_Verification_Univariate_ValidationTests_NonstationaryParentTrendCoverageTests_Normal_ReciprocalSigma_RecoversParentResponses.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GeneralizedExtremeValue","GeneralizedPareto","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Adjacent repaired-source repeat after pinned baseline, identical test and settings."} +{"runId":"20260909-104435-571-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MinimumComposite_EndToEndPredictiveRecovery","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositePredictiveRecoveryTests.MinimumComposite_EndToEndPredictiveRecovery","phase":"stabilized-pass","startedUtc":"2026-09-09T16:44:35.5736004+00:00","wallSeconds":19.1867926,"testSeconds":15.9964164,"buildSeconds":1.52,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-104435-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MinimumComposite_EndToEndPredictiveRecovery\\haden_HADEN_2026-09-09_16_44_54.506.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-104435-571-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MinimumComposite_EndToEndPredictiveRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-104455-558-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MixtureComposite_EndToEndPredictiveRecovery","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositePredictiveRecoveryTests.MixtureComposite_EndToEndPredictiveRecovery","phase":"stabilized-pass","startedUtc":"2026-09-09T16:44:55.5590226+00:00","wallSeconds":17.4173033,"testSeconds":14.3034048,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-104455-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MixtureComposite_EndToEndPredictiveRecovery\\haden_HADEN_2026-09-09_16_45_12.733.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-104455-558-RMC_BestFit_Verification_Univariate_CompositeTests_CompositePredictiveRecoveryTests_MixtureComposite_EndToEndPredictiveRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-104513-564-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_Recovery_Parity","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.NormalMixture2D_Recovery_Parity","phase":"stabilized-pass","startedUtc":"2026-09-09T16:45:13.5640916+00:00","wallSeconds":3.3818789,"testSeconds":0.2657993,"buildSeconds":1.55,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-104513-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_Recovery_Parity\\haden_HADEN_2026-09-09_16_45_16.719.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-104513-564-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture2D_Recovery_Parity.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-104517-483-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture3D_BayesianRecovery","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.NormalMixture3D_BayesianRecovery","phase":"stabilized-pass","startedUtc":"2026-09-09T16:45:17.4833873+00:00","wallSeconds":293.217216,"testSeconds":290.0130814,"buildSeconds":1.45,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-104517-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture3D_BayesianRecovery\\haden_HADEN_2026-09-09_16_50_10.275.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-104517-483-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture3D_BayesianRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-105011-316-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture3D_Recovery_Parity","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.NormalMixture3D_Recovery_Parity","phase":"stabilized-pass","startedUtc":"2026-09-09T16:50:11.3166318+00:00","wallSeconds":4.6689024,"testSeconds":1.6043103,"buildSeconds":1.52,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-105011-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture3D_Recovery_Parity\\haden_HADEN_2026-09-09_16_50_15.757.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-105011-316-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_NormalMixture3D_Recovery_Parity.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-105016-520-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_ZeroInflatedNormalMixture2D_BayesianRecovery","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.ZeroInflatedNormalMixture2D_BayesianRecovery","phase":"stabilized-pass","startedUtc":"2026-09-09T16:50:16.5205405+00:00","wallSeconds":166.6558416,"testSeconds":163.5401275,"buildSeconds":1.42,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-105016-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_ZeroInflatedNormalMixture2D_BayesianRecovery\\haden_HADEN_2026-09-09_16_53_02.774.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-105016-520-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_ZeroInflatedNormalMixture2D_BayesianRecovery.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-105303-740-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_ZeroInflatedNormalMixture2D_Recovery_Parity","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureRecoveryTests.ZeroInflatedNormalMixture2D_Recovery_Parity","phase":"stabilized-pass","startedUtc":"2026-09-09T16:53:03.7410316+00:00","wallSeconds":3.3962806,"testSeconds":0.4061132,"buildSeconds":1.45,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-105303-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_ZeroInflatedNormalMixture2D_Recovery_Parity\\haden_HADEN_2026-09-09_16_53_06.895.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-105303-740-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureRecoveryTests_ZeroInflatedNormalMixture2D_Recovery_Parity.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-105307-683-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverAMHCopulaParameters","method":"RMC.BestFit.Verification.Bivariate.BivariateAnalysisParameterRecoveryTests.RecoverAMHCopulaParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T16:53:07.6831531+00:00","wallSeconds":30.6330005,"testSeconds":27.6849426,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-105307-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverAMHCopulaParameters\\haden_HADEN_2026-09-09_16_53_38.078.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-105307-683-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverAMHCopulaParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-105338-930-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverClaytonCopulaParameters","method":"RMC.BestFit.Verification.Bivariate.BivariateAnalysisParameterRecoveryTests.RecoverClaytonCopulaParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T16:53:38.9309297+00:00","wallSeconds":51.1152841,"testSeconds":48.0730802,"buildSeconds":1.47,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-105338-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverClaytonCopulaParameters\\haden_HADEN_2026-09-09_16_54_29.785.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-105338-930-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverClaytonCopulaParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-105430-578-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverFrankCopulaParameters","method":"RMC.BestFit.Verification.Bivariate.BivariateAnalysisParameterRecoveryTests.RecoverFrankCopulaParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T16:54:30.5789379+00:00","wallSeconds":35.8197787,"testSeconds":32.8481265,"buildSeconds":1.41,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-105430-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverFrankCopulaParameters\\haden_HADEN_2026-09-09_16_55_06.131.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-105430-578-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverFrankCopulaParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-105506-917-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverGumbelCopulaParameters","method":"RMC.BestFit.Verification.Bivariate.BivariateAnalysisParameterRecoveryTests.RecoverGumbelCopulaParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T16:55:06.9177666+00:00","wallSeconds":61.9719155,"testSeconds":58.9818326,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-105506-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverGumbelCopulaParameters\\haden_HADEN_2026-09-09_16_56_08.649.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-105506-917-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverGumbelCopulaParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-105609-481-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverJoeCopulaParameters","method":"RMC.BestFit.Verification.Bivariate.BivariateAnalysisParameterRecoveryTests.RecoverJoeCopulaParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T16:56:09.4816751+00:00","wallSeconds":69.3734231,"testSeconds":66.1281388,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-105609-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverJoeCopulaParameters\\haden_HADEN_2026-09-09_16_57_18.482.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-105609-481-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverJoeCopulaParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-105719-427-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverNormalCopulaParameters","method":"RMC.BestFit.Verification.Bivariate.BivariateAnalysisParameterRecoveryTests.RecoverNormalCopulaParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T16:57:19.4274000+00:00","wallSeconds":42.875674,"testSeconds":39.8548179,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-105719-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverNormalCopulaParameters\\haden_HADEN_2026-09-09_16_58_02.031.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-105719-427-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverNormalCopulaParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-105802-840-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverStudentTCopulaParametersWithMaximumLikelihood","method":"RMC.BestFit.Verification.Bivariate.BivariateAnalysisParameterRecoveryTests.RecoverStudentTCopulaParametersWithMaximumLikelihood","phase":"stabilized-pass","startedUtc":"2026-09-09T16:58:02.8404091+00:00","wallSeconds":28.2424155,"testSeconds":25.3726633,"buildSeconds":1.35,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-105802-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverStudentTCopulaParametersWithMaximumLikelihood\\haden_HADEN_2026-09-09_16_58_30.795.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-105802-840-RMC_BestFit_Verification_Bivariate_BivariateAnalysisParameterRecoveryTests_RecoverStudentTCopulaParametersWithMaximumLikelihood.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-105831-595-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_ExponentialLinearCombination_ParentResponseInsidePredictiveBands","method":"RMC.BestFit.Verification.Bivariate.CoincidentFrequencyAnalysisTests.ExponentialLinearCombination_ParentResponseInsidePredictiveBands","phase":"stabilized-pass","startedUtc":"2026-09-09T16:58:31.5959161+00:00","wallSeconds":39.9610752,"testSeconds":37.0848485,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-105831-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_ExponentialLinearCombination_ParentResponseInsidePredictiveBands\\haden_HADEN_2026-09-09_16_59_11.318.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-105831-595-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_ExponentialLinearCombination_ParentResponseInsidePredictiveBands.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-105912-073-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoNegative_MatchesClosedForm","method":"RMC.BestFit.Verification.Bivariate.CoincidentFrequencyAnalysisTests.SumOfNormals_RhoNegative_MatchesClosedForm","phase":"stabilized-pass","startedUtc":"2026-09-09T16:59:12.0734495+00:00","wallSeconds":43.1423291,"testSeconds":40.1607674,"buildSeconds":1.45,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-105912-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoNegative_MatchesClosedForm\\haden_HADEN_2026-09-09_16_59_54.975.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-105912-073-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoNegative_MatchesClosedForm.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-105955-805-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoPositive_MatchesClosedForm","method":"RMC.BestFit.Verification.Bivariate.CoincidentFrequencyAnalysisTests.SumOfNormals_RhoPositive_MatchesClosedForm","phase":"stabilized-pass","startedUtc":"2026-09-09T16:59:55.8053396+00:00","wallSeconds":46.3383135,"testSeconds":43.1859776,"buildSeconds":1.52,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-105955-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoPositive_MatchesClosedForm\\haden_HADEN_2026-09-09_17_00_41.897.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-105955-805-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoPositive_MatchesClosedForm.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110042-713-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoZero_MatchesClosedForm","method":"RMC.BestFit.Verification.Bivariate.CoincidentFrequencyAnalysisTests.SumOfNormals_RhoZero_MatchesClosedForm","phase":"stabilized-pass","startedUtc":"2026-09-09T17:00:42.7131691+00:00","wallSeconds":51.1781884,"testSeconds":48.0829368,"buildSeconds":1.47,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110042-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoZero_MatchesClosedForm\\haden_HADEN_2026-09-09_17_01_33.621.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110042-713-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoZero_MatchesClosedForm.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110134-573-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_AliMikhailHaq_InferenceFromMargins_MatchesIndependentOptimum","method":"RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.AliMikhailHaq_InferenceFromMargins_MatchesIndependentOptimum","phase":"stabilized-pass","startedUtc":"2026-09-09T17:01:34.5738851+00:00","wallSeconds":3.4841093,"testSeconds":0.09514,"buildSeconds":1.63,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110134-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_AliMikhailHaq_InferenceFromMargins_MatchesIndependentOptimum\\haden_HADEN_2026-09-09_17_01_37.800.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110134-573-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_AliMikhailHaq_InferenceFromMargins_MatchesIndependentOptimum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110138-741-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_AliMikhailHaq_PseudoLikelihood_MatchesIndependentOptimum","method":"RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.AliMikhailHaq_PseudoLikelihood_MatchesIndependentOptimum","phase":"stabilized-pass","startedUtc":"2026-09-09T17:01:38.7412887+00:00","wallSeconds":3.3342331,"testSeconds":0.0755105,"buildSeconds":1.64,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110138-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_AliMikhailHaq_PseudoLikelihood_MatchesIndependentOptimum\\haden_HADEN_2026-09-09_17_01_41.852.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110138-741-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_AliMikhailHaq_PseudoLikelihood_MatchesIndependentOptimum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110142-699-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Clayton_InferenceFromMargins_MatchesIndependentOptimum","method":"RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Clayton_InferenceFromMargins_MatchesIndependentOptimum","phase":"stabilized-pass","startedUtc":"2026-09-09T17:01:42.6993479+00:00","wallSeconds":3.2667833,"testSeconds":0.079675,"buildSeconds":1.6,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110142-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Clayton_InferenceFromMargins_MatchesIndependentOptimum\\haden_HADEN_2026-09-09_17_01_45.739.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110142-699-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Clayton_InferenceFromMargins_MatchesIndependentOptimum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110146-495-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Clayton_PseudoLikelihood_MatchesIndependentOptimum","method":"RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Clayton_PseudoLikelihood_MatchesIndependentOptimum","phase":"stabilized-pass","startedUtc":"2026-09-09T17:01:46.4959417+00:00","wallSeconds":3.0493182,"testSeconds":0.0705576,"buildSeconds":1.45,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110146-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Clayton_PseudoLikelihood_MatchesIndependentOptimum\\haden_HADEN_2026-09-09_17_01_49.308.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110146-495-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Clayton_PseudoLikelihood_MatchesIndependentOptimum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110150-079-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Frank_InferenceFromMargins_MatchesIndependentOptimum","method":"RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Frank_InferenceFromMargins_MatchesIndependentOptimum","phase":"stabilized-pass","startedUtc":"2026-09-09T17:01:50.0792976+00:00","wallSeconds":3.3674632,"testSeconds":0.0729343,"buildSeconds":1.68,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110150-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Frank_InferenceFromMargins_MatchesIndependentOptimum\\haden_HADEN_2026-09-09_17_01_53.218.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110150-079-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Frank_InferenceFromMargins_MatchesIndependentOptimum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110154-069-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Frank_PseudoLikelihood_MatchesIndependentOptimum","method":"RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Frank_PseudoLikelihood_MatchesIndependentOptimum","phase":"stabilized-pass","startedUtc":"2026-09-09T17:01:54.0691935+00:00","wallSeconds":3.2931272,"testSeconds":0.0804151,"buildSeconds":1.59,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110154-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Frank_PseudoLikelihood_MatchesIndependentOptimum\\haden_HADEN_2026-09-09_17_01_57.122.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110154-069-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Frank_PseudoLikelihood_MatchesIndependentOptimum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110157-946-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Gumbel_InferenceFromMargins_MatchesIndependentOptimum","method":"RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Gumbel_InferenceFromMargins_MatchesIndependentOptimum","phase":"stabilized-pass","startedUtc":"2026-09-09T17:01:57.9467746+00:00","wallSeconds":3.2221081,"testSeconds":0.0983614,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110158-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Gumbel_InferenceFromMargins_MatchesIndependentOptimum\\haden_HADEN_2026-09-09_17_02_00.939.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110157-946-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Gumbel_InferenceFromMargins_MatchesIndependentOptimum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110201-770-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Gumbel_PseudoLikelihood_MatchesIndependentOptimum","method":"RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Gumbel_PseudoLikelihood_MatchesIndependentOptimum","phase":"stabilized-pass","startedUtc":"2026-09-09T17:02:01.7710432+00:00","wallSeconds":3.2655531,"testSeconds":0.0743612,"buildSeconds":1.53,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110201-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Gumbel_PseudoLikelihood_MatchesIndependentOptimum\\haden_HADEN_2026-09-09_17_02_04.807.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110201-770-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Gumbel_PseudoLikelihood_MatchesIndependentOptimum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110205-615-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Joe_InferenceFromMargins_MatchesIndependentOptimum","method":"RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Joe_InferenceFromMargins_MatchesIndependentOptimum","phase":"stabilized-pass","startedUtc":"2026-09-09T17:02:05.6156238+00:00","wallSeconds":3.1247843,"testSeconds":0.0804072,"buildSeconds":1.39,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110205-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Joe_InferenceFromMargins_MatchesIndependentOptimum\\haden_HADEN_2026-09-09_17_02_08.512.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110205-615-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Joe_InferenceFromMargins_MatchesIndependentOptimum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110209-325-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Joe_PseudoLikelihood_MatchesIndependentOptimum","method":"RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Joe_PseudoLikelihood_MatchesIndependentOptimum","phase":"stabilized-pass","startedUtc":"2026-09-09T17:02:09.3256049+00:00","wallSeconds":3.2744165,"testSeconds":0.0772223,"buildSeconds":1.53,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110209-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Joe_PseudoLikelihood_MatchesIndependentOptimum\\haden_HADEN_2026-09-09_17_02_12.358.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110209-325-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Joe_PseudoLikelihood_MatchesIndependentOptimum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110213-215-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Normal_InferenceFromMargins_MatchesIndependentOptimum","method":"RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Normal_InferenceFromMargins_MatchesIndependentOptimum","phase":"stabilized-pass","startedUtc":"2026-09-09T17:02:13.2152798+00:00","wallSeconds":3.2012826,"testSeconds":0.0782936,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110213-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Normal_InferenceFromMargins_MatchesIndependentOptimum\\haden_HADEN_2026-09-09_17_02_16.187.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110213-215-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Normal_InferenceFromMargins_MatchesIndependentOptimum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110217-033-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Normal_PseudoLikelihood_MatchesIndependentOptimum","method":"RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.Normal_PseudoLikelihood_MatchesIndependentOptimum","phase":"stabilized-pass","startedUtc":"2026-09-09T17:02:17.0332959+00:00","wallSeconds":3.5342978,"testSeconds":0.0823783,"buildSeconds":1.62,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110217-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Normal_PseudoLikelihood_MatchesIndependentOptimum\\haden_HADEN_2026-09-09_17_02_20.192.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110217-033-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_Normal_PseudoLikelihood_MatchesIndependentOptimum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110221-186-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_StudentT_InferenceFromMargins_MatchesIndependentOptimum","method":"RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.StudentT_InferenceFromMargins_MatchesIndependentOptimum","phase":"stabilized-pass","startedUtc":"2026-09-09T17:02:21.1862243+00:00","wallSeconds":33.4352582,"testSeconds":30.0748478,"buildSeconds":1.62,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110221-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_StudentT_InferenceFromMargins_MatchesIndependentOptimum\\haden_HADEN_2026-09-09_17_02_54.321.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110221-186-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_StudentT_InferenceFromMargins_MatchesIndependentOptimum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110255-272-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_StudentT_PseudoLikelihood_MatchesIndependentOptimum","method":"RMC.BestFit.Verification.Bivariate.CopulaEstimationOracleTests.StudentT_PseudoLikelihood_MatchesIndependentOptimum","phase":"stabilized-pass","startedUtc":"2026-09-09T17:02:55.2724150+00:00","wallSeconds":33.2383681,"testSeconds":30.1242005,"buildSeconds":1.53,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110255-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_StudentT_PseudoLikelihood_MatchesIndependentOptimum\\haden_HADEN_2026-09-09_17_03_28.286.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110255-272-RMC_BestFit_Verification_Bivariate_CopulaEstimationOracleTests_StudentT_PseudoLikelihood_MatchesIndependentOptimum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110350-885-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoZero_MatchesClosedForm","method":"RMC.BestFit.Verification.Bivariate.CoincidentFrequencyAnalysisTests.SumOfNormals_RhoZero_MatchesClosedForm","phase":"coincident-zero-runtime-baseline","startedUtc":"2026-09-09T17:03:50.8870705+00:00","wallSeconds":44.4243838,"testSeconds":41.1593144,"buildSeconds":1.58,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline\\TestResults\\VerificationFocused\\20260909-110351-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoZero_MatchesClosedForm\\haden_HADEN_2026-09-09_17_04_35.030.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110350-885-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoZero_MatchesClosedForm.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"d80bfa8621c48a78cf4ebad7f01326841fac37aa","sourceSha256":"95A9BB4F8927EF0934A13A39B566C1FA30BDB42B2F95AC965746DD122DB39BA0"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-baseline","commit":"fbe0989a87a0bd38a297f8a42e8d979585d0f4ad","sourceSha256":"17C77A3DDAE1EF7B39BA3945DB5F6FB61F93F3D1D57FD48AF96248C9A6054F43"},"numericsAssemblySha256":"3E82F983E4795FD50104CBF0F950A7513BDF94540C3397401A8EC5BBF1A6C60F","bestFitAssemblySha256":"67A7E4809110D816C06ED83E4F9068F6E6FA78E490A5486FCEF62A56D7019DC0","verificationAssemblySha256":"542282B3090D914193685DF647EB18960BC31D061A17CACA7E63A2AA92CC9E46","dependencies":["Exponential","LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Compare current48.083s with pinned baseline after historical34.482s timing hint."} +{"runId":"20260909-110436-190-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoZero_MatchesClosedForm","method":"RMC.BestFit.Verification.Bivariate.CoincidentFrequencyAnalysisTests.SumOfNormals_RhoZero_MatchesClosedForm","phase":"coincident-zero-runtime-current","startedUtc":"2026-09-09T17:04:36.1909983+00:00","wallSeconds":42.7041324,"testSeconds":39.5699971,"buildSeconds":1.53,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110436-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoZero_MatchesClosedForm\\haden_HADEN_2026-09-09_17_05_18.627.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110436-190-RMC_BestFit_Verification_Bivariate_CoincidentFrequencyAnalysisTests_SumOfNormals_RhoZero_MatchesClosedForm.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Adjacent unchanged-source repeat for zero-correlation timing comparison."} +{"runId":"20260909-110539-793-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_CriteriaRankingWeightsAndConfiguredOrder_MatchIndependentOracle","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisCriteriaVerificationTests.CriteriaRankingWeightsAndConfiguredOrder_MatchIndependentOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:05:39.7959843+00:00","wallSeconds":3.5534292,"testSeconds":0.0902202,"buildSeconds":1.67,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110539-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_CriteriaRankingWeightsAndConfiguredOrder_MatchIndependentOracle\\haden_HADEN_2026-09-09_17_05_43.100.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110539-793-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_CriteriaRankingWeightsAndConfiguredOrder_MatchIndependentOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Logistic","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110544-465-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_FittedParameters_MatchIndependentOracle","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisCriteriaVerificationTests.FittedParameters_MatchIndependentOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:05:44.4659413+00:00","wallSeconds":3.4052118,"testSeconds":0.085612,"buildSeconds":1.59,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110544-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_FittedParameters_MatchIndependentOracle\\haden_HADEN_2026-09-09_17_05_47.641.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110544-465-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisCriteriaVerificationTests_FittedParameters_MatchIndependentOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Gumbel","Logistic","Normal","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110548-514-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Exponential_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Exponential_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T17:05:48.5141392+00:00","wallSeconds":22.4162885,"testSeconds":19.2661618,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110548-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Exponential_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_17_06_10.597.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110548-514-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Exponential_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110611-488-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gamma_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Gamma_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T17:06:11.4889132+00:00","wallSeconds":4.5583499,"testSeconds":1.7251646,"buildSeconds":1.38,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110611-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gamma_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_17_06_15.831.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110611-488-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gamma_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110616-707-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedExtremeValue_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.GeneralizedExtremeValue_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T17:06:16.7076900+00:00","wallSeconds":4.6917952,"testSeconds":1.602409,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110616-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedExtremeValue_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_17_06_21.168.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110616-707-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedExtremeValue_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110621-930-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedLogistic_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.GeneralizedLogistic_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T17:06:21.9301170+00:00","wallSeconds":19.4996715,"testSeconds":16.5784639,"buildSeconds":1.41,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110622-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedLogistic_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_17_06_41.197.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110621-930-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedLogistic_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110642-018-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedNormal_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.GeneralizedNormal_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T17:06:42.0190293+00:00","wallSeconds":5.5433897,"testSeconds":2.5477767,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110642-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedNormal_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_17_06_47.338.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110642-018-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedNormal_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110648-134-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedPareto_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.GeneralizedPareto_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T17:06:48.1343569+00:00","wallSeconds":23.8272003,"testSeconds":20.813082,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110648-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedPareto_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_17_07_11.731.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110648-134-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_GeneralizedPareto_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110712-528-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gumbel_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Gumbel_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T17:07:12.5287340+00:00","wallSeconds":4.4012132,"testSeconds":1.3946687,"buildSeconds":1.45,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110712-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gumbel_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_17_07_16.703.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110712-528-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Gumbel_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110717-433-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_KappaFour_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.KappaFour_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T17:07:17.4331020+00:00","wallSeconds":7.7668888,"testSeconds":4.9066144,"buildSeconds":1.37,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110717-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_KappaFour_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_17_07_24.952.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110717-433-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_KappaFour_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110725-825-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Logistic_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Logistic_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T17:07:25.8259302+00:00","wallSeconds":18.3431585,"testSeconds":15.16988,"buildSeconds":1.54,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110725-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Logistic_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_17_07_43.934.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110725-825-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Logistic_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110744-706-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogNormal_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.LogNormal_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T17:07:44.7063117+00:00","wallSeconds":7.8729733,"testSeconds":5.0747201,"buildSeconds":1.26,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110744-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogNormal_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_17_07_52.344.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110744-706-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_LogNormal_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110753-081-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Normal_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Normal_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T17:07:53.0815428+00:00","wallSeconds":4.5352668,"testSeconds":1.7134978,"buildSeconds":1.37,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110753-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Normal_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_17_07_57.397.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110753-081-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Normal_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110758-157-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Weibull_N1000_FittingAnalysisRecoversGeneratingFamily","method":"RMC.BestFit.Verification.DistributionFitting.FittingAnalysisRecoveryTests.Weibull_N1000_FittingAnalysisRecoversGeneratingFamily","phase":"stabilized-pass","startedUtc":"2026-09-09T17:07:58.1576728+00:00","wallSeconds":4.655126,"testSeconds":1.5954294,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110758-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Weibull_N1000_FittingAnalysisRecoversGeneratingFamily\\haden_HADEN_2026-09-09_17_08_02.596.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110758-157-RMC_BestFit_Verification_DistributionFitting_FittingAnalysisRecoveryTests_Weibull_N1000_FittingAnalysisRecoversGeneratingFamily.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110803-350-RMC_BestFit_Verification_DistributionFitting_KappaFourZeroShapeVerificationTests_ZeroShapeNonzeroHondo_PdfMatchesAnalyticalDerivative","method":"RMC.BestFit.Verification.DistributionFitting.KappaFourZeroShapeVerificationTests.ZeroShapeNonzeroHondo_PdfMatchesAnalyticalDerivative","phase":"stabilized-pass","startedUtc":"2026-09-09T17:08:03.3506552+00:00","wallSeconds":2.8896798,"testSeconds":0.0063676,"buildSeconds":1.29,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110803-RMC_BestFit_Verification_DistributionFitting_KappaFourZeroShapeVerificationTests_ZeroShapeNonzeroHondo_PdfMatchesAnalyticalDerivative\\haden_HADEN_2026-09-09_17_08_05.993.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110803-350-RMC_BestFit_Verification_DistributionFitting_KappaFourZeroShapeVerificationTests_ZeroShapeNonzeroHondo_PdfMatchesAnalyticalDerivative.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["KappaFour"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110806-806-RMC_BestFit_Verification_DistributionFitting_KappaFourZeroShapeVerificationTests_ZeroShapeNonzeroHondo_QuantileInvertsCdf","method":"RMC.BestFit.Verification.DistributionFitting.KappaFourZeroShapeVerificationTests.ZeroShapeNonzeroHondo_QuantileInvertsCdf","phase":"stabilized-pass","startedUtc":"2026-09-09T17:08:06.8061123+00:00","wallSeconds":3.0262871,"testSeconds":0.006457,"buildSeconds":1.47,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110806-RMC_BestFit_Verification_DistributionFitting_KappaFourZeroShapeVerificationTests_ZeroShapeNonzeroHondo_QuantileInvertsCdf\\haden_HADEN_2026-09-09_17_08_09.594.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110806-806-RMC_BestFit_Verification_DistributionFitting_KappaFourZeroShapeVerificationTests_ZeroShapeNonzeroHondo_QuantileInvertsCdf.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["KappaFour"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110810-429-RMC_BestFit_Verification_DistributionFitting_LmomcoDistributionFittingVerificationTests_GeneralizedLogistic_MleAndDistributionFunctionsMatchLmomco","method":"RMC.BestFit.Verification.DistributionFitting.LmomcoDistributionFittingVerificationTests.GeneralizedLogistic_MleAndDistributionFunctionsMatchLmomco","phase":"stabilized-pass","startedUtc":"2026-09-09T17:08:10.4292005+00:00","wallSeconds":3.112422,"testSeconds":0.0734683,"buildSeconds":1.45,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110810-RMC_BestFit_Verification_DistributionFitting_LmomcoDistributionFittingVerificationTests_GeneralizedLogistic_MleAndDistributionFunctionsMatchLmomco\\haden_HADEN_2026-09-09_17_08_13.311.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110810-429-RMC_BestFit_Verification_DistributionFitting_LmomcoDistributionFittingVerificationTests_GeneralizedLogistic_MleAndDistributionFunctionsMatchLmomco.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["GeneralizedLogistic","GeneralizedNormal","Logistic","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110814-108-RMC_BestFit_Verification_DistributionFitting_LmomcoDistributionFittingVerificationTests_GeneralizedNormal_MleAndDistributionFunctionsMatchLmomco","method":"RMC.BestFit.Verification.DistributionFitting.LmomcoDistributionFittingVerificationTests.GeneralizedNormal_MleAndDistributionFunctionsMatchLmomco","phase":"stabilized-pass","startedUtc":"2026-09-09T17:08:14.1083446+00:00","wallSeconds":3.0719022,"testSeconds":0.0737548,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110814-RMC_BestFit_Verification_DistributionFitting_LmomcoDistributionFittingVerificationTests_GeneralizedNormal_MleAndDistributionFunctionsMatchLmomco\\haden_HADEN_2026-09-09_17_08_16.953.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110814-108-RMC_BestFit_Verification_DistributionFitting_LmomcoDistributionFittingVerificationTests_GeneralizedNormal_MleAndDistributionFunctionsMatchLmomco.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["GeneralizedLogistic","GeneralizedNormal","Logistic","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110817-735-RMC_BestFit_Verification_DistributionFitting_Log10NormalFittingVerificationTests_ClosedFormMle_LikelihoodCdfAndQuantileMatchAnalyticalOracle","method":"RMC.BestFit.Verification.DistributionFitting.Log10NormalFittingVerificationTests.ClosedFormMle_LikelihoodCdfAndQuantileMatchAnalyticalOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:08:17.7357753+00:00","wallSeconds":3.1093471,"testSeconds":0.0476438,"buildSeconds":1.51,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110817-RMC_BestFit_Verification_DistributionFitting_Log10NormalFittingVerificationTests_ClosedFormMle_LikelihoodCdfAndQuantileMatchAnalyticalOracle\\haden_HADEN_2026-09-09_17_08_20.623.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110817-735-RMC_BestFit_Verification_DistributionFitting_Log10NormalFittingVerificationTests_ClosedFormMle_LikelihoodCdfAndQuantileMatchAnalyticalOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110821-409-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Exponential_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Exponential_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T17:08:21.4094946+00:00","wallSeconds":2.8827527,"testSeconds":0.057951,"buildSeconds":1.28,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110821-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Exponential_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_17_08_24.069.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110821-409-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Exponential_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110824-853-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Gamma_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Gamma_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T17:08:24.8533755+00:00","wallSeconds":3.1219456,"testSeconds":0.0620494,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110824-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Gamma_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_17_08_27.753.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110824-853-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Gamma_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110828-556-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_GeneralizedExtremeValue_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.GeneralizedExtremeValue_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T17:08:28.5566126+00:00","wallSeconds":3.11373,"testSeconds":0.0872711,"buildSeconds":1.47,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110828-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_GeneralizedExtremeValue_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_17_08_31.448.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110828-556-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_GeneralizedExtremeValue_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110832-218-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_GeneralizedPareto_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.GeneralizedPareto_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T17:08:32.2187767+00:00","wallSeconds":3.0797333,"testSeconds":0.0635778,"buildSeconds":1.45,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110832-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_GeneralizedPareto_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_17_08_35.068.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110832-218-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_GeneralizedPareto_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110835-831-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Gumbel_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Gumbel_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T17:08:35.8319879+00:00","wallSeconds":3.0693138,"testSeconds":0.0522741,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110835-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Gumbel_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_17_08_38.685.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110835-831-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Gumbel_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110839-469-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_KappaFour_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.KappaFour_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T17:08:39.4690601+00:00","wallSeconds":3.3834596,"testSeconds":0.3599088,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110839-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_KappaFour_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_17_08_42.625.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110839-469-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_KappaFour_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110843-397-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LnNormal_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.LnNormal_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T17:08:43.3976939+00:00","wallSeconds":3.0068053,"testSeconds":0.0535986,"buildSeconds":1.47,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110843-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LnNormal_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_17_08_46.183.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110843-397-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LnNormal_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110846-955-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Logistic_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Logistic_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T17:08:46.9557845+00:00","wallSeconds":3.0731115,"testSeconds":0.0526234,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110847-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Logistic_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_17_08_49.799.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110846-955-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Logistic_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110850-587-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogNormal_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.LogNormal_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T17:08:50.5871911+00:00","wallSeconds":3.0101748,"testSeconds":0.0615968,"buildSeconds":1.42,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110850-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogNormal_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_17_08_53.373.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110850-587-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_LogNormal_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110854-149-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Normal_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Normal_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T17:08:54.1498944+00:00","wallSeconds":2.9592081,"testSeconds":0.059172,"buildSeconds":1.33,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110854-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Normal_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_17_08_56.883.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110854-149-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Normal_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110857-690-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Weibull_MleAndDistributionFunctionsMatchScipy","method":"RMC.BestFit.Verification.DistributionFitting.ScipyDistributionFittingVerificationTests.Weibull_MleAndDistributionFunctionsMatchScipy","phase":"stabilized-pass","startedUtc":"2026-09-09T17:08:57.6907628+00:00","wallSeconds":3.1038812,"testSeconds":0.0600098,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110857-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Weibull_MleAndDistributionFunctionsMatchScipy\\haden_HADEN_2026-09-09_17_09_00.578.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110857-690-RMC_BestFit_Verification_DistributionFitting_ScipyDistributionFittingVerificationTests_Weibull_MleAndDistributionFunctionsMatchScipy.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110901-372-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Exponential_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_Exponential_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T17:09:01.3723481+00:00","wallSeconds":3.0927797,"testSeconds":0.0487837,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110901-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Exponential_MLE\\haden_HADEN_2026-09-09_17_09_04.231.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110901-372-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Exponential_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110905-044-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GammaDist_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_GammaDist_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T17:09:05.0440905+00:00","wallSeconds":3.1652418,"testSeconds":0.0538104,"buildSeconds":1.55,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110905-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GammaDist_MLE\\haden_HADEN_2026-09-09_17_09_07.985.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110905-044-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GammaDist_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110908-829-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedExtremeValue_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_GeneralizedExtremeValue_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T17:09:08.8296671+00:00","wallSeconds":3.1329627,"testSeconds":0.0797468,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110908-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedExtremeValue_MLE\\haden_HADEN_2026-09-09_17_09_11.744.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110908-829-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedExtremeValue_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110912-535-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedLogistic_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_GeneralizedLogistic_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T17:09:12.5357300+00:00","wallSeconds":3.2134295,"testSeconds":0.0887431,"buildSeconds":1.47,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110912-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedLogistic_MLE\\haden_HADEN_2026-09-09_17_09_15.499.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110912-535-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedLogistic_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110916-288-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedNormal_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_GeneralizedNormal_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T17:09:16.2884454+00:00","wallSeconds":3.1842609,"testSeconds":0.1235946,"buildSeconds":1.53,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110916-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedNormal_MLE\\haden_HADEN_2026-09-09_17_09_19.242.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110916-288-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedNormal_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110920-077-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedPareto_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_GeneralizedPareto_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T17:09:20.0772065+00:00","wallSeconds":3.189515,"testSeconds":0.1377272,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110920-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedPareto_MLE\\haden_HADEN_2026-09-09_17_09_23.034.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110920-077-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_GeneralizedPareto_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110923-826-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Gumbel_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_Gumbel_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T17:09:23.8264789+00:00","wallSeconds":3.1345196,"testSeconds":0.047929,"buildSeconds":1.53,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110923-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Gumbel_MLE\\haden_HADEN_2026-09-09_17_09_26.739.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110923-826-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Gumbel_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110927-515-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Kappa4_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_Kappa4_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T17:09:27.5152200+00:00","wallSeconds":3.4191602,"testSeconds":0.363942,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110927-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Kappa4_MLE\\haden_HADEN_2026-09-09_17_09_30.709.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110927-515-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Kappa4_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110931-490-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LnNormal_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_LnNormal_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T17:09:31.4910103+00:00","wallSeconds":3.1087016,"testSeconds":0.0579414,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110931-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LnNormal_MLE\\haden_HADEN_2026-09-09_17_09_34.379.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110931-490-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LnNormal_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110935-191-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Logistic_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_Logistic_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T17:09:35.1918482+00:00","wallSeconds":3.101268,"testSeconds":0.0552601,"buildSeconds":1.47,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110935-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Logistic_MLE\\haden_HADEN_2026-09-09_17_09_38.067.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110935-191-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Logistic_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110938-844-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LogNormal_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_LogNormal_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T17:09:38.8445572+00:00","wallSeconds":3.0968576,"testSeconds":0.0555542,"buildSeconds":1.47,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110938-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LogNormal_MLE\\haden_HADEN_2026-09-09_17_09_41.677.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110938-844-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_LogNormal_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110942-506-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Normal_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_Normal_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T17:09:42.5066276+00:00","wallSeconds":3.0886118,"testSeconds":0.047265,"buildSeconds":1.51,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110942-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Normal_MLE\\haden_HADEN_2026-09-09_17_09_45.370.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110942-506-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Normal_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110946-200-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Weibull_MLE","method":"RMC.BestFit.Verification.DistributionFitting.UnivariateDistributionMLETests.Test_Weibull_MLE","phase":"stabilized-pass","startedUtc":"2026-09-09T17:09:46.2001186+00:00","wallSeconds":3.1601276,"testSeconds":0.0573907,"buildSeconds":1.52,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110946-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Weibull_MLE\\haden_HADEN_2026-09-09_17_09_49.139.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110946-200-RMC_BestFit_Verification_DistributionFitting_UnivariateDistributionMLETests_Test_Weibull_MLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","KappaFour","LnNormal","Logistic","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110949-941-RMC_BestFit_Verification_ModelEstimation_BayesianAnalysisRecoveryTests_Test_Estimate_PosteriorMean_NearTrueValue","method":"RMC.BestFit.Verification.ModelEstimation.BayesianAnalysisRecoveryTests.Test_Estimate_PosteriorMean_NearTrueValue","phase":"stabilized-pass","startedUtc":"2026-09-09T17:09:49.9411633+00:00","wallSeconds":9.4266264,"testSeconds":6.3837337,"buildSeconds":1.46,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110950-RMC_BestFit_Verification_ModelEstimation_BayesianAnalysisRecoveryTests_Test_Estimate_PosteriorMean_NearTrueValue\\haden_HADEN_2026-09-09_17_09_59.119.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110949-941-RMC_BestFit_Verification_ModelEstimation_BayesianAnalysisRecoveryTests_Test_Estimate_PosteriorMean_NearTrueValue.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-110959-917-RMC_BestFit_Verification_ModelEstimation_BayesianAnalysisRecoveryTests_Test_N1000_DefaultPosteriorIntervals_ContainGeneratingParameters","method":"RMC.BestFit.Verification.ModelEstimation.BayesianAnalysisRecoveryTests.Test_N1000_DefaultPosteriorIntervals_ContainGeneratingParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T17:09:59.9173182+00:00","wallSeconds":9.3195787,"testSeconds":6.2481166,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-110959-RMC_BestFit_Verification_ModelEstimation_BayesianAnalysisRecoveryTests_Test_N1000_DefaultPosteriorIntervals_ContainGeneratingParameters\\haden_HADEN_2026-09-09_17_10_08.982.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-110959-917-RMC_BestFit_Verification_ModelEstimation_BayesianAnalysisRecoveryTests_Test_N1000_DefaultPosteriorIntervals_ContainGeneratingParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111009-738-RMC_BestFit_Verification_ModelEstimation_BayesianConjugateOracleVerificationTests_InformativePrior_NormalNormalConjugatePosterior_MatchesClosedForm","method":"RMC.BestFit.Verification.ModelEstimation.BayesianConjugateOracleVerificationTests.InformativePrior_NormalNormalConjugatePosterior_MatchesClosedForm","phase":"stabilized-pass","startedUtc":"2026-09-09T17:10:09.7384758+00:00","wallSeconds":4.8613356,"testSeconds":1.9382903,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111009-RMC_BestFit_Verification_ModelEstimation_BayesianConjugateOracleVerificationTests_InformativePrior_NormalNormalConjugatePosterior_MatchesClosedForm\\haden_HADEN_2026-09-09_17_10_14.364.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111009-738-RMC_BestFit_Verification_ModelEstimation_BayesianConjugateOracleVerificationTests_InformativePrior_NormalNormalConjugatePosterior_MatchesClosedForm.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111015-160-RMC_BestFit_Verification_ModelEstimation_GmmInfluenceDiagnosticsVerificationTests_Log10NormalObservationInfluence_MatchesRGmmOracle","method":"RMC.BestFit.Verification.ModelEstimation.GmmInfluenceDiagnosticsVerificationTests.Log10NormalObservationInfluence_MatchesRGmmOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:10:15.1601060+00:00","wallSeconds":3.0973584,"testSeconds":0.0536365,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111015-RMC_BestFit_Verification_ModelEstimation_GmmInfluenceDiagnosticsVerificationTests_Log10NormalObservationInfluence_MatchesRGmmOracle\\haden_HADEN_2026-09-09_17_10_17.995.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111015-160-RMC_BestFit_Verification_ModelEstimation_GmmInfluenceDiagnosticsVerificationTests_Log10NormalObservationInfluence_MatchesRGmmOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111018-824-RMC_BestFit_Verification_ModelEstimation_GmmInfluenceDiagnosticsVerificationTests_VanishingCenteredPenalty_PreservesObservationInfluenceScale","method":"RMC.BestFit.Verification.ModelEstimation.GmmInfluenceDiagnosticsVerificationTests.VanishingCenteredPenalty_PreservesObservationInfluenceScale","phase":"stabilized-pass","startedUtc":"2026-09-09T17:10:18.8246308+00:00","wallSeconds":3.0918805,"testSeconds":0.0598068,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111018-RMC_BestFit_Verification_ModelEstimation_GmmInfluenceDiagnosticsVerificationTests_VanishingCenteredPenalty_PreservesObservationInfluenceScale\\haden_HADEN_2026-09-09_17_10_21.695.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111018-824-RMC_BestFit_Verification_ModelEstimation_GmmInfluenceDiagnosticsVerificationTests_VanishingCenteredPenalty_PreservesObservationInfluenceScale.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111022-533-RMC_BestFit_Verification_ModelEstimation_InformationCriterionOracleTests_DIC_MatchesRBayesianToolsOracle","method":"RMC.BestFit.Verification.ModelEstimation.InformationCriterionOracleTests.DIC_MatchesRBayesianToolsOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:10:22.5332047+00:00","wallSeconds":3.3079181,"testSeconds":0.1514975,"buildSeconds":1.56,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111022-RMC_BestFit_Verification_ModelEstimation_InformationCriterionOracleTests_DIC_MatchesRBayesianToolsOracle\\haden_HADEN_2026-09-09_17_10_25.616.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111022-533-RMC_BestFit_Verification_ModelEstimation_InformationCriterionOracleTests_DIC_MatchesRBayesianToolsOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111026-415-RMC_BestFit_Verification_ModelEstimation_InformationCriterionOracleTests_WAIC_MatchesRLooOracle","method":"RMC.BestFit.Verification.ModelEstimation.InformationCriterionOracleTests.WAIC_MatchesRLooOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:10:26.4154011+00:00","wallSeconds":3.2281821,"testSeconds":0.1486585,"buildSeconds":1.51,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111026-RMC_BestFit_Verification_ModelEstimation_InformationCriterionOracleTests_WAIC_MatchesRLooOracle\\haden_HADEN_2026-09-09_17_10_29.422.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111026-415-RMC_BestFit_Verification_ModelEstimation_InformationCriterionOracleTests_WAIC_MatchesRLooOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111030-185-RMC_BestFit_Verification_ModelEstimation_JointPriorSamplingVerificationTests_SampleFromPriors_SoftJointPrior_CurrentlyDrawsIndependentMarginals","method":"RMC.BestFit.Verification.ModelEstimation.JointPriorSamplingVerificationTests.SampleFromPriors_SoftJointPrior_CurrentlyDrawsIndependentMarginals","phase":"stabilized-pass","startedUtc":"2026-09-09T17:10:30.1852309+00:00","wallSeconds":3.0630968,"testSeconds":0.0243505,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111030-RMC_BestFit_Verification_ModelEstimation_JointPriorSamplingVerificationTests_SampleFromPriors_SoftJointPrior_CurrentlyDrawsIndependentMarginals\\haden_HADEN_2026-09-09_17_10_33.012.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111030-185-RMC_BestFit_Verification_ModelEstimation_JointPriorSamplingVerificationTests_SampleFromPriors_SoftJointPrior_CurrentlyDrawsIndependentMarginals.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111033-795-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_FlatPriorMap_MatchesClosedFormLog10NormalMle","method":"RMC.BestFit.Verification.ModelEstimation.Log10NormalEstimationEquivalenceTests.FlatPriorMap_MatchesClosedFormLog10NormalMle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:10:33.7958672+00:00","wallSeconds":3.0273948,"testSeconds":0.0574783,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111033-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_FlatPriorMap_MatchesClosedFormLog10NormalMle\\haden_HADEN_2026-09-09_17_10_36.606.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111033-795-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_FlatPriorMap_MatchesClosedFormLog10NormalMle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111037-388-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_GmmMuPenaltyRegimes_MatchAnalyticalFitAndVarianceInfluence","method":"RMC.BestFit.Verification.ModelEstimation.Log10NormalEstimationEquivalenceTests.GmmMuPenaltyRegimes_MatchAnalyticalFitAndVarianceInfluence","phase":"stabilized-pass","startedUtc":"2026-09-09T17:10:37.3888848+00:00","wallSeconds":3.0902183,"testSeconds":0.0567092,"buildSeconds":1.51,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111037-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_GmmMuPenaltyRegimes_MatchAnalyticalFitAndVarianceInfluence\\haden_HADEN_2026-09-09_17_10_40.256.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111037-388-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_GmmMuPenaltyRegimes_MatchAnalyticalFitAndVarianceInfluence.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111041-006-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_MapAndGmmMuPosterior_InverseVarianceReferenceInsideCentral95Intervals","method":"RMC.BestFit.Verification.ModelEstimation.Log10NormalEstimationEquivalenceTests.MapAndGmmMuPosterior_InverseVarianceReferenceInsideCentral95Intervals","phase":"stabilized-pass","startedUtc":"2026-09-09T17:10:41.0069418+00:00","wallSeconds":3.0810886,"testSeconds":0.0838585,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111041-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_MapAndGmmMuPosterior_InverseVarianceReferenceInsideCentral95Intervals\\haden_HADEN_2026-09-09_17_10_43.865.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111041-006-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_MapAndGmmMuPosterior_InverseVarianceReferenceInsideCentral95Intervals.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111044-612-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_MapMuPriorRegimes_MatchAnalyticalFitAndVarianceInfluence","method":"RMC.BestFit.Verification.ModelEstimation.Log10NormalEstimationEquivalenceTests.MapMuPriorRegimes_MatchAnalyticalFitAndVarianceInfluence","phase":"stabilized-pass","startedUtc":"2026-09-09T17:10:44.6126688+00:00","wallSeconds":3.117392,"testSeconds":0.0693993,"buildSeconds":1.51,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111044-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_MapMuPriorRegimes_MatchAnalyticalFitAndVarianceInfluence\\haden_HADEN_2026-09-09_17_10_47.506.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111044-612-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_MapMuPriorRegimes_MatchAnalyticalFitAndVarianceInfluence.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111048-272-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_UnpenalizedB17CGmm_MatchesExactSampleMoments","method":"RMC.BestFit.Verification.ModelEstimation.Log10NormalEstimationEquivalenceTests.UnpenalizedB17CGmm_MatchesExactSampleMoments","phase":"stabilized-pass","startedUtc":"2026-09-09T17:10:48.2721906+00:00","wallSeconds":3.0555607,"testSeconds":0.0445377,"buildSeconds":1.45,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111048-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_UnpenalizedB17CGmm_MatchesExactSampleMoments\\haden_HADEN_2026-09-09_17_10_51.106.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111048-272-RMC_BestFit_Verification_ModelEstimation_Log10NormalEstimationEquivalenceTests_UnpenalizedB17CGmm_MatchesExactSampleMoments.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111051-858-RMC_BestFit_Verification_ModelEstimation_Log10NormalInfluenceVerificationTests_GmmObservationInfluence_CurrentMagnitudeDiffersFromExactDeletionScale","method":"RMC.BestFit.Verification.ModelEstimation.Log10NormalInfluenceVerificationTests.GmmObservationInfluence_CurrentMagnitudeDiffersFromExactDeletionScale","phase":"stabilized-pass","startedUtc":"2026-09-09T17:10:51.8581684+00:00","wallSeconds":3.1006519,"testSeconds":0.0561904,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111051-RMC_BestFit_Verification_ModelEstimation_Log10NormalInfluenceVerificationTests_GmmObservationInfluence_CurrentMagnitudeDiffersFromExactDeletionScale\\haden_HADEN_2026-09-09_17_10_54.729.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111051-858-RMC_BestFit_Verification_ModelEstimation_Log10NormalInfluenceVerificationTests_GmmObservationInfluence_CurrentMagnitudeDiffersFromExactDeletionScale.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111055-544-RMC_BestFit_Verification_ModelEstimation_MaximumAPosterioriInformationCriteriaOracleTests_AIC_UsesDataLikelihoodAtMap_ExcludesPriorDensity","method":"RMC.BestFit.Verification.ModelEstimation.MaximumAPosterioriInformationCriteriaOracleTests.AIC_UsesDataLikelihoodAtMap_ExcludesPriorDensity","phase":"stabilized-pass","startedUtc":"2026-09-09T17:10:55.5443585+00:00","wallSeconds":3.1056909,"testSeconds":0.0368147,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111055-RMC_BestFit_Verification_ModelEstimation_MaximumAPosterioriInformationCriteriaOracleTests_AIC_UsesDataLikelihoodAtMap_ExcludesPriorDensity\\haden_HADEN_2026-09-09_17_10_58.433.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111055-544-RMC_BestFit_Verification_ModelEstimation_MaximumAPosterioriInformationCriteriaOracleTests_AIC_UsesDataLikelihoodAtMap_ExcludesPriorDensity.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111059-241-RMC_BestFit_Verification_ModelEstimation_MaximumAPosterioriInformationCriteriaOracleTests_BIC_UsesDataLikelihoodAtMap_ExcludesPriorDensity","method":"RMC.BestFit.Verification.ModelEstimation.MaximumAPosterioriInformationCriteriaOracleTests.BIC_UsesDataLikelihoodAtMap_ExcludesPriorDensity","phase":"stabilized-pass","startedUtc":"2026-09-09T17:10:59.2412934+00:00","wallSeconds":3.1347562,"testSeconds":0.0429119,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111059-RMC_BestFit_Verification_ModelEstimation_MaximumAPosterioriInformationCriteriaOracleTests_BIC_UsesDataLikelihoodAtMap_ExcludesPriorDensity\\haden_HADEN_2026-09-09_17_11_02.146.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111059-241-RMC_BestFit_Verification_ModelEstimation_MaximumAPosterioriInformationCriteriaOracleTests_BIC_UsesDataLikelihoodAtMap_ExcludesPriorDensity.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111102-909-RMC_BestFit_Verification_ModelEstimation_MaximumAPosterioriRecoveryTests_Test_Estimate_NormalData_FindsMode","method":"RMC.BestFit.Verification.ModelEstimation.MaximumAPosterioriRecoveryTests.Test_Estimate_NormalData_FindsMode","phase":"stabilized-pass","startedUtc":"2026-09-09T17:11:02.9097099+00:00","wallSeconds":3.1427033,"testSeconds":0.0549119,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111102-RMC_BestFit_Verification_ModelEstimation_MaximumAPosterioriRecoveryTests_Test_Estimate_NormalData_FindsMode\\haden_HADEN_2026-09-09_17_11_05.828.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111102-909-RMC_BestFit_Verification_ModelEstimation_MaximumAPosterioriRecoveryTests_Test_Estimate_NormalData_FindsMode.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111106-632-RMC_BestFit_Verification_ModelEstimation_McmcNumericalVerificationTests_Arwmh_BestFitWiringRecordsEveryRealizedStateInAdaptiveCovariance","method":"RMC.BestFit.Verification.ModelEstimation.McmcNumericalVerificationTests.Arwmh_BestFitWiringRecordsEveryRealizedStateInAdaptiveCovariance","phase":"stabilized-pass","startedUtc":"2026-09-09T17:11:06.6324508+00:00","wallSeconds":3.152874,"testSeconds":0.031399,"buildSeconds":1.52,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111106-RMC_BestFit_Verification_ModelEstimation_McmcNumericalVerificationTests_Arwmh_BestFitWiringRecordsEveryRealizedStateInAdaptiveCovariance\\haden_HADEN_2026-09-09_17_11_09.555.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111106-632-RMC_BestFit_Verification_ModelEstimation_McmcNumericalVerificationTests_Arwmh_BestFitWiringRecordsEveryRealizedStateInAdaptiveCovariance.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111110-348-RMC_BestFit_Verification_ModelEstimation_McmcNumericalVerificationTests_ConservativeEss_MatchesRPosteriorBulkAndTailOracle","method":"RMC.BestFit.Verification.ModelEstimation.McmcNumericalVerificationTests.ConservativeEss_MatchesRPosteriorBulkAndTailOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:11:10.3483554+00:00","wallSeconds":3.1024347,"testSeconds":0.0277029,"buildSeconds":1.51,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111110-RMC_BestFit_Verification_ModelEstimation_McmcNumericalVerificationTests_ConservativeEss_MatchesRPosteriorBulkAndTailOracle\\haden_HADEN_2026-09-09_17_11_13.159.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111110-348-RMC_BestFit_Verification_ModelEstimation_McmcNumericalVerificationTests_ConservativeEss_MatchesRPosteriorBulkAndTailOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111114-010-RMC_BestFit_Verification_ModelEstimation_McmcNumericalVerificationTests_Nuts_BestFitNumericalGradientMatchesPosteriorGradient","method":"RMC.BestFit.Verification.ModelEstimation.McmcNumericalVerificationTests.Nuts_BestFitNumericalGradientMatchesPosteriorGradient","phase":"stabilized-pass","startedUtc":"2026-09-09T17:11:14.0102676+00:00","wallSeconds":3.0984521,"testSeconds":0.0157773,"buildSeconds":1.51,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111114-RMC_BestFit_Verification_ModelEstimation_McmcNumericalVerificationTests_Nuts_BestFitNumericalGradientMatchesPosteriorGradient\\haden_HADEN_2026-09-09_17_11_16.839.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111114-010-RMC_BestFit_Verification_ModelEstimation_McmcNumericalVerificationTests_Nuts_BestFitNumericalGradientMatchesPosteriorGradient.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111117-681-RMC_BestFit_Verification_ModelEstimation_McmcNumericalVerificationTests_RankNormalizedRhat_MatchesRPosteriorOracle","method":"RMC.BestFit.Verification.ModelEstimation.McmcNumericalVerificationTests.RankNormalizedRhat_MatchesRPosteriorOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:11:17.6812859+00:00","wallSeconds":3.0345124,"testSeconds":0.0204987,"buildSeconds":1.42,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111117-RMC_BestFit_Verification_ModelEstimation_McmcNumericalVerificationTests_RankNormalizedRhat_MatchesRPosteriorOracle\\haden_HADEN_2026-09-09_17_11_20.455.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111117-681-RMC_BestFit_Verification_ModelEstimation_McmcNumericalVerificationTests_RankNormalizedRhat_MatchesRPosteriorOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111121-279-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Exponential_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_Exponential_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T17:11:21.2800167+00:00","wallSeconds":3.396773,"testSeconds":0.1704253,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111121-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Exponential_RecoversTrueParameters\\haden_HADEN_2026-09-09_17_11_24.256.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111121-279-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Exponential_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111125-370-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Gamma_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_Gamma_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T17:11:25.3706522+00:00","wallSeconds":3.6756948,"testSeconds":0.2751646,"buildSeconds":1.64,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111125-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Gamma_RecoversTrueParameters\\haden_HADEN_2026-09-09_17_11_28.824.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111125-370-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Gamma_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111129-615-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_GeneralizedLogistic_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_GeneralizedLogistic_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T17:11:29.6151953+00:00","wallSeconds":4.3894019,"testSeconds":1.2099527,"buildSeconds":1.52,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111129-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_GeneralizedLogistic_RecoversTrueParameters\\haden_HADEN_2026-09-09_17_11_33.756.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111129-615-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_GeneralizedLogistic_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111134-576-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_GeneralizedNormal_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_GeneralizedNormal_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T17:11:34.5760428+00:00","wallSeconds":4.0154042,"testSeconds":0.8504831,"buildSeconds":1.56,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111134-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_GeneralizedNormal_RecoversTrueParameters\\haden_HADEN_2026-09-09_17_11_38.368.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111134-576-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_GeneralizedNormal_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111139-106-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_GeneralizedPareto_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_GeneralizedPareto_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T17:11:39.1062288+00:00","wallSeconds":3.4151631,"testSeconds":0.3092581,"buildSeconds":1.54,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111139-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_GeneralizedPareto_RecoversTrueParameters\\haden_HADEN_2026-09-09_17_11_42.294.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111139-106-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_GeneralizedPareto_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111143-037-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_GEV_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_GEV_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T17:11:43.0372238+00:00","wallSeconds":3.4690025,"testSeconds":0.4282906,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111143-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_GEV_RecoversTrueParameters\\haden_HADEN_2026-09-09_17_11_46.285.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111143-037-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_GEV_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111147-059-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Gumbel_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_Gumbel_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T17:11:47.0600057+00:00","wallSeconds":3.1966971,"testSeconds":0.1257413,"buildSeconds":1.47,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111147-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Gumbel_RecoversTrueParameters\\haden_HADEN_2026-09-09_17_11_50.029.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111147-059-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Gumbel_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111150-807-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LnNormal_MatchesClosedFormLogSampleMLE","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_LnNormal_MatchesClosedFormLogSampleMLE","phase":"stabilized-pass","startedUtc":"2026-09-09T17:11:50.8075918+00:00","wallSeconds":3.2557721,"testSeconds":0.1453425,"buildSeconds":1.55,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111150-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LnNormal_MatchesClosedFormLogSampleMLE\\haden_HADEN_2026-09-09_17_11_53.845.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111150-807-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LnNormal_MatchesClosedFormLogSampleMLE.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111154-637-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LnNormal_RecoversGeneratingParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_LnNormal_RecoversGeneratingParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T17:11:54.6378630+00:00","wallSeconds":3.2510544,"testSeconds":0.1524873,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111154-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LnNormal_RecoversGeneratingParameters\\haden_HADEN_2026-09-09_17_11_57.653.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111154-637-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_LnNormal_RecoversGeneratingParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111158-468-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Logistic_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_Logistic_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T17:11:58.4682259+00:00","wallSeconds":2.9904375,"testSeconds":0.1288735,"buildSeconds":1.32,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111158-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Logistic_RecoversTrueParameters\\haden_HADEN_2026-09-09_17_12_01.228.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111158-468-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Logistic_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111202-001-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Normal_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_Normal_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T17:12:02.0012877+00:00","wallSeconds":3.1184927,"testSeconds":0.0996468,"buildSeconds":1.52,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111202-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Normal_RecoversTrueParameters\\haden_HADEN_2026-09-09_17_12_04.894.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111202-001-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Normal_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111205-772-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Weibull_RecoversTrueParameters","method":"RMC.BestFit.Verification.ModelEstimation.MLEIntegrationTests.Test_MLE_Weibull_RecoversTrueParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T17:12:05.7730030+00:00","wallSeconds":3.2875213,"testSeconds":0.1693633,"buildSeconds":1.54,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111205-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Weibull_RecoversTrueParameters\\haden_HADEN_2026-09-09_17_12_08.837.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111205-772-RMC_BestFit_Verification_ModelEstimation_MLEIntegrationTests_Test_MLE_Weibull_RecoversTrueParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","GeneralizedExtremeValue","GeneralizedLogistic","GeneralizedNormal","GeneralizedPareto","Gumbel","LnNormal","Logistic","LogPearsonTypeIII","Normal","PearsonTypeIII","Weibull"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111209-649-RMC_BestFit_Verification_ModelEstimation_PosteriorResamplingVerificationTests_CoincidentFrequencyPosteriorResampling_MatchesIndependentClosedFormOracle","method":"RMC.BestFit.Verification.ModelEstimation.PosteriorResamplingVerificationTests.CoincidentFrequencyPosteriorResampling_MatchesIndependentClosedFormOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:12:09.6495994+00:00","wallSeconds":4.7399732,"testSeconds":1.6558932,"buildSeconds":1.54,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111209-RMC_BestFit_Verification_ModelEstimation_PosteriorResamplingVerificationTests_CoincidentFrequencyPosteriorResampling_MatchesIndependentClosedFormOracle\\haden_HADEN_2026-09-09_17_12_14.168.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111209-649-RMC_BestFit_Verification_ModelEstimation_PosteriorResamplingVerificationTests_CoincidentFrequencyPosteriorResampling_MatchesIndependentClosedFormOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111214-946-RMC_BestFit_Verification_ModelEstimation_PosteriorResamplingVerificationTests_CompositePosteriorResampling_MatchesIndependentCartesianOracle","method":"RMC.BestFit.Verification.ModelEstimation.PosteriorResamplingVerificationTests.CompositePosteriorResampling_MatchesIndependentCartesianOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:12:14.9466044+00:00","wallSeconds":5.214104,"testSeconds":2.1854865,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111215-RMC_BestFit_Verification_ModelEstimation_PosteriorResamplingVerificationTests_CompositePosteriorResampling_MatchesIndependentCartesianOracle\\haden_HADEN_2026-09-09_17_12_19.927.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111214-946-RMC_BestFit_Verification_ModelEstimation_PosteriorResamplingVerificationTests_CompositePosteriorResampling_MatchesIndependentCartesianOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111220-697-RMC_BestFit_Verification_ModelEstimation_PsisLooOracleVerificationTests_ParetoInfluence_UsesRloo210DiagnosticThreshold","method":"RMC.BestFit.Verification.ModelEstimation.PsisLooOracleVerificationTests.ParetoInfluence_UsesRloo210DiagnosticThreshold","phase":"stabilized-pass","startedUtc":"2026-09-09T17:12:20.6980373+00:00","wallSeconds":3.1768393,"testSeconds":0.1551341,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111220-RMC_BestFit_Verification_ModelEstimation_PsisLooOracleVerificationTests_ParetoInfluence_UsesRloo210DiagnosticThreshold\\haden_HADEN_2026-09-09_17_12_23.647.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111220-697-RMC_BestFit_Verification_ModelEstimation_PsisLooOracleVerificationTests_ParetoInfluence_UsesRloo210DiagnosticThreshold.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111224-459-RMC_BestFit_Verification_ModelEstimation_PsisLooOracleVerificationTests_PSISLOO_MatchesRLooOracle","method":"RMC.BestFit.Verification.ModelEstimation.PsisLooOracleVerificationTests.PSISLOO_MatchesRLooOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:12:24.4592587+00:00","wallSeconds":3.2561385,"testSeconds":0.1520282,"buildSeconds":1.51,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111224-RMC_BestFit_Verification_ModelEstimation_PsisLooOracleVerificationTests_PSISLOO_MatchesRLooOracle\\haden_HADEN_2026-09-09_17_12_27.486.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111224-459-RMC_BestFit_Verification_ModelEstimation_PsisLooOracleVerificationTests_PSISLOO_MatchesRLooOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111228-299-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_SingleSegment_Default","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveBayesianRecoveryTests.Test_EstimateParameters_SingleSegment_Default","phase":"stabilized-pass","startedUtc":"2026-09-09T17:12:28.2993874+00:00","wallSeconds":25.601728,"testSeconds":22.5452613,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111228-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_SingleSegment_Default\\haden_HADEN_2026-09-09_17_12_53.659.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111228-299-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_SingleSegment_Default.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111254-409-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_SingleSegment_LowNoise","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveBayesianRecoveryTests.Test_EstimateParameters_SingleSegment_LowNoise","phase":"stabilized-pass","startedUtc":"2026-09-09T17:12:54.4090786+00:00","wallSeconds":26.687538,"testSeconds":23.7475603,"buildSeconds":1.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111254-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_SingleSegment_LowNoise\\haden_HADEN_2026-09-09_17_13_20.846.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111254-409-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_SingleSegment_LowNoise.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111321-688-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_SingleSegment_WideRange","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveBayesianRecoveryTests.Test_EstimateParameters_SingleSegment_WideRange","phase":"stabilized-pass","startedUtc":"2026-09-09T17:13:21.6881616+00:00","wallSeconds":28.15784,"testSeconds":24.8920626,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111321-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_SingleSegment_WideRange\\haden_HADEN_2026-09-09_17_13_49.611.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111321-688-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_SingleSegment_WideRange.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111350-364-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_ThreeSegment_MultipleControl","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveBayesianRecoveryTests.Test_EstimateParameters_ThreeSegment_MultipleControl","phase":"stabilized-pass","startedUtc":"2026-09-09T17:13:50.3643938+00:00","wallSeconds":92.2211882,"testSeconds":89.1348656,"buildSeconds":1.41,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111350-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_ThreeSegment_MultipleControl\\haden_HADEN_2026-09-09_17_15_22.172.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111350-364-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_ThreeSegment_MultipleControl.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111523-152-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_TwoSegment_BankfullTransition","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveBayesianRecoveryTests.Test_EstimateParameters_TwoSegment_BankfullTransition","phase":"stabilized-pass","startedUtc":"2026-09-09T17:15:23.1525461+00:00","wallSeconds":57.1748767,"testSeconds":53.9912572,"buildSeconds":1.55,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111523-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_TwoSegment_BankfullTransition\\haden_HADEN_2026-09-09_17_16_20.093.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111523-152-RMC_BestFit_Verification_RatingCurve_RatingCurveBayesianRecoveryTests_Test_EstimateParameters_TwoSegment_BankfullTransition.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111620-851-RMC_BestFit_Verification_RatingCurve_RatingCurveExampleRecoveryTests_Mle_OneSegment_RecoversExampleCurve","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveExampleRecoveryTests.Mle_OneSegment_RecoversExampleCurve","phase":"stabilized-pass","startedUtc":"2026-09-09T17:16:20.8519542+00:00","wallSeconds":3.3395445,"testSeconds":0.4588381,"buildSeconds":1.39,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111620-RMC_BestFit_Verification_RatingCurve_RatingCurveExampleRecoveryTests_Mle_OneSegment_RecoversExampleCurve\\haden_HADEN_2026-09-09_17_16_23.962.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111620-851-RMC_BestFit_Verification_RatingCurve_RatingCurveExampleRecoveryTests_Mle_OneSegment_RecoversExampleCurve.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111624-705-RMC_BestFit_Verification_RatingCurve_RatingCurveExampleRecoveryTests_Mle_ThreeSegment_RecoversExampleCurve","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveExampleRecoveryTests.Mle_ThreeSegment_RecoversExampleCurve","phase":"stabilized-pass","startedUtc":"2026-09-09T17:16:24.7053615+00:00","wallSeconds":12.0405349,"testSeconds":9.0711772,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111624-RMC_BestFit_Verification_RatingCurve_RatingCurveExampleRecoveryTests_Mle_ThreeSegment_RecoversExampleCurve\\haden_HADEN_2026-09-09_17_16_36.517.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111624-705-RMC_BestFit_Verification_RatingCurve_RatingCurveExampleRecoveryTests_Mle_ThreeSegment_RecoversExampleCurve.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111637-248-RMC_BestFit_Verification_RatingCurve_RatingCurveExampleRecoveryTests_Mle_TwoSegment_RecoversExampleCurve","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveExampleRecoveryTests.Mle_TwoSegment_RecoversExampleCurve","phase":"stabilized-pass","startedUtc":"2026-09-09T17:16:37.2486665+00:00","wallSeconds":5.3550556,"testSeconds":2.4831831,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111637-RMC_BestFit_Verification_RatingCurve_RatingCurveExampleRecoveryTests_Mle_TwoSegment_RecoversExampleCurve\\haden_HADEN_2026-09-09_17_16_42.380.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111637-248-RMC_BestFit_Verification_RatingCurve_RatingCurveExampleRecoveryTests_Mle_TwoSegment_RecoversExampleCurve.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111643-101-RMC_BestFit_Verification_RatingCurve_RatingCurveLikelihoodOracleTests_OneSegment_DataLogLikelihood_IsDischargeSpaceDensity","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveLikelihoodOracleTests.OneSegment_DataLogLikelihood_IsDischargeSpaceDensity","phase":"stabilized-pass","startedUtc":"2026-09-09T17:16:43.1015932+00:00","wallSeconds":2.9526551,"testSeconds":0.0358392,"buildSeconds":1.39,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111643-RMC_BestFit_Verification_RatingCurve_RatingCurveLikelihoodOracleTests_OneSegment_DataLogLikelihood_IsDischargeSpaceDensity\\haden_HADEN_2026-09-09_17_16_45.781.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111643-101-RMC_BestFit_Verification_RatingCurve_RatingCurveLikelihoodOracleTests_OneSegment_DataLogLikelihood_IsDischargeSpaceDensity.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111646-583-RMC_BestFit_Verification_RatingCurve_RatingCurveLikelihoodOracleTests_ThreeSegment_DataLogLikelihood_IsDischargeSpaceDensity","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveLikelihoodOracleTests.ThreeSegment_DataLogLikelihood_IsDischargeSpaceDensity","phase":"stabilized-pass","startedUtc":"2026-09-09T17:16:46.5834713+00:00","wallSeconds":2.8313156,"testSeconds":0.0370373,"buildSeconds":1.24,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111646-RMC_BestFit_Verification_RatingCurve_RatingCurveLikelihoodOracleTests_ThreeSegment_DataLogLikelihood_IsDischargeSpaceDensity\\haden_HADEN_2026-09-09_17_16_49.199.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111646-583-RMC_BestFit_Verification_RatingCurve_RatingCurveLikelihoodOracleTests_ThreeSegment_DataLogLikelihood_IsDischargeSpaceDensity.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111649-915-RMC_BestFit_Verification_RatingCurve_RatingCurveLikelihoodOracleTests_TwoSegment_DataLogLikelihood_IsDischargeSpaceDensity","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveLikelihoodOracleTests.TwoSegment_DataLogLikelihood_IsDischargeSpaceDensity","phase":"stabilized-pass","startedUtc":"2026-09-09T17:16:49.9152985+00:00","wallSeconds":2.9050768,"testSeconds":0.0379651,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111649-RMC_BestFit_Verification_RatingCurve_RatingCurveLikelihoodOracleTests_TwoSegment_DataLogLikelihood_IsDischargeSpaceDensity\\haden_HADEN_2026-09-09_17_16_52.592.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111649-915-RMC_BestFit_Verification_RatingCurve_RatingCurveLikelihoodOracleTests_TwoSegment_DataLogLikelihood_IsDischargeSpaceDensity.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111653-355-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_SingleSegment_Default","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveMLERecoveryTests.Test_EstimateParameters_SingleSegment_Default","phase":"stabilized-pass","startedUtc":"2026-09-09T17:16:53.3550755+00:00","wallSeconds":3.4673824,"testSeconds":0.5681314,"buildSeconds":1.41,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111653-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_SingleSegment_Default\\haden_HADEN_2026-09-09_17_16_56.597.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111653-355-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_SingleSegment_Default.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111657-325-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_SingleSegment_LowNoise","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveMLERecoveryTests.Test_EstimateParameters_SingleSegment_LowNoise","phase":"stabilized-pass","startedUtc":"2026-09-09T17:16:57.3252856+00:00","wallSeconds":3.3170842,"testSeconds":0.5897053,"buildSeconds":1.24,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111657-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_SingleSegment_LowNoise\\haden_HADEN_2026-09-09_17_17_00.424.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111657-325-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_SingleSegment_LowNoise.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111701-142-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_SingleSegment_WideRange","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveMLERecoveryTests.Test_EstimateParameters_SingleSegment_WideRange","phase":"stabilized-pass","startedUtc":"2026-09-09T17:17:01.1425265+00:00","wallSeconds":3.4280427,"testSeconds":0.5595799,"buildSeconds":1.41,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111701-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_SingleSegment_WideRange\\haden_HADEN_2026-09-09_17_17_04.359.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111701-142-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_SingleSegment_WideRange.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111705-073-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_ThreeSegment_MultipleControl","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveMLERecoveryTests.Test_EstimateParameters_ThreeSegment_MultipleControl","phase":"stabilized-pass","startedUtc":"2026-09-09T17:17:05.0738325+00:00","wallSeconds":13.347993,"testSeconds":10.4648302,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111705-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_ThreeSegment_MultipleControl\\haden_HADEN_2026-09-09_17_17_18.185.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111705-073-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_ThreeSegment_MultipleControl.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111718-919-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_TwoSegment_BankfullTransition","method":"RMC.BestFit.Verification.RatingCurve.RatingCurveMLERecoveryTests.Test_EstimateParameters_TwoSegment_BankfullTransition","phase":"stabilized-pass","startedUtc":"2026-09-09T17:17:18.9199768+00:00","wallSeconds":5.4901584,"testSeconds":2.8016769,"buildSeconds":1.24,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111718-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_TwoSegment_BankfullTransition\\haden_HADEN_2026-09-09_17_17_24.185.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111718-919-RMC_BestFit_Verification_RatingCurve_RatingCurveMLERecoveryTests_Test_EstimateParameters_TwoSegment_BankfullTransition.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111724-918-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_BasicHomogeneous_RecoversParameters","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVBayesianRecoveryTests.Bayesian_BasicHomogeneous_RecoversParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T17:17:24.9189349+00:00","wallSeconds":60.0263601,"testSeconds":57.1440621,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111724-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_BasicHomogeneous_RecoversParameters\\haden_HADEN_2026-09-09_17_18_24.717.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111724-918-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_BasicHomogeneous_RecoversParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","Gumbel"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-111825-449-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithCopula_RecoversParameters","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVBayesianRecoveryTests.Bayesian_WithCopula_RecoversParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T17:18:25.4491848+00:00","wallSeconds":156.7461668,"testSeconds":153.6711797,"buildSeconds":1.39,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-111825-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithCopula_RecoversParameters\\haden_HADEN_2026-09-09_17_21_01.775.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-111825-449-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithCopula_RecoversParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","Gumbel"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112102-726-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithLocationRegression_RecoversParameters","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVBayesianRecoveryTests.Bayesian_WithLocationRegression_RecoversParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T17:21:02.7262663+00:00","wallSeconds":103.1817497,"testSeconds":100.0631015,"buildSeconds":1.47,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112102-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithLocationRegression_RecoversParameters\\haden_HADEN_2026-09-09_17_22_45.539.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112102-726-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithLocationRegression_RecoversParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","Gumbel"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112246-430-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithNegativeShape_RecoversParameters","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVBayesianRecoveryTests.Bayesian_WithNegativeShape_RecoversParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T17:22:46.4306326+00:00","wallSeconds":59.8716379,"testSeconds":57.0482919,"buildSeconds":1.3,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112246-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithNegativeShape_RecoversParameters\\haden_HADEN_2026-09-09_17_23_46.077.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112246-430-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithNegativeShape_RecoversParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","Gumbel"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112346-812-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithPositiveShape_RecoversParameters","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVBayesianRecoveryTests.Bayesian_WithPositiveShape_RecoversParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T17:23:46.8121019+00:00","wallSeconds":59.7137206,"testSeconds":56.8543539,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112346-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithPositiveShape_RecoversParameters\\haden_HADEN_2026-09-09_17_24_46.304.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112346-812-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithPositiveShape_RecoversParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","Gumbel"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112447-028-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithZeroShape_RecoversParameters","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVBayesianRecoveryTests.Bayesian_WithZeroShape_RecoversParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T17:24:47.0285557+00:00","wallSeconds":60.285255,"testSeconds":57.4102122,"buildSeconds":1.41,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112447-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithZeroShape_RecoversParameters\\haden_HADEN_2026-09-09_17_25_47.081.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112447-028-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVBayesianRecoveryTests_Bayesian_WithZeroShape_RecoversParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","Gumbel"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112547-821-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_GodambeSensitivityVariabilityAndSandwich_MatchIndependentOracle","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVChunk14OracleTests.GodambeSensitivityVariabilityAndSandwich_MatchIndependentOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:25:47.8211089+00:00","wallSeconds":2.9482157,"testSeconds":0.0363989,"buildSeconds":1.39,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112547-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_GodambeSensitivityVariabilityAndSandwich_MatchIndependentOracle\\haden_HADEN_2026-09-09_17_25_50.484.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112547-821-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_GodambeSensitivityVariabilityAndSandwich_MatchIndependentOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112551-275-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_HeldOutCopulaFold_MatchesIndependentFittedOracle","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVChunk14OracleTests.HeldOutCopulaFold_MatchesIndependentFittedOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:25:51.2751759+00:00","wallSeconds":3.5122584,"testSeconds":0.6706935,"buildSeconds":1.36,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112551-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_HeldOutCopulaFold_MatchesIndependentFittedOracle\\haden_HADEN_2026-09-09_17_25_54.565.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112551-275-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_HeldOutCopulaFold_MatchesIndependentFittedOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112555-292-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_RegionalFixedDrawAggregation_MatchesIndependentPosteriorOracle","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVChunk14OracleTests.RegionalFixedDrawAggregation_MatchesIndependentPosteriorOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:25:55.2920732+00:00","wallSeconds":2.9508976,"testSeconds":0.1043422,"buildSeconds":1.38,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112555-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_RegionalFixedDrawAggregation_MatchesIndependentPosteriorOracle\\haden_HADEN_2026-09-09_17_25_58.028.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112555-292-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_RegionalFixedDrawAggregation_MatchesIndependentPosteriorOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112558-752-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_TemporalBlockBootstrap_MatchesIndependentWholeRowOracle","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVChunk14OracleTests.TemporalBlockBootstrap_MatchesIndependentWholeRowOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:25:58.7524392+00:00","wallSeconds":3.2850701,"testSeconds":0.4131444,"buildSeconds":1.41,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112558-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_TemporalBlockBootstrap_MatchesIndependentWholeRowOracle\\haden_HADEN_2026-09-09_17_26_01.821.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112558-752-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_TemporalBlockBootstrap_MatchesIndependentWholeRowOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112602-542-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_VarianceInflation_UsesExactIndependentAnalyticalTransformation","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVChunk14OracleTests.VarianceInflation_UsesExactIndependentAnalyticalTransformation","phase":"stabilized-pass","startedUtc":"2026-09-09T17:26:02.5426324+00:00","wallSeconds":2.9907461,"testSeconds":0.0982222,"buildSeconds":1.41,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112602-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_VarianceInflation_UsesExactIndependentAnalyticalTransformation\\haden_HADEN_2026-09-09_17_26_05.305.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112602-542-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVChunk14OracleTests_VarianceInflation_UsesExactIndependentAnalyticalTransformation.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112606-039-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVInformationCriteriaTests_MissingSiteModel_InformationCriteria_UseRowYearBlocks","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVInformationCriteriaTests.MissingSiteModel_InformationCriteria_UseRowYearBlocks","phase":"stabilized-pass","startedUtc":"2026-09-09T17:26:06.0392480+00:00","wallSeconds":15.9353733,"testSeconds":13.021223,"buildSeconds":1.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112606-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVInformationCriteriaTests_MissingSiteModel_InformationCriteria_UseRowYearBlocks\\haden_HADEN_2026-09-09_17_26_21.750.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112606-039-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVInformationCriteriaTests_MissingSiteModel_InformationCriteria_UseRowYearBlocks.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112622-485-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_CompleteRows_CopulaLikelihood_MatchesIndependentOracle","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVLikelihoodOracleTests.CompleteRows_CopulaLikelihood_MatchesIndependentOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:26:22.4850928+00:00","wallSeconds":2.7611635,"testSeconds":0.0277919,"buildSeconds":1.27,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112622-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_CompleteRows_CopulaLikelihood_MatchesIndependentOracle\\haden_HADEN_2026-09-09_17_26_25.036.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112622-485-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_CompleteRows_CopulaLikelihood_MatchesIndependentOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112625-753-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_LocationErrorModel_DataLogLikelihood_ExcludesProcessDensity","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVLikelihoodOracleTests.LocationErrorModel_DataLogLikelihood_ExcludesProcessDensity","phase":"stabilized-pass","startedUtc":"2026-09-09T17:26:25.7536500+00:00","wallSeconds":2.9257708,"testSeconds":0.0327157,"buildSeconds":1.41,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112625-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_LocationErrorModel_DataLogLikelihood_ExcludesProcessDensity\\haden_HADEN_2026-09-09_17_26_28.456.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112625-753-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_LocationErrorModel_DataLogLikelihood_ExcludesProcessDensity.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112629-179-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_LocationErrorModel_PosteriorKernel_IsInvariantToTheDecomposition","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVLikelihoodOracleTests.LocationErrorModel_PosteriorKernel_IsInvariantToTheDecomposition","phase":"stabilized-pass","startedUtc":"2026-09-09T17:26:29.1797983+00:00","wallSeconds":2.9283328,"testSeconds":0.0338044,"buildSeconds":1.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112629-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_LocationErrorModel_PosteriorKernel_IsInvariantToTheDecomposition\\haden_HADEN_2026-09-09_17_26_31.887.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112629-179-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_LocationErrorModel_PosteriorKernel_IsInvariantToTheDecomposition.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112632-616-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_LocationErrorModel_ScalarAndPointwiseDecompositionsAgree","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVLikelihoodOracleTests.LocationErrorModel_ScalarAndPointwiseDecompositionsAgree","phase":"stabilized-pass","startedUtc":"2026-09-09T17:26:32.6170011+00:00","wallSeconds":2.7940339,"testSeconds":0.0361207,"buildSeconds":1.27,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112632-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_LocationErrorModel_ScalarAndPointwiseDecompositionsAgree\\haden_HADEN_2026-09-09_17_26_35.175.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112632-616-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_LocationErrorModel_ScalarAndPointwiseDecompositionsAgree.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112635-914-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_LocationErrorModel_ScalarAndPointwiseGradientsAgree","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVLikelihoodOracleTests.LocationErrorModel_ScalarAndPointwiseGradientsAgree","phase":"stabilized-pass","startedUtc":"2026-09-09T17:26:35.9142155+00:00","wallSeconds":2.9222357,"testSeconds":0.0347667,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112635-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_LocationErrorModel_ScalarAndPointwiseGradientsAgree\\haden_HADEN_2026-09-09_17_26_38.595.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112635-914-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_LocationErrorModel_ScalarAndPointwiseGradientsAgree.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112639-353-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_MarginalOnly_WithoutCopula_MatchesIndependentOracle","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVLikelihoodOracleTests.MarginalOnly_WithoutCopula_MatchesIndependentOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:26:39.3537025+00:00","wallSeconds":3.0397394,"testSeconds":0.0224765,"buildSeconds":1.45,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112639-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_MarginalOnly_WithoutCopula_MatchesIndependentOracle\\haden_HADEN_2026-09-09_17_26_42.094.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112639-353-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_MarginalOnly_WithoutCopula_MatchesIndependentOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112642-904-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_MissingSites_DataLogLikelihood_UsesObservedSiteCopulaSubmatrix","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVLikelihoodOracleTests.MissingSites_DataLogLikelihood_UsesObservedSiteCopulaSubmatrix","phase":"stabilized-pass","startedUtc":"2026-09-09T17:26:42.9041318+00:00","wallSeconds":2.8107617,"testSeconds":0.0290592,"buildSeconds":1.3,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112642-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_MissingSites_DataLogLikelihood_UsesObservedSiteCopulaSubmatrix\\haden_HADEN_2026-09-09_17_26_45.496.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112642-904-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_MissingSites_DataLogLikelihood_UsesObservedSiteCopulaSubmatrix.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112646-225-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_MissingSites_PointwiseRows_MatchObservedSubsetOracle","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVLikelihoodOracleTests.MissingSites_PointwiseRows_MatchObservedSubsetOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:26:46.2257703+00:00","wallSeconds":2.9640108,"testSeconds":0.0316697,"buildSeconds":1.41,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112646-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_MissingSites_PointwiseRows_MatchObservedSubsetOracle\\haden_HADEN_2026-09-09_17_26_48.917.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112646-225-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVLikelihoodOracleTests_MissingSites_PointwiseRows_MatchObservedSubsetOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112649-690-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVMLERecoveryTests_MLE_BasicHomogeneous_RecoversParameters","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVMLERecoveryTests.MLE_BasicHomogeneous_RecoversParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T17:26:49.6903266+00:00","wallSeconds":3.3799272,"testSeconds":0.4922727,"buildSeconds":1.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112649-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVMLERecoveryTests_MLE_BasicHomogeneous_RecoversParameters\\haden_HADEN_2026-09-09_17_26_52.857.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112649-690-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVMLERecoveryTests_MLE_BasicHomogeneous_RecoversParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112653-569-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVMLERecoveryTests_MLE_WithCopula_RecoversParameters","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVMLERecoveryTests.MLE_WithCopula_RecoversParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T17:26:53.5697177+00:00","wallSeconds":4.6080396,"testSeconds":1.8183714,"buildSeconds":1.32,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112653-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVMLERecoveryTests_MLE_WithCopula_RecoversParameters\\haden_HADEN_2026-09-09_17_26_57.959.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112653-569-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVMLERecoveryTests_MLE_WithCopula_RecoversParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112658-676-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVSimulationVerificationTests_GenerateRandomValues_WithCopula_ReproducesTheFittedIntersiteDependence","method":"RMC.BestFit.Verification.SpatialExtremes.SpatialGEVSimulationVerificationTests.GenerateRandomValues_WithCopula_ReproducesTheFittedIntersiteDependence","phase":"stabilized-pass","startedUtc":"2026-09-09T17:26:58.6762594+00:00","wallSeconds":3.0456285,"testSeconds":0.079574,"buildSeconds":1.42,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112658-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVSimulationVerificationTests_GenerateRandomValues_WithCopula_ReproducesTheFittedIntersiteDependence\\haden_HADEN_2026-09-09_17_27_01.420.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112658-676-RMC_BestFit_Verification_SpatialExtremes_SpatialGEVSimulationVerificationTests_GenerateRandomValues_WithCopula_ReproducesTheFittedIntersiteDependence.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GeneralizedExtremeValue","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112702-233-RMC_BestFit_Verification_TimeSeriesAnalysis_ARAnalysisTests_Test_EstimateParameters_AR1","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.ARAnalysisTests.Test_EstimateParameters_AR1","phase":"stabilized-pass","startedUtc":"2026-09-09T17:27:02.2332037+00:00","wallSeconds":22.0989462,"testSeconds":19.174504,"buildSeconds":1.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112702-RMC_BestFit_Verification_TimeSeriesAnalysis_ARAnalysisTests_Test_EstimateParameters_AR1\\haden_HADEN_2026-09-09_17_27_24.101.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112702-233-RMC_BestFit_Verification_TimeSeriesAnalysis_ARAnalysisTests_Test_EstimateParameters_AR1.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112724-888-RMC_BestFit_Verification_TimeSeriesAnalysis_AutoRegressiveMLERecoveryTests_Test_EstimateParameters_AR1","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.AutoRegressiveMLERecoveryTests.Test_EstimateParameters_AR1","phase":"stabilized-pass","startedUtc":"2026-09-09T17:27:24.8881031+00:00","wallSeconds":3.2449795,"testSeconds":0.1294438,"buildSeconds":1.53,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112724-RMC_BestFit_Verification_TimeSeriesAnalysis_AutoRegressiveMLERecoveryTests_Test_EstimateParameters_AR1\\haden_HADEN_2026-09-09_17_27_27.901.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112724-888-RMC_BestFit_Verification_TimeSeriesAnalysis_AutoRegressiveMLERecoveryTests_Test_EstimateParameters_AR1.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112728-731-RMC_BestFit_Verification_TimeSeriesAnalysis_MAAnalysisTests_Test_EstimateParameters_MA1","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.MAAnalysisTests.Test_EstimateParameters_MA1","phase":"stabilized-pass","startedUtc":"2026-09-09T17:27:28.7315600+00:00","wallSeconds":20.342377,"testSeconds":17.1666671,"buildSeconds":1.54,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112728-RMC_BestFit_Verification_TimeSeriesAnalysis_MAAnalysisTests_Test_EstimateParameters_MA1\\haden_HADEN_2026-09-09_17_27_48.844.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112728-731-RMC_BestFit_Verification_TimeSeriesAnalysis_MAAnalysisTests_Test_EstimateParameters_MA1.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112749-581-RMC_BestFit_Verification_TimeSeriesAnalysis_MovingAverageMLERecoveryTests_Test_EstimateParameters_MA1","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.MovingAverageMLERecoveryTests.Test_EstimateParameters_MA1","phase":"stabilized-pass","startedUtc":"2026-09-09T17:27:49.5817650+00:00","wallSeconds":2.9806371,"testSeconds":0.0998706,"buildSeconds":1.39,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112749-RMC_BestFit_Verification_TimeSeriesAnalysis_MovingAverageMLERecoveryTests_Test_EstimateParameters_MA1\\haden_HADEN_2026-09-09_17_27_52.344.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112749-581-RMC_BestFit_Verification_TimeSeriesAnalysis_MovingAverageMLERecoveryTests_Test_EstimateParameters_MA1.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112753-072-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesChunk13OracleTests_ArimaxTrendSeasonalityAndCovariatesMatchIndependentPythonOracle","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesChunk13OracleTests.ArimaxTrendSeasonalityAndCovariatesMatchIndependentPythonOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:27:53.0725348+00:00","wallSeconds":2.9006442,"testSeconds":0.0331697,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112753-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesChunk13OracleTests_ArimaxTrendSeasonalityAndCovariatesMatchIndependentPythonOracle\\haden_HADEN_2026-09-09_17_27_55.760.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112753-072-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesChunk13OracleTests_ArimaxTrendSeasonalityAndCovariatesMatchIndependentPythonOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112756-479-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesChunk13OracleTests_FirstOrderConditionalObjectivesMatchIndependentPythonOracle","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesChunk13OracleTests.FirstOrderConditionalObjectivesMatchIndependentPythonOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:27:56.4790776+00:00","wallSeconds":2.9158627,"testSeconds":0.0273833,"buildSeconds":1.39,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112756-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesChunk13OracleTests_FirstOrderConditionalObjectivesMatchIndependentPythonOracle\\haden_HADEN_2026-09-09_17_27_59.161.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112756-479-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesChunk13OracleTests_FirstOrderConditionalObjectivesMatchIndependentPythonOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112759-918-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesChunk13OracleTests_HigherOrderArAndMaResponsesMatchIndependentPythonOracle","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesChunk13OracleTests.HigherOrderArAndMaResponsesMatchIndependentPythonOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:27:59.9187660+00:00","wallSeconds":2.924314,"testSeconds":0.0242301,"buildSeconds":1.41,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112759-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesChunk13OracleTests_HigherOrderArAndMaResponsesMatchIndependentPythonOracle\\haden_HADEN_2026-09-09_17_28_02.611.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112759-918-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesChunk13OracleTests_HigherOrderArAndMaResponsesMatchIndependentPythonOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112803-400-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesChunk13OracleTests_PureArAndMaThroughArimaMatchIndependentPythonOracle","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesChunk13OracleTests.PureArAndMaThroughArimaMatchIndependentPythonOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:28:03.4010269+00:00","wallSeconds":3.0354058,"testSeconds":0.0213457,"buildSeconds":1.42,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112803-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesChunk13OracleTests_PureArAndMaThroughArimaMatchIndependentPythonOracle\\haden_HADEN_2026-09-09_17_28_06.173.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112803-400-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesChunk13OracleTests_PureArAndMaThroughArimaMatchIndependentPythonOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112806-940-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArAndMaTransformedGeneratorsMatchIndependentOracle","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.ArAndMaTransformedGeneratorsMatchIndependentOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:28:06.9401428+00:00","wallSeconds":2.9532005,"testSeconds":0.0153639,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112807-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArAndMaTransformedGeneratorsMatchIndependentOracle\\haden_HADEN_2026-09-09_17_28_09.636.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112806-940-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArAndMaTransformedGeneratorsMatchIndependentOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112810-468-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArimaAndArimaxPredictionUncertaintyBeginsAtForecastBoundary","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.ArimaAndArimaxPredictionUncertaintyBeginsAtForecastBoundary","phase":"stabilized-pass","startedUtc":"2026-09-09T17:28:10.4687246+00:00","wallSeconds":3.2398291,"testSeconds":0.0277651,"buildSeconds":1.6,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112810-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArimaAndArimaxPredictionUncertaintyBeginsAtForecastBoundary\\haden_HADEN_2026-09-09_17_28_13.446.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112810-468-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArimaAndArimaxPredictionUncertaintyBeginsAtForecastBoundary.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112814-214-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArimaDifferencedTransformedGeneratorMatchesIndependentOracle","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.ArimaDifferencedTransformedGeneratorMatchesIndependentOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:28:14.2144788+00:00","wallSeconds":2.9543918,"testSeconds":0.0197429,"buildSeconds":1.41,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112814-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArimaDifferencedTransformedGeneratorMatchesIndependentOracle\\haden_HADEN_2026-09-09_17_28_16.910.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112814-214-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArimaDifferencedTransformedGeneratorMatchesIndependentOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112817-710-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArimaxDifferencedLikelihoodMatchesDateIndexedIndependentOracle","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.ArimaxDifferencedLikelihoodMatchesDateIndexedIndependentOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:28:17.7105422+00:00","wallSeconds":2.9461586,"testSeconds":0.0324149,"buildSeconds":1.43,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112817-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArimaxDifferencedLikelihoodMatchesDateIndexedIndependentOracle\\haden_HADEN_2026-09-09_17_28_20.435.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112817-710-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArimaxDifferencedLikelihoodMatchesDateIndexedIndependentOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112821-158-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArimaxTransformedDifferencedGeneratorMatchesIndependentOracle","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.ArimaxTransformedDifferencedGeneratorMatchesIndependentOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:28:21.1582216+00:00","wallSeconds":2.9329595,"testSeconds":0.0293283,"buildSeconds":1.42,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112821-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArimaxTransformedDifferencedGeneratorMatchesIndependentOracle\\haden_HADEN_2026-09-09_17_28_23.873.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112821-158-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ArimaxTransformedDifferencedGeneratorMatchesIndependentOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112824-597-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_InformationCriteriaUseDataLikelihoodAtMapAndExcludePrior","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.InformationCriteriaUseDataLikelihoodAtMapAndExcludePrior","phase":"stabilized-pass","startedUtc":"2026-09-09T17:28:24.5978211+00:00","wallSeconds":2.8618784,"testSeconds":0.1186633,"buildSeconds":1.26,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112824-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_InformationCriteriaUseDataLikelihoodAtMapAndExcludePrior\\haden_HADEN_2026-09-09_17_28_27.243.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112824-597-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_InformationCriteriaUseDataLikelihoodAtMapAndExcludePrior.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112827-961-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_InvalidScaleBehaviorMatchesScalarAndPointwiseOracle","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.InvalidScaleBehaviorMatchesScalarAndPointwiseOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:28:27.9616796+00:00","wallSeconds":2.9851924,"testSeconds":0.0374164,"buildSeconds":1.44,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112828-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_InvalidScaleBehaviorMatchesScalarAndPointwiseOracle\\haden_HADEN_2026-09-09_17_28_30.719.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112827-961-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_InvalidScaleBehaviorMatchesScalarAndPointwiseOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112831-459-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ManualTransformLambdaRebuildMatchesIndependentLikelihoodOracle","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.ManualTransformLambdaRebuildMatchesIndependentLikelihoodOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:28:31.4596302+00:00","wallSeconds":2.9462584,"testSeconds":0.0423522,"buildSeconds":1.41,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112831-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ManualTransformLambdaRebuildMatchesIndependentLikelihoodOracle\\haden_HADEN_2026-09-09_17_28_34.171.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112831-459-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_ManualTransformLambdaRebuildMatchesIndependentLikelihoodOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112834-913-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_TransformedArimaAndArimaxForecastsMatchModelScaleOracle","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.TransformedArimaAndArimaxForecastsMatchModelScaleOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:28:34.9131654+00:00","wallSeconds":2.9300236,"testSeconds":0.0308568,"buildSeconds":1.42,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112834-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_TransformedArimaAndArimaxForecastsMatchModelScaleOracle\\haden_HADEN_2026-09-09_17_28_37.621.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112834-913-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_TransformedArimaAndArimaxForecastsMatchModelScaleOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112838-350-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_TransformLambdaMatchesIndependentTrainingOnlyOracle","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentOracleTests.TransformLambdaMatchesIndependentTrainingOnlyOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:28:38.3507318+00:00","wallSeconds":3.1375164,"testSeconds":0.0443042,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112838-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_TransformLambdaMatchesIndependentTrainingOnlyOracle\\haden_HADEN_2026-09-09_17_28_41.070.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112838-350-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentOracleTests_TransformLambdaMatchesIndependentTrainingOnlyOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112842-011-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentRecoveryTests_BayesianArima111LogD1RecoversGeneratingParameters","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentRecoveryTests.BayesianArima111LogD1RecoversGeneratingParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T17:28:42.0117244+00:00","wallSeconds":27.0495223,"testSeconds":24.0124586,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112842-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentRecoveryTests_BayesianArima111LogD1RecoversGeneratingParameters\\haden_HADEN_2026-09-09_17_29_08.805.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112842-011-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentRecoveryTests_BayesianArima111LogD1RecoversGeneratingParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112909-623-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentRecoveryTests_BayesianArimax10D1LevelCovariateRecoversGeneratingParameters","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentRecoveryTests.BayesianArimax10D1LevelCovariateRecoversGeneratingParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T17:29:09.6230710+00:00","wallSeconds":31.0297511,"testSeconds":27.9417253,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112909-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentRecoveryTests_BayesianArimax10D1LevelCovariateRecoversGeneratingParameters\\haden_HADEN_2026-09-09_17_29_40.422.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112909-623-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentRecoveryTests_BayesianArimax10D1LevelCovariateRecoversGeneratingParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112941-150-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentRecoveryTests_MleArima111LogD1RecoversGeneratingParameters","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentRecoveryTests.MleArima111LogD1RecoversGeneratingParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T17:29:41.1509919+00:00","wallSeconds":2.9875962,"testSeconds":0.1195368,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112941-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentRecoveryTests_MleArima111LogD1RecoversGeneratingParameters\\haden_HADEN_2026-09-09_17_29_43.906.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112941-150-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentRecoveryTests_MleArima111LogD1RecoversGeneratingParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112944-641-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentRecoveryTests_MleArimax10D1LevelCovariateRecoversGeneratingParameters","method":"RMC.BestFit.Verification.TimeSeriesAnalysis.TimeSeriesIndependentRecoveryTests.MleArimax10D1LevelCovariateRecoversGeneratingParameters","phase":"stabilized-pass","startedUtc":"2026-09-09T17:29:44.6417960+00:00","wallSeconds":3.2380703,"testSeconds":0.3560545,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112944-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentRecoveryTests_MleArimax10D1LevelCovariateRecoversGeneratingParameters\\haden_HADEN_2026-09-09_17_29_47.650.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112944-641-RMC_BestFit_Verification_TimeSeriesAnalysis_TimeSeriesIndependentRecoveryTests_MleArimax10D1LevelCovariateRecoversGeneratingParameters.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":[],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112948-381-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example1","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CExampleTests.Test_Example1","phase":"stabilized-pass","startedUtc":"2026-09-09T17:29:48.3815826+00:00","wallSeconds":3.0300185,"testSeconds":0.1345993,"buildSeconds":1.41,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112948-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example1\\haden_HADEN_2026-09-09_17_29_51.187.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112948-381-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example1.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112951-915-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example2","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CExampleTests.Test_Example2","phase":"stabilized-pass","startedUtc":"2026-09-09T17:29:51.9152953+00:00","wallSeconds":2.8363332,"testSeconds":0.123154,"buildSeconds":1.24,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112951-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example2\\haden_HADEN_2026-09-09_17_29_54.527.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112951-915-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example2.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112955-255-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example3","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CExampleTests.Test_Example3","phase":"stabilized-pass","startedUtc":"2026-09-09T17:29:55.2556143+00:00","wallSeconds":2.9573975,"testSeconds":0.0846662,"buildSeconds":1.42,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112955-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example3\\haden_HADEN_2026-09-09_17_29_57.996.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112955-255-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example3.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-112958-713-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example4","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CExampleTests.Test_Example4","phase":"stabilized-pass","startedUtc":"2026-09-09T17:29:58.7131238+00:00","wallSeconds":3.0600156,"testSeconds":0.201941,"buildSeconds":1.4,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-112958-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example4\\haden_HADEN_2026-09-09_17_30_01.559.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-112958-713-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example4.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-113002-276-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example5","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CExampleTests.Test_Example5","phase":"stabilized-pass","startedUtc":"2026-09-09T17:30:02.2762313+00:00","wallSeconds":3.2651303,"testSeconds":0.0727999,"buildSeconds":1.59,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-113002-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example5\\haden_HADEN_2026-09-09_17_30_05.309.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-113002-276-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example5.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-113006-137-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example6","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CExampleTests.Test_Example6","phase":"stabilized-pass","startedUtc":"2026-09-09T17:30:06.1378410+00:00","wallSeconds":2.9337626,"testSeconds":0.1675947,"buildSeconds":1.28,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-113006-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example6\\haden_HADEN_2026-09-09_17_30_08.852.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-113006-137-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example6.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-113009-620-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example7","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CExampleTests.Test_Example7","phase":"stabilized-pass","startedUtc":"2026-09-09T17:30:09.6207714+00:00","wallSeconds":3.944317,"testSeconds":0.3180569,"buildSeconds":1.75,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-113009-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example7\\haden_HADEN_2026-09-09_17_30_13.327.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-113009-620-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CExampleTests_Test_Example7.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogPearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-113014-112-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMu_N100","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogNormal_PenalityOnMu_N100","phase":"stabilized-pass","startedUtc":"2026-09-09T17:30:14.1125388+00:00","wallSeconds":2.9927183,"testSeconds":0.0625135,"buildSeconds":1.34,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-113014-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMu_N100\\haden_HADEN_2026-09-09_17_30_16.883.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-113014-112-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMu_N100.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-113017-717-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMu_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogNormal_PenalityOnMu_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T17:30:17.7175880+00:00","wallSeconds":3.2572037,"testSeconds":0.0552856,"buildSeconds":1.53,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-113017-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMu_N25\\haden_HADEN_2026-09-09_17_30_20.736.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-113017-717-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMu_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-113021-553-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMuAndSigma_N100","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogNormal_PenalityOnMuAndSigma_N100","phase":"stabilized-pass","startedUtc":"2026-09-09T17:30:21.5532652+00:00","wallSeconds":3.1812351,"testSeconds":0.0819888,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-113021-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMuAndSigma_N100\\haden_HADEN_2026-09-09_17_30_24.508.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-113021-553-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMuAndSigma_N100.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-113025-351-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMuAndSigma_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogNormal_PenalityOnMuAndSigma_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T17:30:25.3513089+00:00","wallSeconds":3.3078896,"testSeconds":0.0629762,"buildSeconds":1.63,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-113025-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMuAndSigma_N25\\haden_HADEN_2026-09-09_17_30_28.424.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-113025-351-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMuAndSigma_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-113029-277-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMuSigmaAndQ99_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogNormal_PenalityOnMuSigmaAndQ99_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T17:30:29.2772464+00:00","wallSeconds":3.7768814,"testSeconds":0.0590481,"buildSeconds":1.59,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-113029-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMuSigmaAndQ99_N25\\haden_HADEN_2026-09-09_17_30_32.749.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-113029-277-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnMuSigmaAndQ99_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-113034-094-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnQ99_N100","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogNormal_PenalityOnQ99_N100","phase":"stabilized-pass","startedUtc":"2026-09-09T17:30:34.0946595+00:00","wallSeconds":5.8329084,"testSeconds":0.0740613,"buildSeconds":2.87,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-113034-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnQ99_N100\\haden_HADEN_2026-09-09_17_30_39.381.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-113034-094-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnQ99_N100.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-113040-922-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnQ99_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogNormal_PenalityOnQ99_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T17:30:40.9223013+00:00","wallSeconds":3.6701552,"testSeconds":0.0664376,"buildSeconds":1.68,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-113041-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnQ99_N25\\haden_HADEN_2026-09-09_17_30_44.348.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-113040-922-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnQ99_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-113046-305-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnSigma_N100","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogNormal_PenalityOnSigma_N100","phase":"stabilized-pass","startedUtc":"2026-09-09T17:30:46.3057990+00:00","wallSeconds":6.2723771,"testSeconds":0.0636873,"buildSeconds":4.13,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-113046-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnSigma_N100\\haden_HADEN_2026-09-09_17_30_52.335.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-113046-305-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnSigma_N100.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-113053-341-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnSigma_N25","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CPenalityTests.LogNormal_PenalityOnSigma_N25","phase":"stabilized-pass","startedUtc":"2026-09-09T17:30:53.3419802+00:00","wallSeconds":3.2429152,"testSeconds":0.0559972,"buildSeconds":1.58,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-113053-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnSigma_N25\\haden_HADEN_2026-09-09_17_30_56.341.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-113053-341-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CPenalityTests_LogNormal_PenalityOnSigma_N25.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["LogNormal","LogPearsonTypeIII","Normal"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-113057-575-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_Exponential_GmmRecoversGeneratingParent","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CSyntheticDataTests.Exponential_GmmRecoversGeneratingParent","phase":"stabilized-pass","startedUtc":"2026-09-09T17:30:57.5760421+00:00","wallSeconds":8.3244264,"testSeconds":0.0973479,"buildSeconds":2.23,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-113100-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_Exponential_GmmRecoversGeneratingParent\\haden_HADEN_2026-09-09_17_31_05.671.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-113057-575-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_Exponential_GmmRecoversGeneratingParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-113106-923-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_Gamma_GmmRecoversGeneratingParent","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CSyntheticDataTests.Gamma_GmmRecoversGeneratingParent","phase":"stabilized-pass","startedUtc":"2026-09-09T17:31:06.9240276+00:00","wallSeconds":3.4631712,"testSeconds":0.0555888,"buildSeconds":1.48,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-113107-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_Gamma_GmmRecoversGeneratingParent\\haden_HADEN_2026-09-09_17_31_10.149.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-113106-923-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_Gamma_GmmRecoversGeneratingParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-113111-015-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_LogNormal_GmmRecoversGeneratingLog10Parent","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CSyntheticDataTests.LogNormal_GmmRecoversGeneratingLog10Parent","phase":"stabilized-pass","startedUtc":"2026-09-09T17:31:11.0158428+00:00","wallSeconds":3.345972,"testSeconds":0.0517881,"buildSeconds":1.68,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-113111-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_LogNormal_GmmRecoversGeneratingLog10Parent\\haden_HADEN_2026-09-09_17_31_14.143.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-113111-015-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_LogNormal_GmmRecoversGeneratingLog10Parent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-113114-981-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_Normal_GmmRecoversGeneratingParent","method":"RMC.BestFit.Verification.Univariate.Bulletin17CTests.B17CSyntheticDataTests.Normal_GmmRecoversGeneratingParent","phase":"stabilized-pass","startedUtc":"2026-09-09T17:31:14.9815090+00:00","wallSeconds":3.2969111,"testSeconds":0.0664069,"buildSeconds":1.54,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-113115-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_Normal_GmmRecoversGeneratingParent\\haden_HADEN_2026-09-09_17_31_18.003.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-113114-981-RMC_BestFit_Verification_Univariate_Bulletin17CTests_B17CSyntheticDataTests_Normal_GmmRecoversGeneratingParent.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Exponential","GammaDistribution","LogNormal","LogPearsonTypeIII","Normal","PearsonTypeIII"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-113119-120-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_CombinationRules_SatisfyTheoreticalBracketingAndRemainDistinct","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.CombinationRules_SatisfyTheoreticalBracketingAndRemainDistinct","phase":"stabilized-pass","startedUtc":"2026-09-09T17:31:19.1204049+00:00","wallSeconds":3.9025539,"testSeconds":0.3874553,"buildSeconds":1.81,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-113119-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_CombinationRules_SatisfyTheoreticalBracketingAndRemainDistinct\\haden_HADEN_2026-09-09_17_31_22.810.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-113119-120-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_CombinationRules_SatisfyTheoreticalBracketingAndRemainDistinct.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-113123-625-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_CorrelationMatrix_MinimumAndMaximumMatchBivariateNormalOrthants","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.CorrelationMatrix_MinimumAndMaximumMatchBivariateNormalOrthants","phase":"stabilized-pass","startedUtc":"2026-09-09T17:31:23.6251131+00:00","wallSeconds":3.2478722,"testSeconds":0.1470286,"buildSeconds":1.53,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-113123-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_CorrelationMatrix_MinimumAndMaximumMatchBivariateNormalOrthants\\haden_HADEN_2026-09-09_17_31_26.623.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-113123-625-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_CorrelationMatrix_MinimumAndMaximumMatchBivariateNormalOrthants.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-113127-486-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MaximumComposite_MatchesIndependentAndComonotonicClosedForms","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.MaximumComposite_MatchesIndependentAndComonotonicClosedForms","phase":"stabilized-pass","startedUtc":"2026-09-09T17:31:27.4866709+00:00","wallSeconds":3.4642157,"testSeconds":0.2743555,"buildSeconds":1.61,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-113127-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MaximumComposite_MatchesIndependentAndComonotonicClosedForms\\haden_HADEN_2026-09-09_17_31_30.718.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-113127-486-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MaximumComposite_MatchesIndependentAndComonotonicClosedForms.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-113132-191-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MaximumPosterior_MatchesCompleteCartesianOracle","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.MaximumPosterior_MatchesCompleteCartesianOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:31:32.1920470+00:00","wallSeconds":7.3270253,"testSeconds":3.972806,"buildSeconds":1.63,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-113132-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MaximumPosterior_MatchesCompleteCartesianOracle\\haden_HADEN_2026-09-09_17_31_39.252.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-113132-191-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MaximumPosterior_MatchesCompleteCartesianOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-113140-158-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MinimumComposite_MatchesIndependentAndComonotonicClosedForms","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.MinimumComposite_MatchesIndependentAndComonotonicClosedForms","phase":"stabilized-pass","startedUtc":"2026-09-09T17:31:40.1588665+00:00","wallSeconds":3.4033292,"testSeconds":0.275493,"buildSeconds":1.51,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-113140-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MinimumComposite_MatchesIndependentAndComonotonicClosedForms\\haden_HADEN_2026-09-09_17_31_43.314.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-113140-158-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MinimumComposite_MatchesIndependentAndComonotonicClosedForms.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-113144-209-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MinimumPosterior_MatchesCompleteCartesianOracle","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.MinimumPosterior_MatchesCompleteCartesianOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:31:44.2095939+00:00","wallSeconds":8.5989174,"testSeconds":4.2309781,"buildSeconds":2.25,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-113144-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MinimumPosterior_MatchesCompleteCartesianOracle\\haden_HADEN_2026-09-09_17_31_52.091.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-113144-209-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MinimumPosterior_MatchesCompleteCartesianOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-113153-413-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MixtureCdf_MatchesExactWeightedNormalSum","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.MixtureCdf_MatchesExactWeightedNormalSum","phase":"stabilized-pass","startedUtc":"2026-09-09T17:31:53.4133350+00:00","wallSeconds":3.3772388,"testSeconds":0.1734043,"buildSeconds":1.63,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-113153-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MixtureCdf_MatchesExactWeightedNormalSum\\haden_HADEN_2026-09-09_17_31_56.573.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-113153-413-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MixtureCdf_MatchesExactWeightedNormalSum.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-113157-395-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MixturePosterior_MatchesCompleteCartesianOracle","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.MixturePosterior_MatchesCompleteCartesianOracle","phase":"stabilized-pass","startedUtc":"2026-09-09T17:31:57.3953041+00:00","wallSeconds":7.2296499,"testSeconds":4.0434844,"buildSeconds":1.49,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-113157-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MixturePosterior_MatchesCompleteCartesianOracle\\haden_HADEN_2026-09-09_17_32_04.296.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-113157-395-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MixturePosterior_MatchesCompleteCartesianOracle.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-113205-322-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MixtureQuantiles_InvertAnalyticWeightedNormalCdf","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.MixtureQuantiles_InvertAnalyticWeightedNormalCdf","phase":"stabilized-pass","startedUtc":"2026-09-09T17:32:05.3227818+00:00","wallSeconds":3.4774862,"testSeconds":0.1896794,"buildSeconds":1.68,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-113205-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MixtureQuantiles_InvertAnalyticWeightedNormalCdf\\haden_HADEN_2026-09-09_17_32_08.583.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-113205-322-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MixtureQuantiles_InvertAnalyticWeightedNormalCdf.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-113209-448-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MixtureQuantiles_MatchPublishedRMistrTable45","method":"RMC.BestFit.Verification.Univariate.CompositeTests.CompositeOracleVerificationTests.MixtureQuantiles_MatchPublishedRMistrTable45","phase":"stabilized-pass","startedUtc":"2026-09-09T17:32:09.4488734+00:00","wallSeconds":3.2701206,"testSeconds":0.1754018,"buildSeconds":1.5,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-113209-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MixtureQuantiles_MatchPublishedRMistrTable45\\haden_HADEN_2026-09-09_17_32_12.493.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-113209-448-RMC_BestFit_Verification_Univariate_CompositeTests_CompositeOracleVerificationTests_MixtureQuantiles_MatchPublishedRMistrTable45.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture","CompetingRisks"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} +{"runId":"20260909-113213-327-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureExternalPackageOracleTests_OrdinaryNormalMixture2D_MatchesScikitLearnArtifact","method":"RMC.BestFit.Verification.Univariate.MixtureTests.MixtureExternalPackageOracleTests.OrdinaryNormalMixture2D_MatchesScikitLearnArtifact","phase":"stabilized-pass","startedUtc":"2026-09-09T17:32:13.3280368+00:00","wallSeconds":3.3089128,"testSeconds":0.3162463,"buildSeconds":1.47,"result":"Passed","trx":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair\\TestResults\\VerificationFocused\\20260909-113213-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureExternalPackageOracleTests_OrdinaryNormalMixture2D_MatchesScikitLearnArtifact\\haden_HADEN_2026-09-09_17_32_16.422.trx","log":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair\\artifacts\\regression-repair\\logs\\20260909-113213-327-RMC_BestFit_Verification_Univariate_MixtureTests_MixtureExternalPackageOracleTests_OrdinaryNormalMixture2D_MatchesScikitLearnArtifact.log","exactIdentityVerified":true,"numerics":{"root":"C:\\GIT\\numerics\\artifacts\\worktrees\\bestfit-regression-repair","commit":"a3bd2afd64286e1b8df3ada8be711d55180cbb11","sourceSha256":"F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5"},"bestFit":{"root":"C:\\GIT\\RMC-BestFit\\artifacts\\worktrees\\bestfit-regression-repair","commit":"3e0371a995b2c9d7993f8b4ddd42b48238b9bb7a","sourceSha256":"593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC"},"numericsAssemblySha256":"41D267BA6C783FAA08748D73E99669C15910CEDD39F9A272977AE03B819E8811","bestFitAssemblySha256":"E12C3D96A3DB4E9D202F354E3ABE410D981E1A369B76598BECA832A447F64E37","verificationAssemblySha256":"2EA547F9660A932E74969ECFF6D7C02A71ACCC9C61C920B490047BB59276A0F0","dependencies":["Normal","Mixture"],"sourceChangedDuringRun":false,"failure":null,"note":"Resumable approved allowlist; no scientific settings altered."} diff --git a/docs/distributions/bestfit-regression-status.md b/docs/distributions/bestfit-regression-status.md new file mode 100644 index 00000000..ed851858 --- /dev/null +++ b/docs/distributions/bestfit-regression-status.md @@ -0,0 +1,138 @@ +# BestFit distribution regression ledger + +Execution began 2026-09-08. See `bestfit-regression-inventory.json` for the frozen 293 exact identities, their dependencies, 35 unrelated exclusions, and 56 locally absent coverage methods. Every executed method's independent TRX identity check, timings, source versions, and artifact path is in `bestfit-regression-runs.jsonl`. + +## Final verification evidence, 2026-09-09 + +All 293 approved methods pass against the final source pair. Each ran serially through the existing guarded runner; an independent completion audit reopened every final TRX and confirmed exactly one result, one definition, the requested fully qualified identity, a passing outcome, matching duration, and the recorded source/assembly fingerprints. No numerical failure remains in this allowlist. All material runtime flags were investigated with equivalent pinned-baseline/current runs; their dispositions and the remaining observed timing variability are recorded below. + +| Area | Final passes | +|---|---:| +| Distribution fitting | 50 | +| Estimation and diagnostics | 40 | +| Univariate | 117 | +| Bivariate | 25 | +| Rating curve | 16 | +| Time series | 22 | +| Spatial | 23 | +| Total | 293 | + +Final production and verification source fingerprints: + +- Numerics: `F74AD06F9E6D7DECF0E03A22B017EA8D1CF4B5221B60582BA2F89CCE134113D5`. +- BestFit: `593621D78DD278F8213247D365DEC659937D19821759F13E0BCEDE57436954CC`. + +The final methods total 3512.867958 seconds of test execution, excluding builds and host startup. The local independent audit is `artifacts/regression-repair/completion-evidence.json`; the tracked JSONL ledger retains the exact method identities, TRX paths, durations, dependencies, source versions, assembly hashes, earlier failures and superseded passes. The frozen inventory still partitions all 384 catalog identities into 293 approved, 35 unrelated and 56 originally absent methods. All 15 original dirty-file states match their initial fingerprints. + +## Final framework, fast-test and documentation gates + +All required gates passed without source changes during execution. The independent gate audit reopened all eight framework/project TRXs, checked every result was `Passed`, reconciled the result counts, and checked the before/after source fingerprints, including tests and oracle artifacts. Together these gates contain 16,392 passing test executions, with zero failed or skipped results. + +| Gate | Passed | +|---|---:| +| Numerics Release net481 | 2,835 | +| Numerics Release net8.0 | 2,850 | +| Numerics Release net9.0 | 2,850 | +| Numerics Release net10.0 | 2,850 | +| Numerics total | 11,385 | +| BestFit core fast tests | 3,420 | +| BestFit UI fast tests | 645 | +| BestFit App fast tests | 444 | +| BestFit API fast tests | 498 | +| BestFit fast total | 5,007 | + +- Numerics: `dotnet build -c Release --nologo -p:EnforceXmlDocumentation=true -warnaserror` and `dotnet test -c Release --no-build --logger trx`, with `VSTEST_CONNECTION_TIMEOUT=600`. Strict build: zero warnings/errors across all four frameworks. Existing framework and test parallelization settings are unchanged. +- BestFit: nine explicit strict Debug project builds with `EnforceXmlDocumentation=true` and `-warnaserror`, followed by the four fast projects with `--no-build -- --report-trx`. All builds reported zero warnings/errors. Local Numerics and HEC-DSS dependency paths were explicit. Verification was only built in this gate; its execution remained restricted to the separate 293-method runner. +- Private/public XML scan: 947 files, passed. Verification catalog: 384 declarations/execution units, zero gaps. Catalog validator fixtures: 22/22 passed. +- Numerics gate directory: `C:/GIT/numerics/artifacts/worktrees/bestfit-regression-repair/artifacts/regression-repair/gates/20260909-113247-Numerics`. +- BestFit gate directory: `C:/GIT/numerics/artifacts/worktrees/bestfit-regression-repair/artifacts/regression-repair/gates/20260909-115844-BestFit`. +- Exact gate TRX paths/hashes and counts: `artifacts/regression-repair/final-gate-evidence.json`. The three Debug assemblies rebuilt by the strict gate match every one of the 293 recorded Numerics/BestFit/Verification assembly hashes. + +The catalog fixture script deliberately launches expected failing cases and leaves their native exit code in the calling PowerShell session. The temporary gate wrapper initially misread that residual value despite all 22 fixture assertions passing. Running the unchanged fixture script in its own PowerShell process returned success; the completed gate retains both outputs and unchanged before/after source fingerprints. No production, test, or catalog-validator setting was changed for that bookkeeping correction. + +The final reviewed scope contains 40 Numerics paths and 12 BestFit paths, including the ledger, focused regression fixtures and the two native DSS test scheduling corrections described below. Numerical literals in the two edited Verification files are unchanged in order (108 and 65 respectively); shared acceptance code, scientific datasets/oracles and global test settings have no diff. The staged whitespace check removed only an extra terminal blank line from the new point-process fast fixture after the gates; its non-terminating-newline bytes are identical, documented in `artifacts/regression-repair/final-whitespace-audit.json`. Neither Verification source fingerprint changed. Local commits remain on the isolated repair branches; original dirty checkouts are preserved and nothing is pushed. + +## Repair scope + +- Restore usable historical constraint tuples, initialization arithmetic and family-specific magnitude rounding in the 14 changed families; preserve the unchanged Kappa behavior. Exceptional-input hardening applies only when the legacy tuple is unusable. Baseline fixtures and actual BestFit Uniform priors/nonstationary bounds protect the readable envelopes, including legacy zero/nonpositive initialization. +- Transform complete Pearson, Log-Pearson III and LnNormal covariance matrices into the verification helpers' existing coordinates, including every cross term. Existing recovery rules, parent coordinates and scientific acceptance settings are unchanged. +- Preserve editable point-process component parameters when automatic constraints are unavailable for a short sample; retain the automatic-prior diagnostic and existing user-edited objects. +- Restore the established rejection of finite interior dependent competing-risk density candidates at or below `1E-300`, while retaining the hardened support-bounded derivative stencil and boundary exceptions. +- Eliminate repeated constant calculations, wrapper/array allocations, duplicate bulk validation and unchanged configuration work. Cache keys and invalidation preserve live parameter, component, weight and correlation changes; generic and derived callback paths retain their existing behavior. +- Update the private SourceLink build tool to `10.0.111` for the observed `NU1902` advisory, without suppressing warnings. +- Serialize only the native DSS UI/App integration fixtures after the final fast gate exposed a process-global native initialization race. Keep all fixture bodies, data rows, assertions and global runner settings unchanged. + +No Verification tolerance, threshold, confidence level, reference, seed, sample size, sampler setting, optimizer setting or scientific policy was changed. One newly introduced Numerics hardening assertion requiring rejection of a previously usable zero-containing Gamma/Weibull sample was corrected to require the existing usable-constraint contract on that identical sample. No established Verification test was removed or skipped. + +## Checkouts and evidence workflow + +Repair checkouts: + +- Numerics: `C:/GIT/numerics/artifacts/worktrees/bestfit-regression-repair`, branch `codex/bestfit-distribution-regressions`. +- BestFit: `C:/GIT/RMC-BestFit/artifacts/worktrees/bestfit-regression-repair`, branch `codex/distribution-regression-repair`. + +Detached comparison checkouts use the corresponding `bestfit-regression-baseline` paths. Never change or clean the original dirty checkouts. Their initial dirty file fingerprints are retained in `artifacts/regression-repair/original-dirty-fingerprints.json`. + +Run one method with `scripts/run-bestfit-regression-method.ps1 -Test -BestFitRoot -NumericsRoot `. This invokes the existing guarded BestFit runner and stops on failure, a mismatched identity, or source changes during execution. Record the diagnosis here before continuing to independent methods. A passing record is current only when its production/verification source fingerprints match the stabilized source; older affected passes are stale. + +## Diagnostic cases and intermediate checkpoints + +The following table preserves the observations made during the repair loop. Entries described as provisional, pending or requiring refresh refer to those intermediate source versions. The final 293-method pass above supersedes those statuses; earlier failing, interrupted and infrastructure-only records remain in the ledger for provenance. + +| Case | Evidence | Cause / repair | State | +|---|---|---|---| +| Pearson fitting covariance recovery | Baseline passes (2.117 s); hardened Numerics fails with either baseline or current BestFit: Alpha standardized error 21.6886 | Covariance changed to public (Mu,Sigma,Gamma), helper still interpreted (Mu,1/Beta,Alpha). Approved full J C J-transpose transformation preserves cross terms and existing assertions. | Repaired exact method passed (1.800 s); provisional until final source freeze | +| Log-Pearson fitting covariance recovery | Baseline passes (2.298 s); hardened Numerics fails with either BestFit version: OneOverBeta standardized error 435.7134 | Same covariance coordinate contract mismatch; full transformation implemented. | Repaired exact method passed (1.590 s); provisional | +| LnNormal fitting covariance recovery | Earlier pass used a mismatched covariance despite passing acceptance | Transform physical Mean/SD covariance into existing log Mu/Sigma-squared test coordinates. | Repaired exact method passed (1.971 s); provisional | +| MLE integration covariance recovery | Log-Pearson baseline passes0.926 s; current fails1.017 s with standardized rate error437.9408; baseline BestFit with repaired Numerics reproduces identical estimate/error in0.989 s | A second helper in MLEIntegrationTests still interpreted public covariance in the old coordinates. Extend the approved complete transformation to its LnNormal, Pearson and Log-Pearson checks, retaining all parent coordinates and recovery rules | Independent review approved; exact LP3/Pearson/LnNormal methods pass0.962/0.841/0.129 s, dense covariance contracts2/2 pass. Other direct consumers audited with no further mismatch found; refresh continues | +| Default prior constraints | Initial 75 fixtures captured from compiled d80bfa8 across 15 families; 33 failed current code, 42 passed | Restore old initialization and family-specific magnitude rounding for valid old cases, keeping exceptional-input hardening separate. Logistic [10,12,14,16] scale upper 100 had become 55.13288954217921; near-unit LogNormal/LP3 scale upper 2 had become .01. | Final 87 new checks +145 hardening checks pass; 356 existing family checks pass; independent review approved; six real BestFit prior/trend checks pass | +| ArrFlike Example3 | Baseline passes (4.406 s); current fails (5.566 s), upper interval 523115.4583 vs reference570635 at unchanged7.5% tolerance; B0/N1 reproduces identical failure | Legacy constraint restoration corrects the credible interval with unchanged likelihood integration and sampler settings. | All five ArrFlike examples pass. After the first performance repair, uncontended current repeats 5.090/4.438 s match paired baseline repeats 5.166/4.456 s | +| Exponential / GPA default-fitting runtime | Repaired 20.327/23.095 s prompted immediate baseline checks | Equivalent baseline takes31.521/33.458 s respectively | No slowdown in these observations; both methods pass | +| LP3/Gamma redundant work | Prepared diagnostic processes ran serially while Verification was paused; raw results and assembly hashes in artifacts/regression-repair/performance | LP3 gradient solved the same quantile twice; ZetaInteger repeatedly allocated an identical constant table (skew4 CDF11720 bytes/call). | First repair reviewed and validated: high-skew CDF allocation 11720 to56 bytes/call, LP3 gradient allocation160 to104 after table repair, numerical diagnostics bitwise identical. Existing oracle checks pass | +| Repeated high-order zeta sums | After the first performance repair, skew4 CDF still took122.64 ms for10000 calls; the small-shape series recomputed identical sums of Math.Pow(k,-n) for every call | Precompute integer n17..59 once with the exact existing summation expression and order, retaining the larger-n fallback and every numerical series/convergence rule. GEV also consumes these constants | Production review clean; CDF workload10.45 ms, inverse102.91 to8.80 ms, gradient121.83 to8.22 ms, Jacobian93.35 to8.75 ms. All70 diagnostic records and165 checksums unchanged. Current hardened kernels retain some scalar-call overhead versus old algorithms; assess material impact through equivalent Verification comparisons | +| Mixture evaluation allocations | Repeated live validation flattened all component parameters before reconstructing arrays for each component; fixed ordinary two-component LogPDF allocated600 bytes/call | Validate the live structure, weights, and each component directly on every evaluation. Public candidate validation and detection of public/nested mutation remain. | Four new checks and five existing contracts pass; independent review clean. Allocation37600 versus231200 bytes over300 fixed calls; all27 diagnostic checks and63 checksums unchanged. Exact Bayesian runtime checks remain | +| GEV/GPA nonstationary runtime | Exact pinned baseline GEV88.511 s / GPA132.880 s versus repaired-before-setter105.190 /149.589 s, all numerically passing | Each bulk assignment validated the requested triple, then repeated validation and allocated lists through Xi/Kappa property setters. Four backing-field assignment substitutions retain the same final validation and values, removing160 bytes/call. | Ten new cases plus144 existing checks pass; independent review clean. Fresh GEV73.236 s / GPA89.232 s pass, clearing both measured end-to-end slowdowns. Diagnostic fixtures, numerical checks, and all70 checksums match the pre-setter build exactly; short timing measurements show host variability | +| LP3 nonstationary runtime | Current exact recovery passes in 197.255 s; pinned fbe0989/d80bfa8 passes in 155.736 and 148.267 s | Remove the per-observation Pearson wrapper through a shared unchanged density body, then reuse the same call-local logarithm base and transformed support bound. | Final exact recovery passes in 126.867 s, clearing the measured slowdown. Allocation falls from 56 bytes/call to zero; 147 existing checks pass and the independent pre/post binary comparison matches all 16,140 records after both refinements. Interrupted attempts without TRX are explicitly incomplete, then rerun | +| Normal-mixture Bayesian runtime | Current two-component exact Bayesian recovery initially passes in 164.972 s; the pinned unchanged method passes in 119.230 s | Delegate sealed Normal live validation to its existing scalar validator, use Array.Exists for the same structural predicate, and cache unchanged Normal and weight logarithms. Weight cache keys check the actual live weight after each component callback. Retain every per-call, inactive-component, candidate and nested validation contract. | Exact final recovery passes in 108.425 s, versus adjacent pinned baseline 105.939 s, clearing the material slowdown. Focused checks pass 119 net10 / 113 net481; all 27 diagnostic records and 63 checksums match. Allocation remains zero per warmed density call. The B0/Ncurrent diagnostic cannot reach sampling because the old integration initializes an empty sample; current BestFit already guards that exceptional case | +| Zero-inflated Normal-mixture runtime | Pinned exact Bayesian recovery passes in 224.583 s; each density evaluation repeatedly recomputes the same component positive mass | Cache the exact existing Normal.LogCCDF(0) value, invalidate on both parameter setters, and retain validation before use. Public tails and non-Normal component behavior remain unchanged. | Exact current recovery passes in 139.928 s. All six mixture recovery methods pass at this checkpoint, including ordinary three-component Bayesian recovery in 274.793 s. Focused checks pass 109 net10 / 106 net481 with zero warnings; all 27 binary diagnostic records and 63 checksums match | +| Required framework/build gates | Strict Release build and all four Numerics framework suites completed at the checkpoint before the zero-survival cache | Patch the private SourceLink build tool to 10.0.111 for the observed NU1902 advisory; correct only the new Weibull fixture's net481 literal using identical pinned/current CLR4 captures and retain exact equality | Checkpoint: 11,218 test executions pass, zero failed/skipped, zero build warnings/errors. Final gates must refresh after the remaining production repair; checkpoint results are not final-source completion evidence | +| Correlated competing-risk MLE | Exact current minimum two-Weibull recovery fails in 0.481 s; pinned fbe0989/d80bfa8 passes in 26.251 s. Actual input capture confirms identical lower/upper bounds; 413/1000 generated values differ by at most 2.84E-14 and initial coordinates by at most 2.13E-14 | At candidate [54.676968161948025,43.16940511169378,55.23342531523667,47.44412398606073] and observation 58.99784456776834, the dependent CDF decreases by one ULP across the new stencil. The new derivative -6.370079603857102E-12 throws and aborts the optimizer. The old derivative is also negative (-8.780908884003441E-11), but the established LogPDF policy rejects that candidate with negative infinity and continues | The first rollback restored too much: the old derivative stencil amplified CDF roundoff and the exact method failed observed-information positive definiteness after 95.172 s. Fixed-parameter comparisons isolate that stencil interaction. The final narrow repair retains the hardened IQR/support-bounded stencil and restores only finite interior candidate rejection. Exact recovery now passes all unchanged checks in 30.279 s; focused checks pass 30/30. Adjacent pinned/current repeats pass in 24.504/27.906 s, so the following performance repairs resolve that measured difference. Identical step reuse passed48net10/47net481 checks but its small timing gain was inconclusive. Internal collection-wrapper removal and reuse of already-read fixed Weibull support bounds preserve all20 numerical values and18 checksums; density allocation is now928 bytes/call (previously1928). Generic components and derived owners retain live support reads, confirmed by the added derived-owner guard. The final pure CDF-body reuse removes duplicate inner validation/configuration/support work only for the already validated exact-owner/exact-Weibull case. Cold, mismatched, generic and derived paths retain both virtual CDF calls. Final exact recovery passes22.648 s versus pinned26.251/24.504/23.030 s, clearing the observed runtime regression. Current optimizer work remains7040 evaluations/175 iterations versus baseline6960/173; no optimization settings changed. Final focused59net10/57net481 checks pass, including derived-owner call/read counts; all20 numerical values and18 diagnostic checksums remain bit-exact. Density allocation is800 bytes/call. Four newly introduced provisional rows asserting the abandoned stencil were removed, while the captured-candidate and endpoint regressions remain; no pre-existing test was removed or changed. The per-CDF XML configuration rebuild is also eliminated for exact built-in Weibull components through a full live-state snapshot; every canonical fallback and mutation check remains. Before/after values are bit-exact, with CDF diagnostic time 9.41 to 2.12 ms per 1000 calls and configuration allocations 3083200 to 11200 bytes per 100 calls. B1/N0 cannot compile against the old IStandardError API and is explicitly infrastructure evidence, not an executed test. All diagnostic inputs, exceptions and source comparisons remain in artifacts/regression-repair/correlated-competing-probe and correlated-curvature-probe | +| Positive-correlation coincident frequency | Current40.509s versus historical34.538s prompted an exact pinned comparison | Pinned fbe0989/d80bfa8 passes40.185s and adjacent current repeat passes39.039s; no source repair needed | Numerical acceptance passes; no reproducible runtime regression | + +## Later diagnoses and final timing dispositions + +### Native DSS UI/App fast-gate initialization + +The first two final BestFit UI fast-gate attempts aborted inside external `hecdss.DLL` before emitting a TRX, reporting Fortran `JWRITE` subscript 7 beyond upper bound 6 during `zinit6`/`zset6`. The second attempt ran without any other gate. Both used the existing 22-worker method-level scheduling. Running only `DssTimeSeriesReaderTests` reproduced the same native abort; running its unchanged `Read_PathFormsAndCase_PreserveCompleteSeriesIdentity` method alone passed in 175 ms. These are host-abort/infrastructure records, not numerical assertion failures or passing gate results. + +Each native reader/writer constructor calls `DssReader.OpenDssFile` and the unlocked process-global `DssGlobals.SetMessageLevel` before opening its unique file. After adding class-level `DoNotParallelize` and an XML explanation, all 645 UI fast tests passed. The App host then exposed the identical native startup abort in `DssPathSelectorRowTests`, whose two native file tests use the same constructors. The same class-level correction was applied there. A scan of all four fast projects found native reader/writer construction only in these two fixtures. + +Method bodies, data, assertions, native/managed dependencies, global parallelization, and all scientific settings are unchanged. The original Fortran source was unavailable locally; the identical isolated-fixture failure and single-method pass, together with the wrapper's shared initialization path, support this focused scheduling correction. The saved red and single-method evidence is under `artifacts/regression-repair/native-dss-gate-diagnosis`; the earlier full gate logs are under `gates/20260909-114759-BestFit`, `gates/20260909-115152-BestFit`, and `gates/20260909-115525-BestFit`. These UI/App test-only changes do not alter either final Verification source fingerprint or any of its three loaded assemblies. After both corrections, the complete final BestFit gate passed, including all 645 UI and 444 App tests with no failures or skips. + +### Zero-correlation coincident-frequency timing comparison + +The zero-correlation Normal sum check passed in 48.0829368 s versus an unverified historical 34.4817613 s hint. The adjacent pinned baseline passed in 41.1593144 s and current repeat passed in 39.5699971 s. The slowdown did not reproduce, and no source change was needed. Exact evidence is recorded under `coincident-zero-runtime-baseline` and `coincident-zero-runtime-current`. + +### Reciprocal-scale timing comparison + +Normal reciprocal-scale recovery passed in 33.1849502 s versus an unverified historical 27.8477332 s hint. The adjacent pinned baseline passed in 33.2960248 s and current repeat passed in 29.9456961 s. This timing flag did not reproduce; no code repair was needed. The exact TRXs are in the ledger under `reciprocal-sigma-runtime-baseline` and `reciprocal-sigma-runtime-current`. + +### Final-source Normal-mixture timing repeat + +The refreshed ordinary two-component Bayesian recovery passed in 139.3480757 s, triggering comparison with the older 105.9387676 s baseline measurement. With the allowlist paused and sources unchanged, the adjacent exact pinned baseline passed in 131.6479174 s and current repeat passed in 137.0565094 s. The adjacent difference is 5.409 s (4.1%), while the baseline itself varied by 24.3% from its earlier observation. The earlier material gap did not reproduce in the adjacent comparison; retain this observed small difference and the host/runtime variability rather than attributing the old-baseline gap to a new production defect. No further source repair was made. TRXs and timings are recorded under `mixture2-final-runtime-baseline` and `mixture2-final-runtime-current`. + +### Point-process explicit parameter initialization + +`PointProcessExternalPackageOracleTests.StationaryPoissonGpa_MatchesSciPyArtifact` exposed a second initialization integration regression. The unchanged three-observation fixture supplies all GEV parameters explicitly, but the new automatic-initialization guard returned before constructing its three editable slots. Current fails with a parameter-list length mismatch; the pinned baseline passes in 0.0452986 s. Baseline BestFit with current Numerics exposes the underlying insufficient-observation exception directly. The baseline's moment-derived bounds for such a short sample were unusable, so this case does not call for fabricating a new Numerics initializer. + +BestFit now creates the missing component parameter slots from existing component values and ModelParameter defaults only when the parameter list is empty after an expected initialization failure. Existing seasonal changepoint defaults and property handlers remain; existing parameter objects and their states are preserved on later failed refreshes. The automatic-initialization diagnostic continues to prevent treating these editable placeholders as derived default priors. Two focused stationary/seasonal regression rows fail before the repair and pass afterward, along with 132 existing point-process/integration checks. No Verification assertion or numerical policy changed. Exact final results and TRXs are recorded in the JSONL ledger; fast evidence is in `artifacts/regression-repair/point-process-initialization`. + +### Zero-flow constraint restoration found during the full refresh + +After 182 passing methods, `B17CExampleTests.Test_Example2` failed with fitted Mu 0.0017102440398818303 versus its unchanged reference 3.02266304070359. The pinned baseline passes in 0.1319459 s; baseline BestFit with current Numerics reproduces the identical failure. The captured 82-observation sample includes zero flows. Numerics' new positivity guard throws before the legacy constraint initialization, leaving BestFit at default zero initial values and +/-double.MaxValue bounds. The baseline instead produces ROS initials [3.1465030824358715,0.4917818001757935,-0.10600610715497381] within [-100,epsilon,-6] and [100,3,6]. + +The same guard rejected previously usable nonpositive samples in Gamma, LnNormal, LogNormal, Log-Pearson III, and Weibull. Their constraint entry points now preserve finite, nonconstant legacy samples and invoke the existing exceptional-input fallback only when the old tuple is unusable. LogNormal retains its exact historical 0.1 substitution and Log-Pearson III its 0.01 substitution inside constraint initialization; input observations and density support are unchanged. Ten literal d80bfa8 fixture rows failed before repair and pass afterward. The newly introduced hardening assertion requiring Gamma/Weibull to reject [0,1,2,3] was corrected to require usable constraints on that identical sample, restoring the prior contract. No Verification assertion, tolerance, reference, or estimator setting changed. + +The unchanged Example2 passed immediately after repair in 0.1346806 s and in the final refresh in 0.123154 s. All 190 focused constraint/hardening checks pass on both net10.0 and net481 with zero warnings/errors. The actual repaired sample, constraint tuple, model bounds, and ten family fixtures match the baseline capture exactly. ROS initial values differ by at most 3.04E-14 after the hardened probability evaluations; all remain inside the identical bounds. Evidence is in `artifacts/regression-repair/b17c-example2-probe`; the exact comparison TRXs are recorded in the JSONL ledger. This production change made the previous 182 passes stale; all earlier methods were subsequently refreshed against the final source. + +The complete serial allowlist refresh finished on 2026-09-09. The independent TRX audit confirms every approved method has final-source passing evidence. The completed runtime comparisons leave no unresolved material slowdown attributable to these changes within the approved inventory. Timing observations characterize this host and workload; they do not assert identical elapsed time on every run. diff --git a/scripts/bestfit-regression-evidence.ps1 b/scripts/bestfit-regression-evidence.ps1 new file mode 100644 index 00000000..026aeefe --- /dev/null +++ b/scripts/bestfit-regression-evidence.ps1 @@ -0,0 +1,17 @@ +# Shared source fingerprinting for the exact-method runner and resumable allowlist. +function Get-BestFitRegressionSourceVersion([string]$Root, [string[]]$SourceDirectory) { + $head = & git -C $Root rev-parse HEAD + if ($LASTEXITCODE -ne 0) { throw "Cannot resolve source version: $Root" } + $files = @(& git -C $Root ls-files --cached --others --exclude-standard -- $SourceDirectory 'Directory.Build.*' 'Directory.Packages.props' 'global.json' 'NuGet.Config') | Sort-Object -Unique + if ($LASTEXITCODE -ne 0) { throw "Cannot resolve source files: $Root" } + $parts = foreach ($relative in $files) { + $absolute = Join-Path $Root $relative + if (Test-Path -LiteralPath $absolute -PathType Leaf) { + $relative + ':' + (Get-FileHash -LiteralPath $absolute -Algorithm SHA256).Hash + } else { $relative + ':absent' } + } + $bytes = [System.Text.Encoding]::UTF8.GetBytes(($parts -join "`n")) + $sha = [System.Security.Cryptography.SHA256]::Create() + try { $fingerprint = [Convert]::ToHexString($sha.ComputeHash($bytes)) } finally { $sha.Dispose() } + return [ordered]@{ root=$Root; commit=[string]$head; sourceSha256=$fingerprint } +} diff --git a/scripts/run-bestfit-regression-allowlist.ps1 b/scripts/run-bestfit-regression-allowlist.ps1 new file mode 100644 index 00000000..ff170cee --- /dev/null +++ b/scripts/run-bestfit-regression-allowlist.ps1 @@ -0,0 +1,82 @@ +<# +.SYNOPSIS +Resumes the approved BestFit allowlist one exact method at a time, stopping for any failure. +.DESCRIPTION +Only the latest exact, passing, unchanged-source evidence for the requested checkout pair is +reused. Failed, absent, or stale methods run through the guarded single-method runner. The +inventory retains excluded and locally absent methods without expanding execution to them. +#> +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)][string]$BestFitRoot, + [Parameter(Mandatory = $true)][string]$NumericsRoot, + [string[]]$Methods, + [switch]$ForceRerun +) +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version Latest +. (Join-Path $PSScriptRoot 'bestfit-regression-evidence.ps1') +$evidenceRoot = Split-Path $PSScriptRoot -Parent +$inventory = Get-Content -LiteralPath (Join-Path $evidenceRoot 'docs\distributions\bestfit-regression-inventory.json') -Raw | ConvertFrom-Json +if ($inventory.methods.Count -ne 293) { throw 'The approved inventory must contain exactly 293 methods.' } +$BestFitRoot = (Resolve-Path -LiteralPath $BestFitRoot).Path +$NumericsRoot = (Resolve-Path -LiteralPath $NumericsRoot).Path +$numericsVersion = Get-BestFitRegressionSourceVersion $NumericsRoot 'Numerics' +$bestFitVersion = Get-BestFitRegressionSourceVersion $BestFitRoot @('src/RMC.BestFit','src/RMC.BestFit.Verification','src/TestCommon','verification/data') +$ledgerPath = Join-Path $evidenceRoot 'docs\distributions\bestfit-regression-runs.jsonl' +$latest = @{} +if (Test-Path -LiteralPath $ledgerPath) { + foreach ($line in Get-Content -LiteralPath $ledgerPath) { + $record = $line | ConvertFrom-Json + if ($null -eq $record.numerics.PSObject.Properties['sourceSha256'] -or $null -eq $record.bestFit.PSObject.Properties['sourceSha256']) { continue } + if ($record.numerics.root -eq $NumericsRoot -and $record.bestFit.root -eq $BestFitRoot -and + $record.numerics.sourceSha256 -eq $numericsVersion.sourceSha256 -and $record.bestFit.sourceSha256 -eq $bestFitVersion.sourceSha256) { + $latest[$record.method] = $record + } + } +} +if ($Methods) { + if (@($Methods | Sort-Object -Unique).Count -ne $Methods.Count) { throw 'Duplicate requested methods are not allowed.' } + foreach ($method in $Methods) { if ($method -notin $inventory.methods.method) { throw "Method is outside the approved inventory: $method" } } + $ordered = @($Methods) +} else { + $ordered = @($inventory.methods | Sort-Object @{Expression={ + if ($_.method -match 'FittingAnalysisRecoveryTests\.(PearsonTypeIII|LogPearsonTypeIII|LnNormal)_') { 0 } + elseif ($_.method -match '\.ArrFlikeTests\.') { 1 } + elseif ($_.method -match 'Pearson|LP3|B17CCovarianceTests|NonstationaryParentTrendCoverageTests') { 2 } + elseif ($_.method -match 'CompetingRiskRecoveryTests|MixtureRecoveryTests|CompositePredictiveRecoveryTests') { 3 } + elseif ($_.area -eq 'DistributionFitting') { 4 } + elseif ($_.method -match '\.ViglioneEtAlTests\.') { 5 } + elseif ($_.area -eq 'ModelEstimation') { 6 } + elseif ($_.area -eq 'Univariate') { 7 } + elseif ($_.area -eq 'Bivariate') { 8 } + elseif ($_.area -eq 'RatingCurve') { 9 } + elseif ($_.area -eq 'TimeSeries') { 10 } + else { 11 } + }},method | Select-Object -ExpandProperty method) +} +$pending = @($ordered | Where-Object { + $record = $latest[$_] + $ForceRerun -or $null -eq $record -or $record.result -ne 'Passed' -or !$record.exactIdentityVerified -or $record.sourceChangedDuringRun +}) +$alreadyPassed = $ordered.Count - $pending.Count +Write-Output "$alreadyPassed current passes retained; $($pending.Count) exact methods pending out of $($ordered.Count) selected." +$logDirectory = Join-Path $evidenceRoot 'artifacts\regression-repair\logs' +New-Item -ItemType Directory -Path $logDirectory -Force | Out-Null +$batchLog = Join-Path $logDirectory ((Get-Date -Format 'yyyyMMdd-HHmmss-fff') + '-allowlist.log') +$index = 0 +foreach ($method in $pending) { + $index++ + Write-Output "[$index/$($pending.Count)] $method" + try { + & (Join-Path $PSScriptRoot 'run-bestfit-regression-method.ps1') -Test $method -BestFitRoot $BestFitRoot -NumericsRoot $NumericsRoot -Phase 'stabilized-pass' -CaseNote 'Resumable approved allowlist; no scientific settings altered.' *>&1 | Out-File -LiteralPath $batchLog -Append -Encoding utf8 + $record = Get-Content -LiteralPath $ledgerPath -Tail 1 | ConvertFrom-Json + if ($record.method -ne $method) { throw 'The latest ledger identity does not match the completed method.' } + if ($record.numerics.sourceSha256 -ne $numericsVersion.sourceSha256 -or $record.bestFit.sourceSha256 -ne $bestFitVersion.sourceSha256) { throw 'Source changed between methods; restart to reconcile stale passes.' } + Write-Output "$($record.result): test $($record.testSeconds)s; build $($record.buildSeconds)s; $($alreadyPassed + $index)/$($ordered.Count) current." + } catch { + if (Test-Path -LiteralPath $batchLog) { Get-Content -LiteralPath $batchLog -Tail 28 } + throw + } +} +Write-Output "All $($ordered.Count) selected exact methods have passing evidence for this source fingerprint pair." diff --git a/scripts/run-bestfit-regression-method.ps1 b/scripts/run-bestfit-regression-method.ps1 new file mode 100644 index 00000000..918021b7 --- /dev/null +++ b/scripts/run-bestfit-regression-method.ps1 @@ -0,0 +1,105 @@ +<# +.SYNOPSIS +Runs one approved BestFit regression method and appends independently checked TRX evidence. +.DESCRIPTION +Uses BestFit's guarded runner without changing its filter or scientific settings. A failed method +is recorded and then reported as an error so an outer serial loop stops for immediate diagnosis. +#> +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)][string]$Test, + [Parameter(Mandatory = $true)][string]$BestFitRoot, + [Parameter(Mandatory = $true)][string]$NumericsRoot, + [string]$Phase = 'iteration', + [string]$CaseNote = '' +) + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version Latest +$evidenceRoot = Split-Path $PSScriptRoot -Parent +$pausePath = Join-Path $evidenceRoot 'artifacts\regression-repair\pause-before-next-method.txt' +if (Test-Path -LiteralPath $pausePath) { + throw ('Paused before executing the next method: ' + (Get-Content -LiteralPath $pausePath -Raw).Trim()) +} +$inventoryPath = Join-Path $evidenceRoot 'docs\distributions\bestfit-regression-inventory.json' +$ledgerPath = Join-Path $evidenceRoot 'docs\distributions\bestfit-regression-runs.jsonl' +$inventory = Get-Content -LiteralPath $inventoryPath -Raw | ConvertFrom-Json +$entry = @($inventory.methods | Where-Object method -eq $Test) +if ($entry.Count -ne 1) { throw "Not one exact approved method: $Test" } +$BestFitRoot = (Resolve-Path -LiteralPath $BestFitRoot).Path +$NumericsRoot = (Resolve-Path -LiteralPath $NumericsRoot).Path + +. (Join-Path $PSScriptRoot 'bestfit-regression-evidence.ps1') + +$numericsBefore = Get-BestFitRegressionSourceVersion $NumericsRoot 'Numerics' +$bestFitBefore = Get-BestFitRegressionSourceVersion $BestFitRoot @('src/RMC.BestFit','src/RMC.BestFit.Verification','src/TestCommon','verification/data') +$logDirectory = Join-Path $evidenceRoot 'artifacts\regression-repair\logs' +New-Item -ItemType Directory -Path $logDirectory -Force | Out-Null +$runId = (Get-Date -Format 'yyyyMMdd-HHmmss-fff') + '-' + $Test.Replace('.','_') +$logPath = Join-Path $logDirectory ($runId + '.log') +$started = [DateTimeOffset]::UtcNow +$stopwatch = [System.Diagnostics.Stopwatch]::StartNew() +$failure = $null +$oldLocal = $env:UseLocalRmcNumerics +$oldProject = $env:RmcNumericsProjectPath +Push-Location $BestFitRoot +try { + $env:UseLocalRmcNumerics = 'true' + $env:RmcNumericsProjectPath = Join-Path $NumericsRoot 'Numerics\Numerics.csproj' + & (Join-Path $BestFitRoot 'scripts\run-verification-test.ps1') -Test $Test *>&1 | Tee-Object -FilePath $logPath +} catch { + $failure = $_.Exception.ToString() + $failure | Add-Content -LiteralPath $logPath +} finally { + Pop-Location + $env:UseLocalRmcNumerics = $oldLocal + $env:RmcNumericsProjectPath = $oldProject + $stopwatch.Stop() +} + +$logs = Get-Content -LiteralPath $logPath -Raw +$trxLine = [regex]::Match($logs, '(?m)^TRX: (.+)\s*$') +$record = [ordered]@{ + runId=$runId; method=$Test; phase=$Phase; startedUtc=$started.ToString('o'); + wallSeconds=$stopwatch.Elapsed.TotalSeconds; testSeconds=$null; buildSeconds=$null; + result='InfrastructureFailure'; trx=$null; log=$logPath; exactIdentityVerified=$false; + numerics=$numericsBefore; bestFit=$bestFitBefore; numericsAssemblySha256=$null; + bestFitAssemblySha256=$null; verificationAssemblySha256=$null; + dependencies=$entry[0].declaredDependencies; sourceChangedDuringRun=$false; + failure=$failure; note=$CaseNote +} +$buildTime = [regex]::Match($logs, 'Time Elapsed (\d+:\d+:\d+(?:\.\d+)?)') +if ($buildTime.Success) { $record.buildSeconds = [TimeSpan]::Parse($buildTime.Groups[1].Value,[cultureinfo]::InvariantCulture).TotalSeconds } +if ($trxLine.Success) { + $trxPath = $trxLine.Groups[1].Value.Trim() + $trxFiles = @(Get-ChildItem -LiteralPath (Split-Path $trxPath) -Filter *.trx -Recurse -File) + if ($trxFiles.Count -ne 1) { throw 'Expected exactly one TRX in the guarded run directory.' } + [xml]$trx = Get-Content -LiteralPath $trxPath -Raw + $results = @($trx.SelectNodes("//*[local-name()='UnitTestResult']")) + $definitions = @($trx.SelectNodes("//*[local-name()='TestMethod']")) + if ($results.Count -ne 1 -or $definitions.Count -ne 1) { throw 'Expected one TRX result and one test definition.' } + $actual = $definitions[0].className + '.' + $definitions[0].name + if ($actual -ne $Test) { throw "TRX identity mismatch: $actual" } + $record.trx = $trxPath + $record.exactIdentityVerified = $true + $record.result = [string]$results[0].outcome + $record.testSeconds = [TimeSpan]::Parse($results[0].duration,[cultureinfo]::InvariantCulture).TotalSeconds + $errorNode = $results[0].SelectSingleNode(".//*[local-name()='Message']") + if ($null -ne $errorNode) { $record.failure=$errorNode.InnerText } + $assemblyRoot = Join-Path $BestFitRoot 'src\RMC.BestFit.Verification\bin\Debug\net10.0' + $numericsAssembly = Join-Path $NumericsRoot 'Numerics\bin\Debug\net10.0\Numerics.dll' + $record.numericsAssemblySha256=(Get-FileHash -LiteralPath (Join-Path $assemblyRoot 'Numerics.dll') -Algorithm SHA256).Hash + if ($record.numericsAssemblySha256 -ne (Get-FileHash -LiteralPath $numericsAssembly -Algorithm SHA256).Hash) { throw 'Verification did not load the requested Numerics build.' } + $record.bestFitAssemblySha256=(Get-FileHash -LiteralPath (Join-Path $assemblyRoot 'RMC.BestFit.dll') -Algorithm SHA256).Hash + $record.verificationAssemblySha256=(Get-FileHash -LiteralPath (Join-Path $assemblyRoot 'RMC.BestFit.Verification.dll') -Algorithm SHA256).Hash +} +$numericsAfter = Get-BestFitRegressionSourceVersion $NumericsRoot 'Numerics' +$bestFitAfter = Get-BestFitRegressionSourceVersion $BestFitRoot @('src/RMC.BestFit','src/RMC.BestFit.Verification','src/TestCommon','verification/data') +$record.sourceChangedDuringRun = $numericsBefore.sourceSha256 -ne $numericsAfter.sourceSha256 -or $bestFitBefore.sourceSha256 -ne $bestFitAfter.sourceSha256 +if ($null -ne $failure -and $record.result -eq 'Passed') { + # A passing TRX cannot override a nonzero guarded-runner exit or infrastructure exception. + $record.result = 'InfrastructureFailure' +} +$record | ConvertTo-Json -Depth 10 -Compress | Add-Content -LiteralPath $ledgerPath -Encoding utf8 +Write-Host "Ledger: $($record.result); test=$($record.testSeconds)s; build=$($record.buildSeconds)s; source changed=$($record.sourceChangedDuringRun)" +if ($record.result -ne 'Passed' -or !$record.exactIdentityVerified -or $record.sourceChangedDuringRun) { throw "Method requires investigation: $Test" } From 4ded6aeca557f0f94181f1d9081231ea0fdce769 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 9 Sep 2026 12:28:48 -0600 Subject: [PATCH 205/222] Changing testing settings --- Test_Numerics/Test_Numerics.csproj | 1 - Test_Numerics/test.runsettings | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Test_Numerics/Test_Numerics.csproj b/Test_Numerics/Test_Numerics.csproj index 292388f6..e320c51e 100644 --- a/Test_Numerics/Test_Numerics.csproj +++ b/Test_Numerics/Test_Numerics.csproj @@ -6,7 +6,6 @@ false true $(MSBuildProjectDirectory)\test.runsettings - false diff --git a/Test_Numerics/test.runsettings b/Test_Numerics/test.runsettings index f75d4df6..b3e68c99 100644 --- a/Test_Numerics/test.runsettings +++ b/Test_Numerics/test.runsettings @@ -1,7 +1,7 @@ - - 1 + + 0 From 376e200ad9959ed639312e30d4d400bc4982cbdf Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 9 Sep 2026 15:48:39 -0600 Subject: [PATCH 206/222] Complete distribution XML documentation and helper cleanup --- .../Base/DistributionEndpointTail.cs | 6 ++ .../Base/DistributionMomentIntegration.cs | 18 +++- .../Univariate/Base/DistributionNumerics.cs | 82 +++++++++++++++++-- .../Base/DistributionParameterBounds.cs | 26 ++++-- .../Base/DistributionTailTransform.cs | 7 +- .../Base/DistributionUncertaintyNumerics.cs | 24 ++++-- .../Base/GammaDistributionNumerics.cs | 67 ++++++++++++++- .../Univariate/Base/MixtureLogWeights.cs | 6 +- .../Univariate/CompetingRisks.cs | 33 ++++++-- .../Distributions/Univariate/Exponential.cs | 4 +- .../Univariate/GammaDistribution.cs | 14 +++- .../Univariate/GeneralizedExtremeValue.cs | 14 +++- .../Univariate/GeneralizedLogistic.cs | 49 ++++++++--- .../Univariate/GeneralizedNormal.cs | 37 ++++++--- .../Univariate/GeneralizedPareto.cs | 6 +- Numerics/Distributions/Univariate/Gumbel.cs | 6 +- .../Univariate/KappaExpectedInformation.cs | 60 ++++++++++++-- Numerics/Distributions/Univariate/LnNormal.cs | 12 +++ .../Distributions/Univariate/LogNormal.cs | 10 +++ .../Univariate/LogPearsonTypeIII.cs | 17 ++++ Numerics/Distributions/Univariate/Mixture.cs | 30 +++++++ Numerics/Distributions/Univariate/Normal.cs | 14 ++++ .../Univariate/PearsonTypeIII.cs | 27 ++++++ Numerics/Distributions/Univariate/Weibull.cs | 7 +- Numerics/Utilities/Tools.cs | 1 + ...est_CompetingRisksEvaluationAllocations.cs | 2 +- .../Test_LegacyParameterConstraints.cs | 2 +- .../Test_NonpositiveConstraintRegressions.cs | 2 +- 28 files changed, 504 insertions(+), 79 deletions(-) diff --git a/Numerics/Distributions/Univariate/Base/DistributionEndpointTail.cs b/Numerics/Distributions/Univariate/Base/DistributionEndpointTail.cs index 562776ad..27c25e8e 100644 --- a/Numerics/Distributions/Univariate/Base/DistributionEndpointTail.cs +++ b/Numerics/Distributions/Univariate/Base/DistributionEndpointTail.cs @@ -7,6 +7,12 @@ namespace Numerics.Distributions internal static class DistributionEndpointTail { /// Returns tail ~ exp(logCoefficient)*distance^power*log(1/distance)^logPower. + /// The distribution whose finite endpoint behavior is requested. + /// to describe the lower CDF tail; to describe the upper survival tail. + /// The exponent applied to distance from the finite endpoint. + /// The exponent applied to the logarithm of the reciprocal endpoint distance. + /// The logarithm of the expansion coefficient. + /// when the distribution has a recognized expansion; otherwise, . /// These are finite lower CDF or upper survival endpoint limits. Infinite power denotes /// faster-than-polynomial decay. No numerical endpoint offset or density floor is used. internal static bool TryExpansion(UnivariateDistributionBase distribution, bool lower, diff --git a/Numerics/Distributions/Univariate/Base/DistributionMomentIntegration.cs b/Numerics/Distributions/Univariate/Base/DistributionMomentIntegration.cs index cf79f247..162831cc 100644 --- a/Numerics/Distributions/Univariate/Base/DistributionMomentIntegration.cs +++ b/Numerics/Distributions/Univariate/Base/DistributionMomentIntegration.cs @@ -8,12 +8,19 @@ namespace Numerics.Distributions internal static class DistributionMomentIntegration { /// Integrates a normalized log density about a local reference without raw-moment subtraction. + /// The natural logarithm of the normalized density in physical coordinates. + /// The lower support endpoint, which may be negative infinity. + /// The upper support endpoint, which may be positive infinity. + /// A finite physical-coordinate reference about which the integration is scaled. + /// A finite positive local scale used by the integration transform. + /// The mean, standard deviation, skewness, and kurtosis in that order. + /// The reference or scale is invalid, a moment is divergent or unresolved, quadrature fails its error checks, unit mass is not recovered, or the variance is not positive. /// Maps each side of the reference through x=center±scale*t/(1-t), including infinite /// endpoints. Integrates the mean offset first, then directly integrates centered powers. /// No probability tails are truncated and no estimated mass is silently normalized. internal static double[] Compute(Func logDensity, double minimum, double maximum, double center, double scale) { - if (!DistributionNumerics.IsFinite(center) || !(scale > 0) || !DistributionNumerics.IsFinite(scale)) + if (!Tools.IsFinite(center) || !(scale > 0) || !Tools.IsFinite(scale)) throw new InvalidOperationException("A finite reference and positive local scale are required for composite moment integration."); double lower = Limit(center - minimum, scale), upper = Limit(maximum - center, scale); double logScale = Math.Log(scale); @@ -36,7 +43,7 @@ double Function(double t) log += order * Math.Log(Math.Abs(centered)); } double value = Math.Exp(log + logScale - 2 * Tools.Log1p(-t)); - if (!DistributionNumerics.IsFinite(value)) + if (!Tools.IsFinite(value)) throw new InvalidOperationException("A composite moment is divergent or cannot be resolved numerically."); return (order % 2 != 0 && centered < 0) ? -value : value; } @@ -46,8 +53,8 @@ double Function(double t) MaxFunctionEvaluations = 200000, ReportFailure = true }; integrator.Integrate(); - if (integrator.Status != IntegrationStatus.Success || !DistributionNumerics.IsFinite(integrator.Result) - || !DistributionNumerics.IsFinite(integrator.StandardError) + if (integrator.Status != IntegrationStatus.Success || !Tools.IsFinite(integrator.Result) + || !Tools.IsFinite(integrator.StandardError) || integrator.StandardError > Math.Max(1E-10, Math.Abs(integrator.Result) * 1E-8)) throw new InvalidOperationException("Composite moment integration did not meet its error tolerance."); return integrator.Result; @@ -65,6 +72,9 @@ double Function(double t) } /// Maps a finite or infinite one-sided support width to [0,1]. + /// The nonnegative physical-coordinate distance from the integration center to one support endpoint. + /// The positive local scale used by the integration transform. + /// Zero for an empty side, one for an infinite or unrepresentably large side, or the transformed finite limit. private static double Limit(double width, double scale) { if (width <= 0) return 0; diff --git a/Numerics/Distributions/Univariate/Base/DistributionNumerics.cs b/Numerics/Distributions/Univariate/Base/DistributionNumerics.cs index dd1877d7..ef9f04aa 100644 --- a/Numerics/Distributions/Univariate/Base/DistributionNumerics.cs +++ b/Numerics/Distributions/Univariate/Base/DistributionNumerics.cs @@ -14,6 +14,9 @@ internal static partial class DistributionNumerics /// The original family-specific initialization and bounds. /// The exceptional-input hardened initialization and bounds. /// The legacy constraints if finite and within ordered bounds; otherwise the hardened constraints. + /// A selected constraint delegate rejects its input. + /// A selected constraint delegate cannot produce usable constraints. + /// A selected constraint delegate encounters an arithmetic failure. /// The fallback must not narrow previously valid prior envelopes or alter their rounding. internal static Tuple PreferLegacyConstraints( Func> legacy, Func> fallback) @@ -26,7 +29,7 @@ internal static Tuple PreferLegacyConstraints( for (int i = 0; i < constraints.Item1.Length; i++) { double initial = constraints.Item1[i], lower = constraints.Item2[i], upper = constraints.Item3[i]; - if (!IsFinite(initial) || !IsFinite(lower) || !IsFinite(upper) || !(lower < upper && lower <= initial && initial <= upper)) + if (!Tools.IsFinite(initial) || !Tools.IsFinite(lower) || !Tools.IsFinite(upper) || !(lower < upper && lower <= initial && initial <= upper)) { usable = false; break; @@ -52,6 +55,8 @@ internal static Tuple PreferLegacyConstraints( } /// A cache key including nested distribution settings omitted from flattened parameter vectors. + /// The distribution whose complete mutable configuration is serialized into the key. + /// An invariant, deterministic representation of the distribution and any nested component configuration. internal static string ConfigurationState(UnivariateDistributionBase distribution) { // Composite serialization flattens scalar parameters and need not support an empirical @@ -69,17 +74,20 @@ internal static string ConfigurationState(UnivariateDistributionBase distributio } /// Forms an affine standardized value without overflowing a finite difference unnecessarily. + /// The value in physical coordinates. + /// The location to subtract. + /// The scale by which to divide the centered value. + /// (x-location)/scale, using an algebraically equivalent form when subtraction of finite operands overflows. internal static double Standardize(double x, double location, double scale) { double difference = x - location; - return double.IsInfinity(difference) && IsFinite(x) && IsFinite(location) + return double.IsInfinity(difference) && Tools.IsFinite(x) && Tools.IsFinite(location) ? x / scale - location / scale : difference / scale; } - /// Whether a value is finite on all supported target frameworks. - internal static bool IsFinite(double value) => !double.IsNaN(value) && !double.IsInfinity(value); - /// Computes log(1-exp(a)) for a nonpositive log probability, including its limits. + /// The logarithm of a probability in the interval from negative infinity through zero. + /// log(1-exp(a)), or not-a-number when is positive or not-a-number. internal static double Log1mExp(double a) { if (a > 0 || double.IsNaN(a)) return double.NaN; @@ -87,6 +95,9 @@ internal static double Log1mExp(double a) } /// Computes log(exp(a)-exp(b)), with equal arguments representing zero mass. + /// The logarithm of the minuend. + /// The logarithm of the subtrahend, which must not exceed . + /// The logarithmic difference, negative infinity for equal arguments, or not-a-number for invalid ordering or input. internal static double LogDifference(double a, double b) { if (double.IsNaN(a) || double.IsNaN(b) || b > a) return double.NaN; @@ -95,6 +106,9 @@ internal static double LogDifference(double a, double b) } /// Adds two nonnegative quantities represented by their logarithms. + /// The logarithm of the first nonnegative quantity. + /// The logarithm of the second nonnegative quantity. + /// The logarithm of the sum, including the natural infinity and not-a-number limits. internal static double LogSum(double a, double b) { if (double.IsNaN(a) || double.IsNaN(b)) return double.NaN; @@ -106,6 +120,8 @@ internal static double LogSum(double a, double b) } /// The normal log CDF, preserving the logarithm after the tail itself underflows. + /// The standard Normal variate. + /// The natural logarithm of the standard Normal cumulative probability, including endpoint and not-a-number limits. /// The far tail uses the convergent Laplace continued fraction for the Mills ratio. internal static double NormalLogCDF(double z) { @@ -122,12 +138,18 @@ internal static double NormalLogCDF(double z) } /// The normal log survival function, evaluated directly through reflection. + /// The standard Normal variate. + /// The natural logarithm of the standard Normal probability above . internal static double NormalLogSurvival(double z) => NormalLogCDF(-z); /// The exponential divided difference expm1(x)/x, including x=0. + /// The divided-exponential argument. + /// expm1(x)/x, with the continuous value one at zero. internal static double Exprel(double x) => x == 0 ? 1 : Tools.Expm1(x) / x; /// The derivative of expm1(x)/x, with a convergent series at zero. + /// The divided-exponential argument. + /// The derivative of expm1(x)/x, including its continuous value one-half at zero. internal static double ExprelDerivative(double x) { if (Math.Abs(x) >= 0.1) return ((x - 1) * Math.Exp(x) + 1) / x / x; @@ -142,6 +164,8 @@ internal static double ExprelDerivative(double x) } /// Rejects nonfinite and endpoint probabilities for quantile uncertainty calculations. + /// The probability to validate. + /// is not finite and strictly between zero and one. internal static void ValidateProbability(double probability) { if (!(probability > 0 && probability < 1)) @@ -149,12 +173,20 @@ internal static void ValidateProbability(double probability) } /// Requires a positive sample size for asymptotic uncertainty. + /// The number of independent observations. + /// is not positive. internal static void ValidateSampleSize(int sampleSize) { if (sampleSize <= 0) throw new ArgumentOutOfRangeException(nameof(sampleSize), "Sample size must be positive."); } /// Checks sample size and finite interior probabilities for interval approximations requiring n-1. + /// The available number of observations. + /// The quantile probabilities to validate. + /// The confidence probabilities to validate. + /// The smallest sample size accepted by the calling interval method. + /// or is . + /// The sample is too small, a probability list is empty, or a probability is not finite and strictly interior. internal static void ValidateConfidenceInputs(int sampleSize, IList quantiles, IList percentiles, int minimumSampleSize = 1) { if (sampleSize < minimumSampleSize) throw new ArgumentOutOfRangeException(nameof(sampleSize), "Insufficient observations for this confidence interval method."); @@ -166,6 +198,11 @@ internal static void ValidateConfidenceInputs(int sampleSize, IList quan } /// Checks that an initialization sample is finite, sufficiently long, and nonconstant. + /// The observations to validate. + /// The minimum accepted number of observations. + /// to require every observation to be strictly positive; otherwise, . + /// is . + /// The sample is too short, contains an invalid observation, or is constant. internal static void ValidateSample(IList sample, int minimumCount = 2, bool positive = false) { if (sample == null) throw new ArgumentNullException(nameof(sample)); @@ -173,7 +210,7 @@ internal static void ValidateSample(IList sample, int minimumCount = 2, bool distinct = false; for (int i = 0; i < sample.Count; i++) { - if (!IsFinite(sample[i]) || (positive && sample[i] <= 0)) + if (!Tools.IsFinite(sample[i]) || (positive && sample[i] <= 0)) throw new ArgumentOutOfRangeException(nameof(sample), positive ? "Observations must be finite and strictly positive." : "Observations must be finite."); distinct |= sample[i] != sample[0]; } @@ -181,6 +218,13 @@ internal static void ValidateSample(IList sample, int minimumCount = 2, } /// Assembles quantile gradients by observation row and obtains a determinant without artificial pivots. + /// The distribution that supplies gradients in public parameter coordinates. + /// One finite interior probability per matrix row and public parameter. + /// The signed determinant, including zero for an exactly singular Jacobian. + /// The square quantile-gradient matrix. + /// or is . + /// The probability count, an individual probability, or a returned gradient dimension is invalid. + /// A quantile derivative is nonfinite or the determinant cannot be evaluated from finite entries. internal static double[,] QuantileJacobian(IStandardError distribution, IList probabilities, out double determinant) { var matrix = QuantileGradientMatrix(distribution, probabilities); @@ -190,6 +234,12 @@ internal static void ValidateSample(IList sample, int minimumCount = 2, } /// Builds the square Jacobian with one quantile per row in the public parameter coordinates. + /// The distribution that supplies quantile gradients. + /// One finite interior probability per matrix row and public parameter. + /// The square matrix whose rows are quantile gradients. + /// or is . + /// The probability count, an individual probability, or a returned gradient dimension is invalid. + /// A returned quantile derivative is nonfinite. internal static double[,] QuantileGradientMatrix(IStandardError distribution, IList probabilities) { if (distribution == null) throw new ArgumentNullException(nameof(distribution)); @@ -206,7 +256,7 @@ internal static void ValidateSample(IList sample, int minimumCount = 2, if (gradient.Length != count) throw new ArgumentOutOfRangeException(nameof(probabilities), "The quantile Jacobian must be square."); for (int j = 0; j < count; j++) { - if (!IsFinite(gradient[j])) throw new InvalidOperationException("The quantile Jacobian contains a nonfinite derivative."); + if (!Tools.IsFinite(gradient[j])) throw new InvalidOperationException("The quantile Jacobian contains a nonfinite derivative."); matrix[i, j] = gradient[j]; } } @@ -214,6 +264,11 @@ internal static void ValidateSample(IList sample, int minimumCount = 2, } /// Log absolute determinant by row/column equilibration and partial pivoting. + /// The finite square matrix to factor. + /// The determinant sign, or zero when the matrix is exactly singular. + /// The natural logarithm of the absolute determinant, or negative infinity for exact singularity. + /// is not square. + /// contains a nonfinite entry. /// Zero pivots retain exact singularity; no jitter or artificial tiny pivots are inserted. internal static double LogAbsDeterminant(double[,] matrix, out int sign) { @@ -226,7 +281,7 @@ internal static double LogAbsDeterminant(double[,] matrix, out int sign) { double scale = 0; for (int j = 0; j < n; j++) scale = Math.Max(scale, Math.Abs(a[i, j])); - if (!IsFinite(scale)) throw new InvalidOperationException("The determinant requires finite matrix entries."); + if (!Tools.IsFinite(scale)) throw new InvalidOperationException("The determinant requires finite matrix entries."); if (scale == 0) { sign = 0; return double.NegativeInfinity; } log += Math.Log(scale); for (int j = 0; j < n; j++) @@ -268,6 +323,9 @@ internal static double LogAbsDeterminant(double[,] matrix, out int sign) } /// Fraction-free integer elimination for a determinant whose floating-point pivot is unresolved. + /// The finite square binary64 matrix to evaluate exactly after dyadic scaling. + /// The exact determinant sign, or zero when the matrix is singular. + /// The natural logarithm of the absolute determinant, or negative infinity for exact singularity. /// Each finite binary64 row is scaled by an exact power of two to integers. /// Bareiss elimination then distinguishes exact dependence from a merely small determinant. /// The threshold selecting this path does not classify a matrix as singular. @@ -325,6 +383,8 @@ private static double ExactDyadicLogDeterminant(double[,] matrix, out int sign) } /// Whether a reviewed distribution has no probability atoms. + /// The distribution or composite distribution to classify. + /// when the distribution and every nested component are recognized as continuous and the mixture has no hurdle atom; otherwise, . private static bool IsContinuous(UnivariateDistributionBase distribution) { if (distribution is CompetingRisks competing) return competing.Distributions.All(IsContinuous); @@ -338,6 +398,10 @@ private static bool IsContinuous(UnivariateDistributionBase distribution) } /// Resolves a positive continuous interval when both pairs of log tails round to identical values. + /// The continuous distribution whose interval probability is required. + /// The open lower interval endpoint. + /// The closed upper interval endpoint. + /// The logarithm of the integrated interval probability, or negative infinity when the distribution is not recognized as continuous or the clipped interval has no finite positive width. /// Eight-point Gauss-Legendre integration is used only after tail subtraction collapses. /// The explicitly identified continuous families avoid treating an atom as a density contribution. internal static double CollapsedContinuousLogInterval(UnivariateDistributionBase distribution, double lower, double upper) @@ -346,7 +410,7 @@ internal static double CollapsedContinuousLogInterval(UnivariateDistributionBase lower = Math.Max(lower, distribution.Minimum); upper = Math.Min(upper, distribution.Maximum); double width = upper - lower; - if (!(width > 0) || !IsFinite(width)) return double.NegativeInfinity; + if (!(width > 0) || !Tools.IsFinite(width)) return double.NegativeInfinity; double[] nodes = { .019855071751231884, .10166676129318663, .23723379504183551, .4082826787521751, .5917173212478249, .7627662049581645, .8983332387068134, .9801449282487681 }; double[] weights = { .05061426814518813, .11119051722668724, .15685332293894365, .181341891689181, diff --git a/Numerics/Distributions/Univariate/Base/DistributionParameterBounds.cs b/Numerics/Distributions/Univariate/Base/DistributionParameterBounds.cs index 39f51199..3e757c62 100644 --- a/Numerics/Distributions/Univariate/Base/DistributionParameterBounds.cs +++ b/Numerics/Distributions/Univariate/Base/DistributionParameterBounds.cs @@ -3,40 +3,56 @@ namespace Numerics.Distributions { + /// Provides scale-aware construction of finite initialization and parameter bounds for univariate distributions. internal static partial class DistributionNumerics { /// Returns a finite magnitude used to evaluate the same initialization estimator in unit coordinates. + /// The validated finite observations whose largest absolute magnitude defines the scale. + /// The largest absolute observation. + /// The sample has no finite positive magnitude. internal static double InitializationScale(IList sample) { double scale = 0; for (int i = 0; i < sample.Count; i++) scale = Math.Max(scale, Math.Abs(sample[i])); - if (!(scale > 0) || !IsFinite(scale)) + if (!(scale > 0) || !Tools.IsFinite(scale)) throw new ArgumentOutOfRangeException(nameof(sample), "A finite nonzero sample magnitude is required for initialization."); return scale; } /// Constructs ordered finite positive bounds that contain a representable positive initial parameter. + /// The finite positive initial parameter that the bounds must contain. + /// The resulting finite positive lower bound. + /// The resulting finite upper bound. + /// is not finite and positive, or ordered finite bounds cannot be represented. internal static void PositiveParameterBounds(double initial, out double lower, out double upper) { - if (!(initial > 0) || !IsFinite(initial)) + if (!(initial > 0) || !Tools.IsFinite(initial)) throw new ArgumentOutOfRangeException(nameof(initial), "The initial parameter must be finite and positive."); lower = Math.Max(double.Epsilon, Math.Min(Tools.DoubleMachineEpsilon, initial / 10)); double decade = Math.Pow(10, Math.Ceiling(Math.Log10(initial) + 1)); - upper = IsFinite(decade) ? Math.Max(initial, decade) : double.MaxValue; + upper = Tools.IsFinite(decade) ? Math.Max(initial, decade) : double.MaxValue; if (!(lower < upper)) throw new ArgumentOutOfRangeException(nameof(initial), "Finite ordered parameter bounds cannot be represented."); } /// Constructs signed, scale-aware finite location bounds and a feasible initial location. + /// On input, the proposed finite location; on output, that value or a feasible midpoint when it was outside the constructed bounds. + /// A finite positive characteristic sample scale. + /// The minimum observation in physical coordinates. + /// The maximum observation in physical coordinates. + /// to use as the upper location bound; otherwise, . + /// The resulting finite lower location bound. + /// The resulting finite upper location bound. + /// The initial location or scale is invalid, or ordered finite location bounds cannot be represented. /// For a lower-endpoint family the upper bound is the actual sample minimum. internal static void LocationParameterBounds(ref double initial, double scale, double dataMinimum, double dataMaximum, bool upperAtMinimum, out double lower, out double upper) { - if (!IsFinite(initial) || !(scale > 0) || !IsFinite(scale)) + if (!Tools.IsFinite(initial) || !(scale > 0) || !Tools.IsFinite(scale)) throw new ArgumentOutOfRangeException(nameof(initial), "Initialization requires a finite location and positive scale."); double magnitude = Math.Max(Math.Abs(initial), Math.Max(scale, Math.Max(Math.Abs(dataMinimum), Math.Abs(dataMaximum)))); double radius = Math.Pow(10, Math.Ceiling(Math.Log10(magnitude) + 1)); - if (!IsFinite(radius)) radius = double.MaxValue; + if (!Tools.IsFinite(radius)) radius = double.MaxValue; lower = -radius; upper = upperAtMinimum ? dataMinimum : radius; if (!(lower < upper)) diff --git a/Numerics/Distributions/Univariate/Base/DistributionTailTransform.cs b/Numerics/Distributions/Univariate/Base/DistributionTailTransform.cs index 6daa9601..9f2d5413 100644 --- a/Numerics/Distributions/Univariate/Base/DistributionTailTransform.cs +++ b/Numerics/Distributions/Univariate/Base/DistributionTailTransform.cs @@ -2,6 +2,7 @@ namespace Numerics.Distributions { + /// Provides overflow-resistant transforms used by Hosking-form distribution tails. internal static partial class DistributionNumerics { /// Evaluates the Hosking shape transform without losing a finite logarithm to affine or product overflow. @@ -18,12 +19,12 @@ internal static double HoskingShapeTransform(double x, double location, double s double standardized = Standardize(x, location, scale); if (shape == 0 || double.IsNaN(standardized)) return standardized; double product = shape * standardized; - if (IsFinite(product)) return product == 0 ? standardized : -Tools.Log1p(-product) / shape; - if (!IsFinite(x) || !IsFinite(location)) return -Tools.Log1p(-product) / shape; + if (Tools.IsFinite(product)) return product == 0 ? standardized : -Tools.Log1p(-product) / shape; + if (!Tools.IsFinite(x) || !Tools.IsFinite(location)) return -Tools.Log1p(-product) / shape; double difference = x - location; double logDifference; - if (IsFinite(difference)) logDifference = Math.Log(Math.Abs(difference)); + if (Tools.IsFinite(difference)) logDifference = Math.Log(Math.Abs(difference)); else { double magnitude = Math.Max(Math.Abs(x), Math.Abs(location)); diff --git a/Numerics/Distributions/Univariate/Base/DistributionUncertaintyNumerics.cs b/Numerics/Distributions/Univariate/Base/DistributionUncertaintyNumerics.cs index 11bff032..c2bcf16c 100644 --- a/Numerics/Distributions/Univariate/Base/DistributionUncertaintyNumerics.cs +++ b/Numerics/Distributions/Univariate/Base/DistributionUncertaintyNumerics.cs @@ -2,6 +2,7 @@ namespace Numerics.Distributions { + /// Provides range-preserving covariance contractions and divided-exponential products for distribution uncertainty calculations. internal static partial class DistributionNumerics { /// Contracts an unchanged covariance and quantile gradient without forming avoidable overflowing products. @@ -16,7 +17,7 @@ internal static partial class DistributionNumerics /// diagonal inflation or negative-variance floor is applied. internal static double ScaledQuantileVariance(double[,] covariance, double[] gradient, double scale = 1) { - if (!(scale > 0) || !IsFinite(scale)) throw new ArgumentOutOfRangeException(nameof(scale)); + if (!(scale > 0) || !Tools.IsFinite(scale)) throw new ArgumentOutOfRangeException(nameof(scale)); if (covariance.GetLength(0) != gradient.Length || covariance.GetLength(1) != gradient.Length) throw new ArgumentOutOfRangeException(nameof(covariance), "Covariance and gradient dimensions must agree."); var gradientLogs = new double[gradient.Length]; @@ -28,7 +29,7 @@ internal static double ScaledQuantileVariance(double[,] covariance, double[] gra gradientLogs[i] = Math.Log(Math.Abs(gradient[i])); for (int j = 0; j < gradient.Length; j++) { - if (!IsFinite(covariance[i, j])) throw new InvalidOperationException("The parameter covariance is outside the finite floating-point range."); + if (!Tools.IsFinite(covariance[i, j])) throw new InvalidOperationException("The parameter covariance is outside the finite floating-point range."); } } double largestLogTerm = double.NegativeInfinity; @@ -52,7 +53,7 @@ internal static double ScaledQuantileVariance(double[,] covariance, double[] gra quadratic = next; } quadratic += correction; - if (!IsFinite(quadratic) || quadratic < 0) + if (!Tools.IsFinite(quadratic) || quadratic < 0) throw new InvalidOperationException("The quantile variance could not be resolved as a nonnegative quadratic form."); if (quadratic == 0) return 0; return Math.Exp(2 * Math.Log(scale) + largestLogTerm + Math.Log(quadratic)); @@ -67,7 +68,7 @@ internal static double ScaledQuantileVariance(double[,] covariance, double[] gra /// avoids squaring a large shape or forming an underflowed unscaled residual. internal static double GammaScaledFisherResidual(double shape) { - if (!(shape > 0) || !IsFinite(shape)) throw new ArgumentOutOfRangeException(nameof(shape)); + if (!(shape > 0) || !Tools.IsFinite(shape)) throw new ArgumentOutOfRangeException(nameof(shape)); double shifted = shape, recurrence = 0; while (shifted < 32) { @@ -84,18 +85,31 @@ internal static double GammaScaledFisherResidual(double shape) } /// Forms scale times value times exprel(argument), retaining a finite product after unit-scale overflow. + /// The signed outer multiplier. + /// The signed value multiplying the divided exponential. + /// The argument of the exponential relative function. + /// times times exprel(argument), evaluated with extended logarithmic range when necessary. internal static double ScaledExprelProduct(double scale, double value, double argument) { return ScaledExprelProductCore(scale, value, argument, false); } /// Forms scale times value squared times exprel'(argument) without squaring a tiny value prematurely. + /// The signed outer multiplier. + /// The value whose square multiplies the derivative. + /// The argument of the exponential relative derivative. + /// times the square of times exprel'(argument), evaluated with extended logarithmic range when necessary. internal static double ScaledExprelDerivativeProduct(double scale, double value, double argument) { return ScaledExprelProductCore(scale, value, argument, true); } /// Combines signed multipliers and divided-exponential logarithms when ordinary product arithmetic loses range. + /// The signed outer multiplier. + /// The signed value, or the value to square when is . + /// The divided-exponential argument. + /// to use the derivative and square ; otherwise, to use the function itself. + /// The requested signed product, including its natural zero, infinity, or not-a-number limit. private static double ScaledExprelProductCore(double scale, double value, double argument, bool derivative) { if (value == 0) return 0; @@ -104,7 +118,7 @@ private static double ScaledExprelProductCore(double scale, double value, double double divided = derivative ? ExprelDerivative(argument) : Exprel(argument); double factor = derivative ? value * value : value; double result = scale * (factor * divided); - if (IsFinite(result) && result != 0) return result; + if (Tools.IsFinite(result) && result != 0) return result; double logDivided; if (argument > 50) logDivided = derivative ? argument + Math.Log(argument - 1) + Tools.Log1p(Math.Exp(-argument) / (argument - 1)) - 2 * Math.Log(argument) diff --git a/Numerics/Distributions/Univariate/Base/GammaDistributionNumerics.cs b/Numerics/Distributions/Univariate/Base/GammaDistributionNumerics.cs index a31839a4..42000b81 100644 --- a/Numerics/Distributions/Univariate/Base/GammaDistributionNumerics.cs +++ b/Numerics/Distributions/Univariate/Base/GammaDistributionNumerics.cs @@ -3,6 +3,7 @@ namespace Numerics.Distributions { + /// Provides stable gamma-function tails, quantiles, derivatives, and asymptotic expansions for univariate distributions. internal static partial class DistributionNumerics { /// Immutable integer zeta values reused by the log-gamma and GEV series. @@ -29,6 +30,9 @@ private static double[] CreateIntegerZetaValues() } /// Trigamma with an exact recurrence and a sufficiently large asymptotic argument for covariance work. + /// The finite positive gamma shape. + /// The trigamma value at . + /// is not finite and positive. internal static double AccurateTrigamma(double shape) { ValidateGammaShape(shape); @@ -39,12 +43,24 @@ internal static double AccurateTrigamma(double shape) } /// Log regularized lower gamma integral P(a,x). + /// The finite positive gamma shape. + /// The nonnegative unit-scale gamma variate. + /// The natural logarithm of the regularized lower incomplete gamma integral, including endpoint and not-a-number limits. + /// is not finite and positive. internal static double GammaLogCDF(double shape, double x) => GammaLogTail(shape, x, false, out _); /// Log regularized upper gamma integral Q(a,x). + /// The finite positive gamma shape. + /// The nonnegative unit-scale gamma variate. + /// The natural logarithm of the regularized upper incomplete gamma integral, including endpoint and not-a-number limits. + /// is not finite and positive. internal static double GammaLogSurvival(double shape, double x) => GammaLogTail(shape, x, true, out _); /// Log unit-scale gamma density, including one-sided endpoint limits. + /// The finite positive gamma shape. + /// The unit-scale gamma variate. + /// The log density, negative infinity outside support, the appropriate zero endpoint limit, or not-a-number when is not-a-number. + /// is not finite and positive. internal static double GammaLogDensity(double shape, double x) { ValidateGammaShape(shape); @@ -55,6 +71,11 @@ internal static double GammaLogDensity(double shape, double x) } /// Implicit shape derivative of the actual unit-scale gamma quantile at its value. + /// The finite positive gamma shape. + /// The nonnegative finite unit-scale gamma quantile. + /// The derivative of the unit-scale quantile with respect to , including zero at the lower endpoint. + /// is invalid or is negative or nonfinite. + /// A required gamma series or continued fraction does not converge. /// Differentiates the convergent lower series or upper continued fraction together /// with the probability. The large-shape expansion is differentiated analytically. No /// frequency-factor approximation or perturbation across the shape boundary is used. @@ -62,7 +83,7 @@ internal static double GammaQuantileShapeDerivative(double shape, double unitQua { ValidateGammaShape(shape); if (unitQuantile == 0) return 0; - if (!(unitQuantile > 0) || !IsFinite(unitQuantile)) + if (!(unitQuantile > 0) || !Tools.IsFinite(unitQuantile)) throw new ArgumentOutOfRangeException(nameof(unitQuantile)); bool upper = unitQuantile >= shape; double log = GammaLogTail(shape, unitQuantile, upper, out double derivative); @@ -72,6 +93,12 @@ internal static double GammaQuantileShapeDerivative(double shape, double unitQua } /// Inverts a gamma tail directly, retaining tiny upper and lower probabilities. + /// The finite positive gamma shape. + /// The lower- or upper-tail probability in the closed unit interval. + /// to invert the upper tail; to invert the lower tail. + /// The nonnegative unit-scale gamma quantile, including zero and positive-infinity endpoint limits. + /// is invalid or is outside the closed unit interval or not-a-number. + /// The gamma tail evaluation or bracketed quantile solve does not converge. /// Uses a tail-aware bracketed Newton solve; a normal approximation supplies only /// the initial point. Tiny quantiles are solved in logarithmic coordinates. internal static double GammaInverseCDF(double shape, double probability, bool upperTail = false) @@ -89,7 +116,7 @@ internal static double GammaInverseCDF(double shape, double probability, bool up double z = Normal.StandardZ(probability) * (upperTail ? -1 : 1); double w = 1 - 1 / (9 * shape) + z / (3 * root); double guess = w > 0 ? shape * w * w * w : Math.Exp(smallLog); - if (!(guess > 0) || !IsFinite(guess)) guess = Math.Max(shape, 1); + if (!(guess > 0) || !Tools.IsFinite(guess)) guess = Math.Max(shape, 1); double lower = 0, upper = Math.Max(Math.Max(shape, 1), guess); bool Below(double value) { @@ -111,7 +138,7 @@ bool Below(double value) if (upperTail ? residual > 0 : residual < 0) lower = x; else upper = x; double slope = Math.Exp(GammaLogDensity(shape, x) - log) * (upperTail ? -1 : 1); double next = x - residual / slope; - if (!(next > lower && next < upper) || !IsFinite(next)) next = lower + (upper - lower) / 2; + if (!(next > lower && next < upper) || !Tools.IsFinite(next)) next = lower + (upper - lower) / 2; if (next == x || next == lower || next == upper) return next; x = next; } @@ -119,12 +146,21 @@ bool Below(double value) } /// Requires a positive finite gamma shape. + /// The shape to validate. + /// is not finite and positive. private static void ValidateGammaShape(double shape) { - if (!(shape > 0) || !IsFinite(shape)) throw new ArgumentOutOfRangeException(nameof(shape), "Gamma shape must be positive and finite."); + if (!(shape > 0) || !Tools.IsFinite(shape)) throw new ArgumentOutOfRangeException(nameof(shape), "Gamma shape must be positive and finite."); } /// Evaluates a gamma log tail and its fixed-observation shape derivative. + /// The finite positive gamma shape. + /// The unit-scale gamma variate. + /// to evaluate the upper tail; to evaluate the lower tail. + /// The fixed- derivative of the returned log tail with respect to . + /// The requested log tail, including endpoint and not-a-number limits. + /// is not finite and positive. + /// The selected lower series or upper continued fraction does not converge. private static double GammaLogTail(double a, double x, bool upper, out double derivative) { ValidateGammaShape(a); @@ -205,6 +241,10 @@ private static double GammaLogTail(double a, double x, bool upper, out double de } /// Direct Q series at small shape/argument, avoiding a near-one lower-tail subtraction. + /// The positive gamma shape in the small-shape branch. + /// The positive unit-scale variate in the small-argument branch. + /// The fixed- derivative of the returned log upper tail with respect to . + /// The natural logarithm of the regularized upper incomplete gamma integral. private static double GammaSmallUpper(double a, double x, out double derivative) { double sum = 0, dsum = 0, power = 1; @@ -225,6 +265,9 @@ private static double GammaSmallUpper(double a, double x, out double derivative) } /// Log(x^a exp(-x)/Gamma(a)) without subtracting large near-equal terms. + /// The positive gamma shape. + /// The positive unit-scale gamma variate. + /// log(x^a*exp(-x)/Gamma(a)). private static double GammaLogKernel(double a, double x) { if (a < 16) return a * Math.Log(x) - x - Gamma.LogGamma(a); @@ -235,6 +278,9 @@ private static double GammaLogKernel(double a, double x) } /// Fixed-x derivative of the log kernel, retaining its small residual at large shape. + /// The positive gamma shape. + /// The positive fixed unit-scale gamma variate. + /// The derivative of the log gamma kernel with respect to . private static double GammaKernelShapeDerivative(double a, double x) { if (a < 16) return Math.Log(x) - Gamma.Digamma(a); @@ -245,6 +291,8 @@ private static double GammaKernelShapeDerivative(double a, double x) } /// Stirling log-gamma remainder for arguments at least sixteen. + /// The gamma argument, expected to be at least sixteen. + /// The retained Stirling-series correction to the leading log-gamma terms. private static double StirlingRemainder(double a) { double r = 1 / a, r2 = r * r; @@ -252,6 +300,8 @@ private static double StirlingRemainder(double a) } /// Cancellation-free log(1+x)-x. + /// The argument, greater than negative one in its probability-distribution uses. + /// log(1+x)-x, evaluated by a local series when subtraction would cancel. internal static double Log1pMinusX(double x) { if (Math.Abs(x) >= 0.25) return Tools.Log1p(x) - x; @@ -267,6 +317,8 @@ internal static double Log1pMinusX(double x) } /// Log Gamma(1+a) with the zeta Taylor series at small a. + /// The increment from one. + /// log(Gamma(1+a)). internal static double LogGammaOnePlus(double a) { if (a > 0.5) return Gamma.LogGamma(a + 1); @@ -282,6 +334,8 @@ internal static double LogGammaOnePlus(double a) } /// Integer zeta constants for the log Gamma(1+a) series. + /// The integer zeta argument, which must be at least two. + /// The Riemann zeta value at . internal static double ZetaInteger(int n) { if (n < 60) return IntegerZetaValues[n - 2]; @@ -301,6 +355,11 @@ internal static double ZetaInteger(int n) }; /// Uniform gamma expansion with analytical fixed-x shape differentiation. + /// The large positive gamma shape. + /// (x-a)/a, restricted to the local uniform-expansion region. + /// to evaluate the upper tail; to evaluate the lower tail. + /// The fixed-observation derivative of the returned log tail with respect to . + /// The requested gamma log tail from the Temme uniform expansion. private static double GammaTemme(double a, double delta, bool upper, out double derivative) { double eta = delta == 0 ? 0 : Math.Sign(delta) * Math.Sqrt(-2 * Log1pMinusX(delta)); diff --git a/Numerics/Distributions/Univariate/Base/MixtureLogWeights.cs b/Numerics/Distributions/Univariate/Base/MixtureLogWeights.cs index c0bd5681..89fe96b7 100644 --- a/Numerics/Distributions/Univariate/Base/MixtureLogWeights.cs +++ b/Numerics/Distributions/Univariate/Base/MixtureLogWeights.cs @@ -6,6 +6,10 @@ namespace Numerics.Distributions internal static class MixtureLogWeights { /// Returns the row log probability and corresponding responsibilities. + /// The component log probabilities for one observation. + /// The corresponding nonnegative mixture weights. + /// The destination populated with normalized component responsibilities. + /// The logarithm of the weighted row probability, or a nonfinite value when the row cannot be normalized. /// Nonfinite or impossible rows are returned as nonfinite log probabilities so the /// caller can retain its observation-specific error message and aggregate likelihood convention. internal static double Normalize(double[] logDensities, double[] weights, double[] responsibilities) @@ -13,7 +17,7 @@ internal static double Normalize(double[] logDensities, double[] weights, double double maximum = double.NegativeInfinity; for (int i = 0; i < weights.Length; i++) if (weights[i] > 0) maximum = Math.Max(maximum, logDensities[i]); - if (!DistributionNumerics.IsFinite(maximum)) return maximum; + if (!Tools.IsFinite(maximum)) return maximum; double weightedMaximum = double.NegativeInfinity; for (int i = 0; i < weights.Length; i++) { diff --git a/Numerics/Distributions/Univariate/CompetingRisks.cs b/Numerics/Distributions/Univariate/CompetingRisks.cs index 524bfb13..a188353d 100644 --- a/Numerics/Distributions/Univariate/CompetingRisks.cs +++ b/Numerics/Distributions/Univariate/CompetingRisks.cs @@ -82,11 +82,17 @@ private sealed class WeibullConfiguration /// Publishes the lazily computed step atomically for concurrent readers of unchanged state. private sealed class DensityStep { + /// The cached finite-difference step in physical coordinates. internal readonly double Value; + + /// Initializes an immutable derivative-step publication. + /// The component-derived finite-difference step. internal DensityStep(double value) { Value = value; } } /// Returns a previously computed component-derived step, excluding observation-dependent fallbacks. + /// The cached derivative step, or zero when no step has been published. + /// when a component-derived step is available; otherwise, . internal bool TryGetDensityStep(out double step) { var cached = Volatile.Read(ref _densityStep); @@ -95,8 +101,11 @@ internal bool TryGetDensityStep(out double step) } /// Stores the unchanged derivative-step expression for this exact component configuration. + /// The component-derived finite-difference step to publish. internal void CacheDensityStep(double step) => Volatile.Write(ref _densityStep, new DensityStep(step)); + /// Captures the mutable state that affects the exact built-in Weibull evaluation path. + /// The competing-risks distribution whose current state is captured. private WeibullConfiguration(CompetingRisks owner) { _minimum = owner.MinimumOfRandomVariables; @@ -125,6 +134,8 @@ private WeibullConfiguration(CompetingRisks owner) } /// Captures only exact built-in Weibulls; derived and custom XML callbacks retain the generic path. + /// The competing-risks distribution to inspect. + /// An immutable snapshot for an exact built-in Weibull configuration, or when the optimized path is not applicable. internal static WeibullConfiguration? Capture(CompetingRisks owner) { if (owner._distributions is null) return null; @@ -134,6 +145,8 @@ private WeibullConfiguration(CompetingRisks owner) } /// Compares live values without allocating wrappers, parameter arrays, or XML. + /// The competing-risks distribution whose live state is compared with this snapshot. + /// when every captured scalar, Weibull parameter, and correlation entry is bitwise unchanged; otherwise, . internal bool Matches(CompetingRisks owner) { if (owner._distributions is null || owner._distributions.Length != _parameterBits.Length / 2 @@ -488,6 +501,7 @@ public IUnivariateDistribution Bootstrap(ParameterEstimationMethod estimationMet /// Set the distribution parameters. /// /// The competing distributions. + /// is . public void SetParameters(UnivariateDistributionBase[] distributions) { if (distributions == null) throw new ArgumentNullException(nameof(Distributions)); @@ -502,6 +516,8 @@ public void SetParameters(UnivariateDistributionBase[] distributions) /// Set the distribution parameters. /// /// The competing distributions. + /// is . + /// An element does not derive from . public void SetParameters(IUnivariateDistribution[] distributions) { if (distributions == null) throw new ArgumentNullException(nameof(Distributions)); @@ -665,6 +681,9 @@ public override double LogPDF(double x) } /// Combines endpoint tail exponents before evaluating the one-sided density limit. + /// The finite support endpoint at which the independent log-density limit is required. + /// The logarithm of the endpoint density limit, including positive or negative infinity. + /// A component with a zero endpoint tail has no recognized analytical expansion. /// For a product tail c*t^a*log(1/t)^b, its density tends to zero for a>1, /// infinity for a<1, and c times the logarithmic limit for a=1. private double IndependentEndpointLogDensity(double x) @@ -699,6 +718,7 @@ private void ValidateEvaluation() /// Upper support bound already read by the caller. /// Whether exact built-in Weibulls permit reuse of the caller's support bounds. /// The resolved finite CDF derivative. + /// No positive floating-point stencil can be formed, or numerical differentiation produces an invalid boundary density. /// A centered local step is used in the interior; endpoints use a one-sided /// step. Finite negative interior slopes are returned for candidate rejection by /// . Negative boundary or nonfinite density remains a failure. @@ -716,9 +736,9 @@ private double DependentDensity(double x, double minimum, double maximum, out bo foreach (var distribution in Distributions) { double width = distribution.InverseCDF(.75) - distribution.InverseCDF(.25); - if (width > 0 && DistributionNumerics.IsFinite(width)) scale = Math.Min(scale, width); + if (width > 0 && Tools.IsFinite(width)) scale = Math.Min(scale, width); } - bool componentScale = DistributionNumerics.IsFinite(scale); + bool componentScale = Tools.IsFinite(scale); if (!componentScale) scale = Math.Max(1, Math.Abs(x)); step = Math.Pow(Tools.DoubleMachineEpsilon, 1.0 / 3) * scale; if (componentScale && configuration is not null) configuration.CacheDensityStep(step); @@ -730,7 +750,7 @@ private double DependentDensity(double x, double minimum, double maximum, out bo double density = reuseBounds ? (DependentCDFCore(right) - DependentCDFCore(left)) / (right - left) : (CDF(right) - CDF(left)) / (right - left); - if (!DistributionNumerics.IsFinite(density) || (density < 0 && (x == (reuseBounds ? minimum : Minimum) || x == (reuseBounds ? maximum : Maximum)))) + if (!Tools.IsFinite(density) || (density < 0 && (x == (reuseBounds ? minimum : Minimum) || x == (reuseBounds ? maximum : Maximum)))) throw new InvalidOperationException("Numerical differentiation of the dependent CDF did not produce a nonnegative finite density."); return density; } @@ -866,13 +886,13 @@ public override double InverseCDF(double probability) { double reference = minX / 2 + maxX / 2; double scale = maxX / 2 - minX / 2; - if (!(scale > 0) || !DistributionNumerics.IsFinite(scale)) + if (!(scale > 0) || !Tools.IsFinite(scale)) scale = Distributions.Select(d => d.InverseCDF(.75) - d.InverseCDF(.25)) - .Where(width => width > 0 && DistributionNumerics.IsFinite(width)).DefaultIfEmpty(1).Min(); + .Where(width => width > 0 && Tools.IsFinite(width)).DefaultIfEmpty(1).Min(); double Argument(double t) { double value = reference + scale * t; - if (double.IsInfinity(value) && DistributionNumerics.IsFinite(t)) value = scale * (reference / scale + t); + if (double.IsInfinity(value) && Tools.IsFinite(t)) value = scale * (reference / scale + t); return Math.Max(Minimum, Math.Min(Maximum, value)); } double Residual(double t) => probability <= .5 ? LogCDF(Argument(t)) - Math.Log(probability) @@ -898,6 +918,7 @@ double Residual(double t) => probability <= .5 ? LogCDF(Argument(t)) - Math.Log( /// Returns a list of cumulative incidence functions. /// /// Optional. The stratification bins to integrate over. Default is 200 bins. + /// One empirical cumulative-incidence function for each competing distribution. public List CumulativeIncidenceFunctions(List? bins = null) { RefreshCachedConfiguration(); diff --git a/Numerics/Distributions/Univariate/Exponential.cs b/Numerics/Distributions/Univariate/Exponential.cs index 4ce23181..4c8a23c1 100644 --- a/Numerics/Distributions/Univariate/Exponential.cs +++ b/Numerics/Distributions/Univariate/Exponential.cs @@ -252,6 +252,8 @@ public override void SetParameters(IList parameters) /// /// A list of parameters. /// Determines whether to throw an exception or not. + /// when the parameter vector is valid; otherwise, the validation exception. + /// is and the parameter count, location, or scale is invalid. public override ArgumentOutOfRangeException? ValidateParameters(IList parameters, bool throwException) { if (parameters == null || parameters.Count != NumberOfParameters) @@ -482,7 +484,7 @@ public override double InverseCDF(double probability) ValidateParameters([Xi, Alpha], true); double unitQuantile = -Tools.Log1p(-probability); double displacement = Alpha * unitQuantile; - return double.IsInfinity(displacement) && DistributionNumerics.IsFinite(unitQuantile) + return double.IsInfinity(displacement) && Tools.IsFinite(unitQuantile) ? Alpha * (Xi / Alpha + unitQuantile) : Xi + displacement; } diff --git a/Numerics/Distributions/Univariate/GammaDistribution.cs b/Numerics/Distributions/Univariate/GammaDistribution.cs index acfeb923..a6cc2e32 100644 --- a/Numerics/Distributions/Univariate/GammaDistribution.cs +++ b/Numerics/Distributions/Univariate/GammaDistribution.cs @@ -565,6 +565,9 @@ double logLH(double[] x) /// Estimates parameters using a Newton-Raphson method. /// /// Array of sample data. + /// is . + /// The sample is insufficient, constant, or contains a nonpositive or nonfinite observation. + /// The transformed observation statistic is not-a-number. public void MLE_NR(IList sample) { DistributionNumerics.ValidateSample(sample, 2, true); @@ -683,6 +686,8 @@ public override double InverseCDF(double probability) /// Returns the inverse CDF using the modified Wilson-Hilferty transformation. /// /// Probability between 0 and 1. + /// The approximate gamma quantile in physical coordinates. + /// The probability is outside the closed unit interval or the distribution parameters are invalid. /// /// Cornish-Fisher transformation (Fisher and Cornish, 1960) for abs(skew) less than or equal to 2. If abs(skew) > 2 then use Modified Wilson-Hilferty transformation (Kirby,1972). /// @@ -708,11 +713,12 @@ public double WilsonHilfertyInverseCDF(double probability) /// Coefficient of skewness. /// Probability between 0 and 1. /// The named approximate frequency factor, not the actual Gamma quantile. + /// is nonfinite or is outside the closed unit interval or not-a-number. /// Large skew magnitude is capped at 9.75 before evaluating its powers. Negative /// skew uses the reflected probability and sign of the corresponding positive-skew approximation. public static double FrequencyFactorKp(double skewness, double probability) { - if (!DistributionNumerics.IsFinite(skewness)) throw new ArgumentOutOfRangeException(nameof(skewness)); + if (!Tools.IsFinite(skewness)) throw new ArgumentOutOfRangeException(nameof(skewness)); if (!(probability >= 0 && probability <= 1)) throw new ArgumentOutOfRangeException(nameof(probability)); double C = skewness; double absC = Math.Abs(C); @@ -794,9 +800,10 @@ public static double FrequencyFactorKp(double skewness, double probability) /// Coefficient of skewness. /// Probability between 0 and 1. /// The partial derivative of the frequency factor with respect to skewness. + /// is nonfinite or is not finite and strictly between zero and one. public static double PartialKp(double skewness, double probability) { - if (!DistributionNumerics.IsFinite(skewness)) throw new ArgumentOutOfRangeException(nameof(skewness)); + if (!Tools.IsFinite(skewness)) throw new ArgumentOutOfRangeException(nameof(skewness)); DistributionNumerics.ValidateProbability(probability); double C = skewness; double absC = Math.Abs(C); @@ -901,6 +908,9 @@ public override UnivariateDistributionBase Clone() /// Wilson-Hilferty and frequency-factor approximations are not used here. An algebraically /// equivalent sum of two nonnegative terms preserves the mean-direction variance at large /// shape; physical scale is restored in logarithms. Probability must be finite and strictly interior. + /// The probability, sample size, or distribution parameters are invalid. + /// is not maximum likelihood or method of moments. + /// A gamma tail or quantile calculation does not converge. public double QuantileVariance(double probability, int sampleSize, ParameterEstimationMethod estimationMethod) { DistributionNumerics.ValidateProbability(probability); diff --git a/Numerics/Distributions/Univariate/GeneralizedExtremeValue.cs b/Numerics/Distributions/Univariate/GeneralizedExtremeValue.cs index 2706226a..9ff6ff86 100644 --- a/Numerics/Distributions/Univariate/GeneralizedExtremeValue.cs +++ b/Numerics/Distributions/Univariate/GeneralizedExtremeValue.cs @@ -591,7 +591,7 @@ private Tuple GetRobustParameterConstraints(IList< lowerVals[2] = -10; upperVals[2] = 10d; // Correct initial value of kappa if necessary - if (!DistributionNumerics.IsFinite(initialVals[2]) || initialVals[2] <= lowerVals[2] || initialVals[2] >= upperVals[2]) + if (!Tools.IsFinite(initialVals[2]) || initialVals[2] <= lowerVals[2] || initialVals[2] >= upperVals[2]) { initialVals[2] = 0d; } @@ -679,6 +679,8 @@ public override double LogCCDF(double x) } /// Maps an interior observation to its Gumbel coordinate using the exact nonzero shape. + /// The observation in physical coordinates. + /// The corresponding Gumbel coordinate. private double TransformedValue(double x) { return DistributionNumerics.HoskingShapeTransform(x, Xi, Alpha, Kappa); @@ -701,7 +703,7 @@ public override double InverseCDF(double probability) double product = Kappa * logarithm; double unitQuantile = double.IsNegativeInfinity(product) ? 1 / Kappa : DistributionNumerics.ScaledExprelProduct(1, -logarithm, product); double displacement = double.IsNegativeInfinity(product) ? Alpha / Kappa : DistributionNumerics.ScaledExprelProduct(Alpha, -logarithm, product); - return double.IsInfinity(displacement) && DistributionNumerics.IsFinite(unitQuantile) + return double.IsInfinity(displacement) && Tools.IsFinite(unitQuantile) ? Alpha * (Xi / Alpha + unitQuantile) : Xi + displacement; } @@ -728,7 +730,7 @@ public Matrix ExpectedInformationMatrix(int sampleSize) double logarithm = Math.Log(Math.Abs(value)) + Math.Log(sampleSize) - (i < 2 ? Math.Log(Alpha) : 0) - (j < 2 ? Math.Log(Alpha) : 0); value = Math.Sign(value) * Math.Exp(logarithm); - if (!DistributionNumerics.IsFinite(value)) + if (!Tools.IsFinite(value)) throw new InvalidOperationException("Expected information is outside the finite floating-point range."); } information[i, j] = information[j, i] = value; @@ -802,6 +804,8 @@ public double QuantileVariance(double probability, int sampleSize, ParameterEsti } /// Returns log Var(T^power), T unit exponential, without forming raw gamma moments. + /// The real exponent, established by the caller to be greater than negative one-half. + /// The logarithm of the variance of a unit-exponential variate raised to , including positive infinity when the second moment overflows. /// The caller establishes power > -1/2 and uses a divided series near zero. internal static double LogPowerVariance(double power) { @@ -811,6 +815,7 @@ internal static double LogPowerVariance(double power) } /// Evaluates analytical standardized central moments with log-Gamma divided differences near zero shape. + /// The standardized mean shift, standard deviation, skewness, and kurtosis in that order. /// Finite differences of log Gamma remove the cancelling powers before evaluation. /// Exponential polynomial identities then retain the second through fourth centered moments. /// The exact zero is the analytical Gumbel limit; every nonzero kappa remains in the series. @@ -838,6 +843,9 @@ private double[] SmallShapeStandardizedMoments() } /// Returns the order-r forward difference of log Gamma(1+j*kappa), divided by kappa to power r. + /// The nonzero shape increment. + /// The forward-difference order, expected to be two, three, or four. + /// The normalized log-gamma forward difference. private static double NormalizedLogGammaDifference(double kappa, int order) { double sum = 0, power = 1; diff --git a/Numerics/Distributions/Univariate/GeneralizedLogistic.cs b/Numerics/Distributions/Univariate/GeneralizedLogistic.cs index cbd84ab9..1a9d63d3 100644 --- a/Numerics/Distributions/Univariate/GeneralizedLogistic.cs +++ b/Numerics/Distributions/Univariate/GeneralizedLogistic.cs @@ -221,6 +221,7 @@ public override double Kurtosis private static readonly double[] FourthCoefficients = BuildMomentCoefficients(4); /// Coefficients of pi*k/sin(pi*k) as a power series in k squared. + /// The reciprocal-sinc series coefficients indexed by powers of squared shape. private static double[] BuildReciprocalCoefficients() { var sinc = new double[15]; @@ -235,6 +236,9 @@ private static double[] BuildReciprocalCoefficients() } /// Multiplies truncated power series used to remove exact central-moment zeros algebraically. + /// The first coefficient vector. + /// The second coefficient vector of the same length. + /// The product truncated to the input vector length. private static double[] Multiply(double[] left, double[] right) { var result = new double[left.Length]; @@ -244,6 +248,8 @@ private static double[] Multiply(double[] left, double[] right) } /// Forms central-moment numerator coefficients before evaluation, avoiding cancellation near zero shape. + /// The central-moment order, expected to be two, three, or four. + /// The numerator coefficients indexed by powers of squared shape. private static double[] BuildMomentCoefficients(int order) { var b1 = ReciprocalCoefficients; @@ -267,6 +273,10 @@ private static double[] BuildMomentCoefficients(int order) } /// Horner evaluation after dividing out the exact leading power of kappa squared. + /// The power-series coefficients. + /// The squared shape at which to evaluate the reduced series. + /// The first coefficient retained after removing the exact leading zero. + /// The reduced polynomial value. private static double Polynomial(double[] coefficients, double squaredShape, int first) { double value = 0; @@ -275,14 +285,20 @@ private static double Polynomial(double[] coefficients, double squaredShape, int } /// Returns pi*k/sin(pi*k), including its removable singularity. + /// The generalized-logistic shape. + /// pi*k/sin(pi*k), using its power series near zero. private static double ReciprocalSinc(double k) => Math.Abs(k) <= .05 ? 1 + k * k * Polynomial(ReciprocalCoefficients, k * k, 1) : Math.PI * k / Math.Sin(Math.PI * k); /// Returns the standardized mean shift without subtracting nearly equal raw moments. + /// The generalized-logistic shape. + /// The standardized mean displacement from location. private static double StandardMean(double k) => Math.Abs(k) <= .05 ? -k * Polynomial(ReciprocalCoefficients, k * k, 1) : (1 - ReciprocalSinc(k)) / k; /// Returns standardized variance after dividing out its exact kappa-squared zero. + /// The generalized-logistic shape. + /// The standardized variance. private static double StandardVariance(double k) => Math.Abs(k) <= .05 ? Polynomial(VarianceCoefficients, k * k, 1) : (ReciprocalSinc(2 * k) - Math.Pow(ReciprocalSinc(k), 2)) / k / k; @@ -424,11 +440,14 @@ public override void SetParameters(IList parameters) /// Gets the parameters using the direct method of moments. Moments are derived from the real-space data. /// /// The array of sample moments. + /// The location, scale, and shape derived from the supplied product moments. + /// is . + /// The moment vector is too short or contains an invalid mean, dispersion, or skewness. public double[] DirectMethodOfMoments(IList moments) { if (moments == null) throw new ArgumentNullException(nameof(moments)); - if (moments.Count < 3 || !DistributionNumerics.IsFinite(moments[0]) || !DistributionNumerics.IsFinite(moments[1]) - || moments[1] <= 0 || !DistributionNumerics.IsFinite(moments[2])) + if (moments.Count < 3 || !Tools.IsFinite(moments[0]) || !Tools.IsFinite(moments[1]) + || moments[1] <= 0 || !Tools.IsFinite(moments[2])) throw new ArgumentOutOfRangeException(nameof(moments)); double k = SolveForKappa(moments[2]); double a = moments[1] / Math.Sqrt(StandardVariance(k)); @@ -454,9 +473,10 @@ public double[] MomentsFromParameters(IList parameters) /// /// Kappa /// + /// is nonfinite. public double SolveForKappa(double skew) { - if (!DistributionNumerics.IsFinite(skew)) throw new ArgumentOutOfRangeException(nameof(skew)); + if (!Tools.IsFinite(skew)) throw new ArgumentOutOfRangeException(nameof(skew)); if (skew == 0) return 0; if (Math.Abs(skew) >= 10) return double.NaN; // Retain Brent and its convergence settings, evaluating finite moment values inside the open moment domain. @@ -467,8 +487,8 @@ public double SolveForKappa(double skew) public double[] ParametersFromLinearMoments(IList moments) { if (moments == null) throw new ArgumentNullException(nameof(moments)); - if (moments.Count < 3 || !DistributionNumerics.IsFinite(moments[0]) || !DistributionNumerics.IsFinite(moments[1]) - || moments[1] <= 0 || !DistributionNumerics.IsFinite(moments[2]) || Math.Abs(moments[2]) >= 1) + if (moments.Count < 3 || !Tools.IsFinite(moments[0]) || !Tools.IsFinite(moments[1]) + || moments[1] <= 0 || !Tools.IsFinite(moments[2]) || Math.Abs(moments[2]) >= 1) throw new ArgumentOutOfRangeException(nameof(moments)); double kappa = -moments[2]; double alpha = moments[1] / ReciprocalSinc(kappa); @@ -558,6 +578,8 @@ private double[] LegacyConstraintParametersFromLinearMoments(IList momen /// Handles samples whose legacy initialization or bounds are not finite or outside ordered bounds. /// The validated observations. /// Finite initial values and bounds from the hardened initialization path. + /// The sample is invalid, insufficient, or constant. + /// No finite supported initializer can be placed within finite bounds. private Tuple GetRobustParameterConstraints(IList sample) { DistributionNumerics.ValidateSample(sample, 4); @@ -576,7 +598,7 @@ private Tuple GetRobustParameterConstraints(IList< initialVals[0] *= magnitude; initialVals[1] *= magnitude; var candidate = new GeneralizedLogistic(initialVals[0], initialVals[1], initialVals[2]); - if (!candidate.ParametersValid || !DistributionNumerics.IsFinite(candidate.LogLikelihood(sample))) + if (!candidate.ParametersValid || !Tools.IsFinite(candidate.LogLikelihood(sample))) initialVals = [moments[0] * magnitude, moments[1] * magnitude, 0]; // Get bounds of location double locationMagnitude = Math.Max(Math.Abs(initialVals[0]), initialVals[1]); @@ -594,7 +616,7 @@ private Tuple GetRobustParameterConstraints(IList< initialVals[2] = 0d; } candidate.SetParameters(initialVals); - if (!candidate.ParametersValid || !DistributionNumerics.IsFinite(candidate.LogLikelihood(sample)) + if (!candidate.ParametersValid || !Tools.IsFinite(candidate.LogLikelihood(sample)) || initialVals[0] <= lowerVals[0] || initialVals[0] >= upperVals[0] || initialVals[1] <= lowerVals[1] || initialVals[1] >= upperVals[1]) throw new InvalidOperationException("The sample does not admit a finite supported generalized-logistic initializer within finite bounds."); @@ -622,12 +644,14 @@ double logLH(double[] x) solver.Maximize(); if (solver.Status != OptimizationStatus.Success || ValidateParameters(solver.BestParameterSet.Values, false) != null - || !DistributionNumerics.IsFinite(new GeneralizedLogistic(solver.BestParameterSet.Values[0], solver.BestParameterSet.Values[1], solver.BestParameterSet.Values[2]).LogLikelihood(sample))) + || !Tools.IsFinite(new GeneralizedLogistic(solver.BestParameterSet.Values[0], solver.BestParameterSet.Values[1], solver.BestParameterSet.Values[2]).LogLikelihood(sample))) throw new InvalidOperationException($"Generalized logistic maximum likelihood estimation failed with optimizer status {solver.Status} or a nonfinite fit."); return solver.BestParameterSet.Values; } /// Retains decimal-order fitting bounds without overflow or a zero-centered collapse. + /// The positive magnitude from which to select the next decimal order. + /// The next decimal-order bound, capped at the largest finite binary64 value. private static double FiniteDecimalBound(double value) { double bound = Math.Pow(10, Math.Ceiling(Math.Log10(value)) + 1); @@ -635,6 +659,7 @@ private static double FiniteDecimalBound(double value) } /// Retains a finite support endpoint when an intermediate scale/shape quotient overflows. + /// The finite-shape support endpoint in physical coordinates. private double FiniteShapeEndpoint() { double shift = Alpha / Kappa; @@ -643,6 +668,8 @@ private double FiniteShapeEndpoint() } /// Inverts the exact Hosking transformation, including compensated endpoint residuals. + /// An observation inside the distribution support. + /// The corresponding standard logistic variate. private double LatentLogistic(double x) { double y = DistributionNumerics.Standardize(x, Xi, Alpha); @@ -708,6 +735,8 @@ public override double InverseCDF(double probability) } /// Combines shape exponentials and physical scale before exponentiation or affine addition. + /// The standard logistic quantile. + /// The corresponding quantile in physical coordinates. private double QuantileAtLatent(double z) { double v = -Kappa * z; @@ -716,10 +745,10 @@ private double QuantileAtLatent(double z) double offset = v > 50 ? -Math.Sign(Kappa) * Math.Exp(Math.Log(Alpha) + v - Math.Log(Math.Abs(Kappa))) : Alpha * standard; double value = Xi + offset; - if (double.IsInfinity(value) && DistributionNumerics.IsFinite(standard)) + if (double.IsInfinity(value) && Tools.IsFinite(standard)) { double combined = Xi / Alpha + standard; - if (DistributionNumerics.IsFinite(combined)) return Alpha * combined; + if (Tools.IsFinite(combined)) return Alpha * combined; } return value; } diff --git a/Numerics/Distributions/Univariate/GeneralizedNormal.cs b/Numerics/Distributions/Univariate/GeneralizedNormal.cs index 767d82be..f219e2a7 100644 --- a/Numerics/Distributions/Univariate/GeneralizedNormal.cs +++ b/Numerics/Distributions/Univariate/GeneralizedNormal.cs @@ -350,8 +350,8 @@ public override void SetParameters(IList parameters) public double[] ParametersFromLinearMoments(IList moments) { if (moments == null) throw new ArgumentNullException(nameof(moments)); - if (moments.Count < 3 || !DistributionNumerics.IsFinite(moments[0]) || !DistributionNumerics.IsFinite(moments[1]) - || moments[1] <= 0 || !DistributionNumerics.IsFinite(moments[2]) || Math.Abs(moments[2]) >= 1) + if (moments.Count < 3 || !Tools.IsFinite(moments[0]) || !Tools.IsFinite(moments[1]) + || moments[1] <= 0 || !Tools.IsFinite(moments[2]) || Math.Abs(moments[2]) >= 1) throw new ArgumentOutOfRangeException(nameof(moments), "Finite L-moments require positive L-scale and absolute L-skewness below one."); double L1 = moments[0]; double L2 = moments[1]; @@ -465,6 +465,8 @@ private double[] LegacyConstraintParametersFromLinearMoments(IList momen /// Handles samples whose legacy initialization or bounds are not finite or outside ordered bounds. /// The validated observations. /// Finite initial values and bounds from the hardened initialization path. + /// The sample is invalid, insufficient, or constant. + /// No finite supported initializer can be placed within finite bounds. private Tuple GetRobustParameterConstraints(IList sample) { DistributionNumerics.ValidateSample(sample, 4); @@ -482,7 +484,7 @@ private Tuple GetRobustParameterConstraints(IList< initialVals[0] *= magnitude; initialVals[1] *= magnitude; var candidate = new GeneralizedNormal(initialVals[0], initialVals[1], initialVals[2]); - if (!candidate.ParametersValid || !DistributionNumerics.IsFinite(candidate.LogLikelihood(sample))) + if (!candidate.ParametersValid || !Tools.IsFinite(candidate.LogLikelihood(sample))) initialVals = [moments[0] * magnitude, moments[1] * Math.Sqrt(Math.PI) * magnitude, 0]; // Get bounds of location double locationMagnitude = Math.Max(Math.Abs(initialVals[0]), initialVals[1]); @@ -500,7 +502,7 @@ private Tuple GetRobustParameterConstraints(IList< initialVals[2] = 0d; } candidate.SetParameters(initialVals); - if (!candidate.ParametersValid || !DistributionNumerics.IsFinite(candidate.LogLikelihood(sample)) + if (!candidate.ParametersValid || !Tools.IsFinite(candidate.LogLikelihood(sample)) || initialVals[0] <= lowerVals[0] || initialVals[0] >= upperVals[0] || initialVals[1] <= lowerVals[1] || initialVals[1] >= upperVals[1]) throw new InvalidOperationException("The sample does not admit a finite supported generalized-normal initializer within finite bounds."); @@ -528,12 +530,14 @@ double logLH(double[] x) solver.Maximize(); if (solver.Status != OptimizationStatus.Success || ValidateParameters(solver.BestParameterSet.Values, false) != null - || !DistributionNumerics.IsFinite(new GeneralizedNormal(solver.BestParameterSet.Values[0], solver.BestParameterSet.Values[1], solver.BestParameterSet.Values[2]).LogLikelihood(sample))) + || !Tools.IsFinite(new GeneralizedNormal(solver.BestParameterSet.Values[0], solver.BestParameterSet.Values[1], solver.BestParameterSet.Values[2]).LogLikelihood(sample))) throw new InvalidOperationException($"Generalized normal maximum likelihood estimation failed with optimizer status {solver.Status} or a nonfinite fit."); return solver.BestParameterSet.Values; } /// Analytical shifted-lognormal moments, preserving representable scale products. + /// The mean, standard deviation, skewness, and kurtosis in that order. + /// The distribution parameters are invalid. private double[] AnalyticalMoments() { if (!_parametersValid) ValidateParameters(Xi, Alpha, Kappa, true); @@ -561,6 +565,8 @@ private double[] AnalyticalMoments() } /// Returns unit-scale L-scale with the exact kappa=0 normal limit. + /// The generalized-normal shape. + /// The unit-scale second L-moment. private static double NormalLScale(double k) { double v = k * k; @@ -580,6 +586,8 @@ private static double NormalLScale(double k) } /// Retains the existing decimal-order bound construction while preventing overflow. + /// The positive magnitude from which to select the next decimal order. + /// The next decimal-order bound, capped at the largest finite binary64 value. private static double FiniteDecimalBound(double value) { double bound = Math.Pow(10, Math.Ceiling(Math.Log10(value)) + 1); @@ -587,6 +595,7 @@ private static double FiniteDecimalBound(double value) } /// Retains a finite support endpoint when an intermediate scale/shape quotient overflows. + /// The finite-shape support endpoint in physical coordinates. private double FiniteShapeEndpoint() { double shift = Alpha / Kappa; @@ -595,6 +604,8 @@ private double FiniteShapeEndpoint() } /// Inverts the Hosking shape transform, retaining its exact nonzero shape and support residual. + /// An observation inside the distribution support. + /// The corresponding standard Normal variate. private double LatentNormal(double x) { double y = DistributionNumerics.Standardize(x, Xi, Alpha); @@ -654,6 +665,8 @@ public override double InverseCDF(double probability) } /// Combines shape exponentials and physical scale before exponentiation or affine addition. + /// The standard Normal quantile. + /// The corresponding quantile in physical coordinates. private double QuantileAtLatent(double z) { double v = -Kappa * z; @@ -662,10 +675,10 @@ private double QuantileAtLatent(double z) double offset = v > 50 ? -Math.Sign(Kappa) * Math.Exp(Math.Log(Alpha) + v - Math.Log(Math.Abs(Kappa))) : Alpha * standard; double value = Xi + offset; - if (double.IsInfinity(value) && DistributionNumerics.IsFinite(standard)) + if (double.IsInfinity(value) && Tools.IsFinite(standard)) { double combined = Xi / Alpha + standard; - if (DistributionNumerics.IsFinite(combined)) return Alpha * combined; + if (Tools.IsFinite(combined)) return Alpha * combined; } return value; } @@ -694,13 +707,13 @@ private double QuantileAtLatent(double z) covariance[1, 1] = shapedScale * shapedScale + scale * scale / 2; covariance[2, 2] = (Kappa / 2 / sampleSize) * Kappa + inverseR / sampleSize; covariance[0, 1] = -scale * shapedScale; - if (!DistributionNumerics.IsFinite(covariance[0, 1]) || covariance[0, 1] == 0) + if (!Tools.IsFinite(covariance[0, 1]) || covariance[0, 1] == 0) covariance[0, 1] = -Math.Sign(Kappa) * Math.Exp(2 * logAlpha + logK - logN); covariance[0, 2] = c * inverseR * Alpha / sampleSize; - if (c > 0 && (covariance[0, 2] == 0 || !DistributionNumerics.IsFinite(covariance[0, 2]))) + if (c > 0 && (covariance[0, 2] == 0 || !Tools.IsFinite(covariance[0, 2]))) covariance[0, 2] = Math.Exp(logAlpha + Math.Log(c) + logInverseR - logN); covariance[1, 2] = Alpha * Kappa / 2 / sampleSize; - if (!DistributionNumerics.IsFinite(covariance[1, 2]) || covariance[1, 2] == 0) + if (!Tools.IsFinite(covariance[1, 2]) || covariance[1, 2] == 0) covariance[1, 2] = Math.Sign(Kappa) * Math.Exp(logAlpha + logK - Math.Log(2) - logN); covariance[1, 0] = covariance[0, 1]; covariance[2, 0] = covariance[0, 2]; @@ -709,6 +722,10 @@ private double QuantileAtLatent(double z) } /// Evaluates the closed-form information factors, retaining log(1/R) after underflow. + /// The generalized-normal shape. + /// The returned exponential divided-difference factor. + /// The returned reciprocal information residual, including natural underflow to zero. + /// The natural logarithm of , retained even when the value underflows. private static void NormalInformationFactors(double kappa, out double c, out double inverseR, out double logInverseR) { double v = kappa * kappa; diff --git a/Numerics/Distributions/Univariate/GeneralizedPareto.cs b/Numerics/Distributions/Univariate/GeneralizedPareto.cs index 151f0391..83ba3f39 100644 --- a/Numerics/Distributions/Univariate/GeneralizedPareto.cs +++ b/Numerics/Distributions/Univariate/GeneralizedPareto.cs @@ -559,7 +559,7 @@ private Tuple GetRobustParameterConstraints(IList< lowerVals[2] = -10d; upperVals[2] = 10d; // Correct initial values if necessary - if (!DistributionNumerics.IsFinite(initialVals[2]) || initialVals[2] <= lowerVals[2] || initialVals[2] >= upperVals[2]) + if (!Tools.IsFinite(initialVals[2]) || initialVals[2] <= lowerVals[2] || initialVals[2] >= upperVals[2]) { initialVals[2] = 0d; } @@ -610,6 +610,8 @@ public override double LogPDF(double x) } /// Maps an interior observation to its exponential coordinate without a shape-zero plateau. + /// The observation in physical coordinates. + /// The corresponding unit-exponential coordinate. private double TransformedValue(double x) { return DistributionNumerics.HoskingShapeTransform(x, Xi, Alpha, Kappa); @@ -653,7 +655,7 @@ public override double InverseCDF(double probability) double product = Kappa * logarithm; double unitQuantile = double.IsNegativeInfinity(product) ? 1 / Kappa : DistributionNumerics.ScaledExprelProduct(1, -logarithm, product); double displacement = double.IsNegativeInfinity(product) ? Alpha / Kappa : DistributionNumerics.ScaledExprelProduct(Alpha, -logarithm, product); - return double.IsInfinity(displacement) && DistributionNumerics.IsFinite(unitQuantile) + return double.IsInfinity(displacement) && Tools.IsFinite(unitQuantile) ? Alpha * (Xi / Alpha + unitQuantile) : Xi + displacement; } diff --git a/Numerics/Distributions/Univariate/Gumbel.cs b/Numerics/Distributions/Univariate/Gumbel.cs index 163bc2d2..a133dba6 100644 --- a/Numerics/Distributions/Univariate/Gumbel.cs +++ b/Numerics/Distributions/Univariate/Gumbel.cs @@ -401,7 +401,9 @@ double logLH(double[] x) /// /// This function is used to calculate the maximum likelihood estimates of location and scale parameters. /// - /// + /// The finite, nonconstant observations used to estimate location and scale. + /// is . + /// The sample is insufficient, constant, or contains a nonfinite observation. /// /// Handbook of Statistical Distributions with Application /// @@ -531,7 +533,7 @@ public override double InverseCDF(double probability) ValidateParameters(Xi, _alpha, true); double unitQuantile = -Math.Log(-Math.Log(probability)); double displacement = Alpha * unitQuantile; - return double.IsInfinity(displacement) && DistributionNumerics.IsFinite(unitQuantile) + return double.IsInfinity(displacement) && Tools.IsFinite(unitQuantile) ? Alpha * (Xi / Alpha + unitQuantile) : Xi + displacement; } diff --git a/Numerics/Distributions/Univariate/KappaExpectedInformation.cs b/Numerics/Distributions/Univariate/KappaExpectedInformation.cs index c6aa3d77..2f16b53d 100644 --- a/Numerics/Distributions/Univariate/KappaExpectedInformation.cs +++ b/Numerics/Distributions/Univariate/KappaExpectedInformation.cs @@ -51,7 +51,7 @@ internal static class KappaExpectedInformation internal static double[,] ParameterCovariance(double alpha, double k, double h, int sampleSize, int parameterCount) { DistributionNumerics.ValidateSampleSize(sampleSize); - if (!DistributionNumerics.IsFinite(alpha) || alpha <= 0) throw new ArgumentOutOfRangeException(nameof(alpha)); + if (!Tools.IsFinite(alpha) || alpha <= 0) throw new ArgumentOutOfRangeException(nameof(alpha)); double[,] information = ExpectedInformation(k, h, parameterCount, out _, out _, out _); double[,] covariance = InvertInformation(information); for (int i = 0; i < parameterCount; i++) @@ -63,7 +63,7 @@ internal static class KappaExpectedInformation double logarithm = Math.Log(Math.Abs(value)) - Math.Log(sampleSize) + (i < 2 ? Math.Log(alpha) : 0) + (j < 2 ? Math.Log(alpha) : 0); value = Math.Sign(value) * Math.Exp(logarithm); - if (!DistributionNumerics.IsFinite(value)) + if (!Tools.IsFinite(value)) throw new InvalidOperationException("The local MLE covariance is outside the finite floating-point range."); } covariance[i, j] = covariance[j, i] = value; @@ -79,6 +79,8 @@ internal static class KappaExpectedInformation /// Estimated absolute errors of the score means. /// Estimated absolute errors of information entries. /// The symmetric per-observation standardized information matrix. + /// The shape values or requested parameter dimension are outside the regular information domain. + /// Complete-tail quadrature fails or an integrated score mean is inconsistent with zero within its numerical error. internal static double[,] ExpectedInformation(double k, double h, int parameterCount, out double[] scoreMeans, out double[] scoreMeanErrors, out double[,] informationErrors) { @@ -110,16 +112,22 @@ internal static class KappaExpectedInformation } /// Checks information regularity without changing distribution or fitting validity. + /// The finite kappa shape. + /// The finite hondo shape. + /// The requested information dimension, either three or four. + /// The dimension is unsupported, a shape is nonfinite, or the regular expected-information boundary is violated. private static void ValidateDomain(double k, double h, int count) { if (count != 3 && count != 4) throw new ArgumentOutOfRangeException(nameof(count)); - if (!DistributionNumerics.IsFinite(k) || !DistributionNumerics.IsFinite(h)) + if (!Tools.IsFinite(k) || !Tools.IsFinite(h)) throw new ArgumentOutOfRangeException(nameof(k), "Information requires finite shape parameters."); if (k >= .5 || h >= .5 || k * h >= .5) throw new ArgumentOutOfRangeException(nameof(k), "Regular information requires kappa < 1/2, hondo < 1/2 and kappa*hondo < 1/2."); } /// Evaluates expm1(a*z)/a and its second divided difference without cancellation in weighted scores. + /// The combined exponential argument a*z. + /// (expm1(x)-x)/x^2, including its continuous value one-half at zero. private static double SecondExponentialRelative(double x) { if (Math.Abs(x) >= .1) return (Tools.Expm1(x) - x) / x / x; @@ -134,9 +142,17 @@ private static double SecondExponentialRelative(double x) } /// Returns log(abs(expm1(x))) without overflowing the exponential. + /// The exponential argument. + /// The natural logarithm of the absolute value of expm1(x). private static double LogAbsoluteExpm1(double x) => x > 36 ? x + Tools.Log1p(-Math.Exp(-x)) : Math.Log(Math.Abs(Tools.Expm1(x))); /// Forms scores times the square-root integration Jacobian before multiplying exponential terms. + /// The logarithm of the lower-tail probability. + /// The logarithm of the complementary upper-tail probability. + /// The kappa shape. + /// The hondo shape. + /// The logarithm of the square root of the quadrature-coordinate Jacobian. + /// The weighted location, scale, kappa, and hondo scores in that order. private static double[] WeightedScores(double logp, double logq, double k, double h, double logroot) { double u = -logp; @@ -180,6 +196,13 @@ private static double[] WeightedScores(double logp, double logq, double k, doubl } /// Pairs the full lower and upper p tails in a common finite quadrature coordinate. + /// The finite quadrature coordinate in the open unit interval. + /// The kappa shape. + /// The hondo shape. + /// The number of score coordinates to retain. + /// The positive power used to regularize the probability-tail coordinate. + /// The paired score means followed by the upper triangle of the score cross-products. + /// A paired score or cross-product is nonfinite. private static double[] Integrand(double coordinate, double k, double h, int count, int power) { double logCoordinate = Math.Log(coordinate); @@ -195,7 +218,7 @@ private static double[] Integrand(double coordinate, double k, double h, int cou for (int i = 0; i < count; i++) for (int j = i; j < count; j++, index++) values[index] = lower[i] * lower[j] + upper[i] * upper[j]; foreach (double value in values) - if (!DistributionNumerics.IsFinite(value)) + if (!Tools.IsFinite(value)) throw new InvalidOperationException("A complete-tail information integrand could not be represented finitely."); return values; } @@ -203,12 +226,24 @@ private static double[] Integrand(double coordinate, double k, double h, int cou /// Stores a subinterval's Kronrod estimate and conservative local error indicators. private sealed class Interval { + /// The lower and upper quadrature-coordinate endpoints. internal double Lower, Upper; + + /// The Kronrod estimates for the score and information components. internal double[] Values = Array.Empty(); + + /// The conservative absolute-error estimates corresponding to . internal double[] Errors = Array.Empty(); } /// G10K21 rule with absolute-deviation rescaling and a floating-point roundoff floor. + /// The lower quadrature-coordinate endpoint. + /// The upper quadrature-coordinate endpoint. + /// The kappa shape. + /// The hondo shape. + /// The number of score coordinates to integrate. + /// The probability-tail coordinate power. + /// The interval estimates and conservative error indicators. private static Interval Evaluate(double a, double b, double k, double h, int count, int power) { double center = .5 * (a + b), half = .5 * (b - a); @@ -252,6 +287,12 @@ private static Interval Evaluate(double a, double b, double k, double h, int cou } /// Refines the interval with the largest normalized error until every component converges. + /// The kappa shape. + /// The hondo shape. + /// The number of score coordinates to integrate. + /// The probability-tail coordinate power. + /// The converged complete-tail score and information estimates with accumulated errors. + /// The interval limit or floating-point subdivision resolution is exhausted before every component meets tolerance. private static Interval Integrate(double k, double h, int count, int power) { var initial = Evaluate(0, 1, k, h, count, power); @@ -294,6 +335,9 @@ private static Interval Integrate(double k, double h, int count, int power) } /// Inverts an SPD matrix with diagonal scaling and checked Cholesky solves. + /// The finite symmetric positive-definite expected-information matrix. + /// The symmetrized inverse transformed back from diagonal scaling. + /// The matrix is not numerically positive definite, an inverse entry is nonfinite, or a solve fails its residual check. private static double[,] InvertInformation(double[,] information) { int count = information.GetLength(0); @@ -302,7 +346,7 @@ private static Interval Integrate(double k, double h, int count, int power) var lower = new double[count, count]; for (int i = 0; i < count; i++) { - if (!(information[i, i] > 0) || !DistributionNumerics.IsFinite(information[i, i])) + if (!(information[i, i] > 0) || !Tools.IsFinite(information[i, i])) throw new InvalidOperationException("Expected information has a nonpositive or nonfinite diagonal."); scale[i] = Math.Sqrt(information[i, i]); } @@ -315,7 +359,7 @@ private static Interval Integrate(double k, double h, int count, int power) for (int m = 0; m < j; m++) value -= lower[i, m] * lower[j, m]; if (i == j) { - if (!(value > 0) || !DistributionNumerics.IsFinite(value)) + if (!(value > 0) || !Tools.IsFinite(value)) throw new InvalidOperationException("Expected information is not numerically positive definite."); lower[i, j] = Math.Sqrt(value); } @@ -342,7 +386,7 @@ private static Interval Integrate(double k, double h, int count, int power) { double value = 0; for (int j = 0; j < count; j++) value += normalized[i, j] * solution[j]; - if (!DistributionNumerics.IsFinite(value) || Math.Abs(value - (i == column ? 1 : 0)) > 1E-9) + if (!Tools.IsFinite(value) || Math.Abs(value - (i == column ? 1 : 0)) > 1E-9) throw new InvalidOperationException("The expected-information inverse failed its residual check."); } } @@ -350,7 +394,7 @@ private static Interval Integrate(double k, double h, int count, int power) for (int j = i; j < count; j++) { double value = .5 * (inverse[i, j] + inverse[j, i]) / scale[i] / scale[j]; - if (!DistributionNumerics.IsFinite(value)) throw new InvalidOperationException("Expected-information inversion was nonfinite."); + if (!Tools.IsFinite(value)) throw new InvalidOperationException("Expected-information inversion was nonfinite."); inverse[i, j] = inverse[j, i] = value; } return inverse; diff --git a/Numerics/Distributions/Univariate/LnNormal.cs b/Numerics/Distributions/Univariate/LnNormal.cs index 971f7a03..ee1f9cd0 100644 --- a/Numerics/Distributions/Univariate/LnNormal.cs +++ b/Numerics/Distributions/Univariate/LnNormal.cs @@ -94,6 +94,11 @@ public double Sigma } /// Validates the internal natural-log coordinates without applying physical-mean constraints. + /// The proposed finite natural-log mean. + /// The proposed finite positive natural-log standard deviation. + /// to throw the validation error; to return it. + /// when both log-coordinate parameters are valid; otherwise, the corresponding validation exception. + /// is and either log-coordinate parameter is invalid. private static ArgumentOutOfRangeException? ValidateLogParameters(double mean, double standardDeviation, bool throwException) { ArgumentOutOfRangeException? error = null; @@ -354,6 +359,9 @@ public override void SetParameters(IList parameters) /// This method was proposed by the U.S. Water Resources Council (WRC, 1967). /// /// The array of sample data. + /// The product moments of the natural-log-transformed observations. + /// is . + /// The sample is insufficient, constant, nonfinite, or contains a nonpositive observation. public static double[] IndirectMethodOfMoments(IList sample) { DistributionNumerics.ValidateSample(sample, 4, positive: true); @@ -367,6 +375,9 @@ public static double[] IndirectMethodOfMoments(IList sample) /// This method was proposed by the U.S. Water Resources Council (WRC, 1967). /// /// The array of sample data. + /// The linear moments of the natural-log-transformed observations. + /// is . + /// The sample is insufficient, constant, nonfinite, or contains a nonpositive observation. public double[] IndirectMethodOfLinearMoments(IList sample) { DistributionNumerics.ValidateSample(sample, 4, positive: true); @@ -380,6 +391,7 @@ public double[] IndirectMethodOfLinearMoments(IList sample) /// /// The real-space mean of the data. /// The real-space standard deviation of the data. + /// The natural-log mean and standard deviation, or two not-a-number values when the physical moments are invalid. public static double[] DirectMethodOfMoments(double mean, double standardDeviation) { if (!(mean > 0d) || !(standardDeviation > 0d) || double.IsInfinity(mean) || double.IsInfinity(standardDeviation)) diff --git a/Numerics/Distributions/Univariate/LogNormal.cs b/Numerics/Distributions/Univariate/LogNormal.cs index f723dee9..9b1befc8 100644 --- a/Numerics/Distributions/Univariate/LogNormal.cs +++ b/Numerics/Distributions/Univariate/LogNormal.cs @@ -343,6 +343,9 @@ public override void SetParameters(IList parameters) /// This method was proposed by the U.S. Water Resources Council (WRC, 1967). /// /// The array of sample data. + /// The product moments of the base-log-transformed observations. + /// is . + /// The sample is insufficient, constant, nonfinite, or contains a nonpositive observation. public double[] IndirectMethodOfMoments(IList sample) { DistributionNumerics.ValidateSample(sample, 4, positive: true); @@ -356,6 +359,9 @@ public double[] IndirectMethodOfMoments(IList sample) /// This method was proposed by the U.S. Water Resources Council (WRC, 1967). /// /// The array of sample data. + /// The linear moments of the base-log-transformed observations. + /// is . + /// The sample is insufficient, constant, nonfinite, or contains a nonpositive observation. public double[] IndirectMethodOfLinearMoments(IList sample) { DistributionNumerics.ValidateSample(sample, 4, positive: true); @@ -369,6 +375,7 @@ public double[] IndirectMethodOfLinearMoments(IList sample) /// /// The real-space mean of the data. /// The real-space standard deviation of the data. + /// The configured-base logarithmic mean and standard deviation, including propagated not-a-number values for invalid physical moments. public double[] DirectMethodOfMoments(double mean, double standardDeviation) { double[] natural = LnNormal.DirectMethodOfMoments(mean, standardDeviation); @@ -539,6 +546,9 @@ public override double InverseCDF(double probability) /// This is the same sampling approach as used in HEC-FDA. /// Each simulated distribution retains the configured . /// + /// A matrix with one row per quantile and one column per requested confidence percentile. + /// or is . + /// The sample size, realization count, a probability list, an individual probability, or the distribution parameters are invalid. public double[,] MonteCarloConfidenceIntervals(int sampleSize, int realizations, IList quantiles, IList percentiles) { DistributionNumerics.ValidateConfidenceInputs(sampleSize, quantiles, percentiles, 2); diff --git a/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs b/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs index 1317f618..397232bb 100644 --- a/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs +++ b/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs @@ -161,6 +161,8 @@ public override double LogCCDF(double x) } /// Log raw-moment contribution after removing the location, with each moment's existence checked separately. + /// The positive raw-moment order. + /// The logarithm of the shape-dependent raw-moment factor, or positive infinity when that moment does not exist. private double LogMomentShape(int order) { double scale = order * Sigma * Math.Log(Base), argument = Gamma * scale / 2d; @@ -176,6 +178,8 @@ private double LogMomentShape(int order) } /// Expands centered exponential moments before evaluating them, avoiding cancellation for tiny log scale. + /// The resulting standardized third central moment. + /// The resulting standardized fourth central moment. private void SmallScaleStandardizedMoments(out double skewness, out double kurtosis) { const int order = 12; @@ -200,6 +204,9 @@ private void SmallScaleStandardizedMoments(out double skewness, out double kurto } /// Multiplies equal-length truncated power series used for centered moment evaluation. + /// The first coefficient vector. + /// The second coefficient vector of the same length. + /// The product truncated to the input vector length. private static double[] MultiplySeries(double[] left, double[] right) { var result = new double[left.Length]; @@ -493,6 +500,9 @@ public override void SetParameters(IList parameters) /// This method was proposed by the U.S. Water Resources Council (WRC, 1967). /// /// The array of sample data. + /// The product moments of the base-log-transformed observations. + /// is . + /// The sample is insufficient, constant, nonfinite, or contains a nonpositive observation. public double[] IndirectMethodOfMoments(IList sample) { DistributionNumerics.ValidateSample(sample, 4, positive: true); @@ -507,6 +517,9 @@ public double[] IndirectMethodOfMoments(IList sample) /// This method was proposed by the U.S. Water Resources Council (WRC, 1967). /// /// The array of sample data. + /// The linear moments of the base-log-transformed observations. + /// is . + /// The sample is insufficient, constant, nonfinite, or contains a nonpositive observation. public double[] IndirectMethodOfLinearMoments(IList sample) { DistributionNumerics.ValidateSample(sample, 4, positive: true); @@ -781,6 +794,8 @@ public override double InverseCDF(double probability) /// Returns the inverse CDF using the modified Wilson-Hilferty transformation. /// /// Probability between 0 and 1. + /// The approximate log-Pearson type III quantile in physical coordinates. + /// The probability is outside the closed unit interval or the distribution parameters are invalid. /// /// Cornish-Fisher transformation (Fisher and Cornish, 1960) for abs(skew) less than or equal to 2. If abs(skew) > 2 then use Modified Wilson-Hilferty transformation (Kirby,1972). /// @@ -850,6 +865,8 @@ public double[] QuantileGradient(double probability) /// Returns a list of partial derivatives of X given probability with respect to each moment. /// /// Probability between 0 and 1. + /// The physical-quantile gradient in public moment coordinates. + /// The probability is not finite and strictly interior or the distribution parameters are invalid. public IList QuantileGradientForMoments(double probability) { return QuantileGradient(probability); diff --git a/Numerics/Distributions/Univariate/Mixture.cs b/Numerics/Distributions/Univariate/Mixture.cs index 084d72ad..ddd3281a 100644 --- a/Numerics/Distributions/Univariate/Mixture.cs +++ b/Numerics/Distributions/Univariate/Mixture.cs @@ -70,9 +70,15 @@ public Mixture(double[] weights, IUnivariateDistribution[] distributions) /// An immutable weight/log pair published atomically to concurrent density readers. private sealed class WeightLogEntry { + /// The exact binary representation of the cached weight. internal readonly long WeightBits; + + /// The natural logarithm associated with . internal readonly double LogValue; + /// Initializes an immutable cache entry for one exact weight value. + /// The binary64 bits of the weight read by the caller. + /// The natural logarithm of that weight. internal WeightLogEntry(long weightBits, double logValue) { WeightBits = weightBits; @@ -197,6 +203,9 @@ private static double BitDecrement(double value) } /// Gets the component log probability above zero without requiring representable probability mass. + /// The zero-based component index. + /// The component log survival probability at zero. + /// The component does not have a finite, nonpositive log probability above zero. private double PositiveLogMass(int componentIndex) { double log = Distributions[componentIndex] is Normal normal @@ -206,18 +215,34 @@ private double PositiveLogMass(int componentIndex) } /// Evaluates the positive-conditional component log density. + /// The zero-based component index. + /// The value in the component's physical coordinates. + /// The component log density conditional on a value above zero, or negative infinity when is not positive. + /// The component has no valid positive log mass. private double PositiveConditionalLogPDF(int componentIndex, double x) => x > 0 ? Distributions[componentIndex].LogPDF(x) - PositiveLogMass(componentIndex) : double.NegativeInfinity; /// Evaluates the positive-conditional component log CDF through an interval probability. + /// The zero-based component index. + /// The positive upper evaluation endpoint. + /// The log conditional probability in the interval from zero through , or negative infinity when is not positive. + /// The component has no valid positive log mass. private double PositiveConditionalLogCDF(int componentIndex, double x) => x <= 0 ? double.NegativeInfinity : Distributions[componentIndex].LogLikelihood_Intervals(0, x) - PositiveLogMass(componentIndex); /// Evaluates the positive-conditional component log survival directly. + /// The zero-based component index. + /// The physical-coordinate survival threshold. + /// Zero when is not positive; otherwise, the component log survival probability conditional on a value above zero. + /// The component has no valid positive log mass. private double PositiveConditionalLogCCDF(int componentIndex, double x) => x <= 0 ? 0 : Math.Min(0, Distributions[componentIndex].LogCCDF(x) - PositiveLogMass(componentIndex)); /// Inverts a positive-conditional component using a direct log-survival equation. + /// The zero-based component index. + /// The conditional cumulative probability in the closed unit interval. + /// The component quantile conditional on a value above zero, including its endpoint limits. + /// The component has no valid positive mass or a finite quantile bracket cannot be formed. /// Retains the existing root tolerance and iteration limit; it avoids constructing /// an unconditional probability that can round to one when positive mass is very small. private double PositiveConditionalQuantile(int componentIndex, double probability) @@ -257,6 +282,7 @@ private void RefreshCachedConfiguration() } /// Checks mutable weights and current component validity before evaluation. + /// The component collection, weights, zero weight, total mass, or a component's parameters are invalid. /// Validates live component parameters directly so evaluation does not flatten and re-slice /// the same state. Sealed Normal components delegate to their existing scalar validator without /// allocating parameter arrays; other components retain their list validator. Every component is @@ -1146,6 +1172,10 @@ public override double LogCCDF(double x) } /// Combines component interval log probabilities while retaining the hurdle atom's endpoint convention. + /// The open lower interval endpoint. + /// The closed upper interval endpoint. + /// The logarithm of the total mixture probability in (lower, upper], including the zero hurdle mass when applicable. + /// The live mixture configuration or a component interval is invalid. internal double LogIntervalProbability(double lower, double upper) { ValidateEvaluation(); diff --git a/Numerics/Distributions/Univariate/Normal.cs b/Numerics/Distributions/Univariate/Normal.cs index 4ebb73cf..879b5703 100644 --- a/Numerics/Distributions/Univariate/Normal.cs +++ b/Numerics/Distributions/Univariate/Normal.cs @@ -371,6 +371,8 @@ private Tuple GetLegacyParameterConstraints(IList< /// Handles samples whose legacy initialization or bounds are not finite or outside ordered bounds. /// The validated observations. /// Finite initial values and bounds from the hardened initialization path. + /// The sample is invalid, insufficient, or constant. + /// The sample moments are nonfinite, have no positive dispersion, or cannot be placed inside finite bounds. internal Tuple GetRobustParameterConstraints(IList sample) { DistributionNumerics.ValidateSample(sample, 4); @@ -500,6 +502,7 @@ internal double LogCCDFAtZero() /// R8_NORMAL_01_CDF_INVERSE inverts the standard normal CDF. /// /// The probability value. + /// The standard Normal quantile corresponding to . private static double r8_normal_01_cdf_inverse(double p) { //****************************************************************************80 @@ -708,6 +711,9 @@ public static double[] StandardZ(IList probabilities) /// /// References: Stedinger, J. Confidence Intervals for Design Events. Journal of Hydraulic Engineering. 1983. /// + /// A matrix with one row per quantile and one column per requested confidence percentile. + /// or is . + /// The sample size, a probability list, an individual probability, or the distribution parameters are invalid. public double[,] NormalConfidenceIntervals(int sampleSize, IList quantiles, IList percentiles) { DistributionNumerics.ValidateConfidenceInputs(sampleSize, quantiles, percentiles); @@ -743,6 +749,9 @@ public static double[] StandardZ(IList probabilities) /// /// References: Stedinger, J. Confidence Intervals for Design Events. Journal of Hydraulic Engineering. 1983. /// + /// A matrix with one row per quantile and one column per requested confidence percentile. + /// or is . + /// The sample size, a probability list, an individual probability, or the distribution parameters are invalid. public double[,] NoncentralTConfidenceIntervals(int sampleSize, IList quantiles, IList percentiles) { DistributionNumerics.ValidateConfidenceInputs(sampleSize, quantiles, percentiles, 2); @@ -777,6 +786,9 @@ public static double[] StandardZ(IList probabilities) /// /// This is the same sampling approach as used in HEC-FDA. /// + /// A matrix with one row per quantile and one column per requested confidence percentile. + /// or is . + /// The sample size, realization count, a probability list, an individual probability, or the distribution parameters are invalid. public double[,] MonteCarloConfidenceIntervals(int sampleSize, int realizations, IList quantiles, IList percentiles) { DistributionNumerics.ValidateConfidenceInputs(sampleSize, quantiles, percentiles, 2); @@ -832,6 +844,8 @@ public static double[] StandardZ(IList probabilities) /// /// The data sample size N used for computing the standard error. /// Exceedance probability. + /// The expected nonexceedance probability after accounting for sampling uncertainty. + /// is not finite and strictly interior or is less than two. public double ExpectedProbability(int sampleSize, double probability) { DistributionNumerics.ValidateProbability(probability); diff --git a/Numerics/Distributions/Univariate/PearsonTypeIII.cs b/Numerics/Distributions/Univariate/PearsonTypeIII.cs index de624393..233f38ba 100644 --- a/Numerics/Distributions/Univariate/PearsonTypeIII.cs +++ b/Numerics/Distributions/Univariate/PearsonTypeIII.cs @@ -123,6 +123,10 @@ public double Alpha public override double LogCCDF(double x) => LogTail(x, true); /// Evaluates the signed gamma tail without subtracting a rounded probability from one. + /// The observation in physical coordinates. + /// to evaluate the survival probability; to evaluate the cumulative probability. + /// The requested log probability, including support-endpoint limits. + /// The distribution parameters are invalid. private double LogTail(double x, bool upper) { if (!_parametersValid) ValidateParameters(Mu, Sigma, Gamma, true); @@ -149,10 +153,19 @@ private double LogTail(double x, bool upper) } /// Forms the unit gamma coordinate in centered form for large shape. + /// The observation in physical coordinates. + /// The standardized observation (x-mu)/sigma. + /// The corresponding nonnegative unit-scale gamma coordinate. private double UnitGammaValue(double x, double z) => UnitGammaValue(Mu, Sigma, Gamma, x, z); /// Forms the unit gamma coordinate directly from validated Pearson parameters. + /// The finite Pearson mean. + /// The finite positive Pearson standard deviation. + /// The finite Pearson skew. + /// The observation in physical coordinates. + /// The standardized observation (x-mu)/sigma. + /// The corresponding nonnegative unit-scale gamma coordinate. private static double UnitGammaValue(double mu, double sigma, double gamma, double x, double z) { if (x == mu - sigma * (2d / gamma)) return 0d; @@ -161,22 +174,32 @@ private static double UnitGammaValue(double mu, double sigma, double gamma, doub } /// Bounds local tail-expansion error while retaining every nonzero skew. + /// The standardized observation. + /// when the configured skew and observation satisfy the local tail-expansion error bound; otherwise, . private bool UseLocalTailExpansion(double z) => UseLocalTailExpansion(Gamma, z); /// Bounds local tail-expansion error directly from a validated skew. + /// The validated Pearson skew. + /// The standardized observation. + /// when the skew and observation satisfy the local tail-expansion error bound; otherwise, . private static bool UseLocalTailExpansion(double gamma, double z) { return Math.Abs(gamma) <= 1E-3 && Math.Abs(gamma) * Math.Pow(1d + Math.Abs(z), 3d) <= 1E-3; } /// Avoids cancellation in centered gamma quantiles only where the skew expansion is accurate. + /// The standard Normal quantile. + /// when the configured skew and quantile satisfy the local quantile-expansion error bound; otherwise, . private bool UseLocalQuantileExpansion(double z) { return Math.Abs(Gamma) <= 1E-3 && Math.Abs(Gamma) * Math.Pow(1d + Math.Abs(z), 3d) <= 0.02d; } /// Evaluates the smooth gamma quantile expansion and its skew derivative through cubic order. + /// The standard Normal quantile. + /// The derivative of the returned standardized quantile with respect to the configured skew. + /// The standardized Pearson quantile from the local skew expansion. private double LocalStandardQuantile(double z, out double derivative) { double z2 = z * z; @@ -689,6 +712,8 @@ public override double InverseCDF(double probability) /// Returns the inverse CDF using the modified Wilson-Hilferty transformation. /// /// Probability between 0 and 1. + /// The approximate Pearson type III quantile in physical coordinates. + /// The probability is outside the closed unit interval or the distribution parameters are invalid. /// /// Cornish-Fisher transformation (Fisher and Cornish, 1960) for abs(skew) less than or equal to 2. If abs(skew) > 2 then use Modified Wilson-Hilferty transformation (Kirby,1972). /// @@ -800,6 +825,8 @@ public double[] QuantileGradient(double probability) /// Returns a list of partial derivatives of X given probability with respect to each moment. /// /// Probability between 0 and 1. + /// The quantile gradient in public moment coordinates. + /// The probability is not finite and strictly interior or the distribution parameters are invalid. public double[] QuantileGradientForMoments(double probability) { return QuantileGradient(probability); diff --git a/Numerics/Distributions/Univariate/Weibull.cs b/Numerics/Distributions/Univariate/Weibull.cs index c40e16ec..c4017b22 100644 --- a/Numerics/Distributions/Univariate/Weibull.cs +++ b/Numerics/Distributions/Univariate/Weibull.cs @@ -338,6 +338,7 @@ private Tuple GetLegacyParameterConstraints(IList< /// Retains the established constraint initializer arithmetic for ordinary samples. /// Observations. /// The legacy initial parameter values. + /// Fewer than two observations are supplied. private double[] LegacyConstraintSolveMLE(IList samples) { double n = samples.Count; @@ -393,6 +394,8 @@ private double[] LegacyConstraintSolveMLE(IList samples) /// Handles samples whose legacy initialization or bounds are not finite or outside ordered bounds. /// The validated observations. /// Finite initial values and bounds from the hardened initialization path. + /// The sample is invalid, insufficient, constant, or produces an invalid positive parameter. + /// The initialization iteration is numerically unresolved. private Tuple GetRobustParameterConstraints(IList sample) { DistributionNumerics.ValidateSample(sample, 2, true); @@ -486,7 +489,7 @@ public double[] SolveMLE(IList samples) QofC = n * s2 / (n * s3 - s1 * s2); previousC = c; c = (c + QofC) / 2d; - if (!(c > 0) || !DistributionNumerics.IsFinite(c)) + if (!(c > 0) || !Tools.IsFinite(c)) throw new InvalidOperationException("The Weibull initialization iteration did not produce a finite positive shape."); } @@ -562,6 +565,8 @@ public override double LogCCDF(double x) } /// Forms log(x/lambda) without an overflowing or underflowing intermediate ratio. + /// The positive observation in physical coordinates. + /// The logarithm of x/lambda. private double LogStandardizedValue(double x) { double ratio = x / Lambda; diff --git a/Numerics/Utilities/Tools.cs b/Numerics/Utilities/Tools.cs index 692e561e..b63da8e7 100644 --- a/Numerics/Utilities/Tools.cs +++ b/Numerics/Utilities/Tools.cs @@ -375,6 +375,7 @@ public static double[] Denormalize(IList values, double min, double max) return result; } + /// /// Returns the standardized values. /// diff --git a/Test_Numerics/Distributions/Univariate/Test_CompetingRisksEvaluationAllocations.cs b/Test_Numerics/Distributions/Univariate/Test_CompetingRisksEvaluationAllocations.cs index 9296a2eb..6684f315 100644 --- a/Test_Numerics/Distributions/Univariate/Test_CompetingRisksEvaluationAllocations.cs +++ b/Test_Numerics/Distributions/Univariate/Test_CompetingRisksEvaluationAllocations.cs @@ -1,7 +1,7 @@ using Numerics.Data.Statistics; using Numerics.Distributions; -namespace Test_Numerics.Distributions +namespace Distributions { /// Protects live evaluation from temporary collection-wrapper allocations. [TestClass] diff --git a/Test_Numerics/Distributions/Univariate/Test_LegacyParameterConstraints.cs b/Test_Numerics/Distributions/Univariate/Test_LegacyParameterConstraints.cs index c3665161..32da24d9 100644 --- a/Test_Numerics/Distributions/Univariate/Test_LegacyParameterConstraints.cs +++ b/Test_Numerics/Distributions/Univariate/Test_LegacyParameterConstraints.cs @@ -1,6 +1,6 @@ using Numerics.Distributions; -namespace Test_Numerics.Distributions +namespace Distributions { /// Freezes valid prior envelopes and initial values from the pre-hardening baseline. [TestClass] diff --git a/Test_Numerics/Distributions/Univariate/Test_NonpositiveConstraintRegressions.cs b/Test_Numerics/Distributions/Univariate/Test_NonpositiveConstraintRegressions.cs index 9fe015d3..5a8c391d 100644 --- a/Test_Numerics/Distributions/Univariate/Test_NonpositiveConstraintRegressions.cs +++ b/Test_Numerics/Distributions/Univariate/Test_NonpositiveConstraintRegressions.cs @@ -1,6 +1,6 @@ using Numerics.Distributions; -namespace Test_Numerics.Distributions +namespace Distributions { /// Preserves usable constraint envelopes for nonpositive observations from the pre-hardening baseline. [TestClass] From 1b904505bb50fac8ff1a1eb053a7f99ad40f78c0 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 9 Sep 2026 16:31:09 -0600 Subject: [PATCH 207/222] Restore class-level test parallelism Restores the pre-serialization assembly attribute Parallelize(Scope = ExecutionScope.ClassLevel). The serialization commit replaced it with DoNotParallelize alongside MaxCpuCount=1 and TestTfmsInParallel=false; the follow-up settings change reverted the latter two but left the assembly attribute, so every method ran single-threaded per test host. Measured on the full suite (Release, VSTest, 22 logical processors): net10.0 2,850/2,850 in 1m48s parallel vs 2m46s serial (-35%); net481 2,835/2,835 in 2m20s parallel vs 3m21s serial (-30%); zero failures under parallelism on this evidence, so no per-class DoNotParallelize pins are needed yet. The wall-clock runs were taken with the unstaged configuration-snapshot working-tree changes present, which affect a handful of guard tests at millisecond scale and no test results. --- Test_Numerics/AssemblyAttributes.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Test_Numerics/AssemblyAttributes.cs b/Test_Numerics/AssemblyAttributes.cs index 3119b2d6..be2dce49 100644 --- a/Test_Numerics/AssemblyAttributes.cs +++ b/Test_Numerics/AssemblyAttributes.cs @@ -1,3 +1,3 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -[assembly: DoNotParallelize] +[assembly: Parallelize(Scope = ExecutionScope.ClassLevel)] From 645aebbe9881f254f1f4c9a0d413f0f8f4ae07de Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 9 Sep 2026 16:32:00 -0600 Subject: [PATCH 208/222] Generalize the configuration snapshot beyond all-Weibull components Adds DistributionSnapshot, an immutable bitwise capture of a distribution tree's mutable configuration whose equality implies an identical canonical configuration string: exact built-in leaves mirror their GetParameters scalar order (extended by the logarithm base on the log families and the physical-moment surface on LnNormal, which sit outside the flattened parameters), and exact Mixture/CompetingRisks nodes capture their flags, weights, seed, and correlation entries and recurse into children. Derived types and table-backed families defeat capture, so their owners retain the generic canonical-string path; a bitwise mismatch always falls back to the string comparison, which remains the deciding authority. CompetingRisks' WeibullConfiguration becomes DependentConfigurationCache around the shared snapshot, so the dependent arm stops rebuilding the canonical string on every call for any exact supported component set, and the derivative-step cache now also engages there (the step is a pure function of the pinned parameters). The fixed-support dependent fast arm stays gated on AllExactWeibull: extending DependentCDFCore reuse to other families could move boundary-stencil values and is left as a separate evidence-carrying change. Measured (2 LnNormal components, PerfectlyPositive, Release, per call): dependent CDF 31,057 B -> 136 B (-99.6%) and 4,030 ns -> 1,237 ns (-69%); LogPDF 62,362 B -> 464 B (-99.3%) and 8,995 ns -> 3,097 ns (-66%). All-Weibull behavior and results are unchanged. All 36 CompetingRisks family tests pass, and the full suites passed with these changes present: net10.0 2,850/2,850, net481 2,835/2,835. --- .../Univariate/Base/DistributionSnapshot.cs | 310 ++++++++++++++++++ .../Univariate/CompetingRisks.cs | 114 +++---- 2 files changed, 349 insertions(+), 75 deletions(-) create mode 100644 Numerics/Distributions/Univariate/Base/DistributionSnapshot.cs diff --git a/Numerics/Distributions/Univariate/Base/DistributionSnapshot.cs b/Numerics/Distributions/Univariate/Base/DistributionSnapshot.cs new file mode 100644 index 00000000..871899af --- /dev/null +++ b/Numerics/Distributions/Univariate/Base/DistributionSnapshot.cs @@ -0,0 +1,310 @@ +using System; +using System.Collections.Generic; + +namespace Numerics.Distributions +{ + /// + /// An immutable bitwise snapshot of a distribution's mutable configuration whose equality + /// implies an identical canonical configuration string. + /// + /// + /// Composite owners publish a snapshot after refreshing their caches and compare it against + /// live state on later evaluations, replacing per-call canonical-string serialization with a + /// scalar walk that allocates nothing. Capture succeeds only for exact built-in types whose + /// canonical form is fully determined by the captured scalars; derived types and + /// table-backed families return so their owners retain the generic + /// canonical-string path. A bitwise match implies the canonical configuration string is + /// unchanged; a mismatch falls back to the string comparison, which remains the deciding + /// authority, so incomplete capture can never invalidate correctly cached state. + /// + internal sealed class DistributionSnapshot + { + /// The exact runtime type of the captured distribution. + private readonly Type _type; + + /// The captured scalar state, as raw bits, in the fixed per-family order. + private readonly long[] _scalarBits; + + /// Captured child snapshots for composite nodes; otherwise . + private readonly DistributionSnapshot[]? _children; + + /// Initializes an immutable snapshot node. + /// The exact runtime type of the captured distribution. + /// The captured scalar state in the fixed per-family order. + /// Child snapshots for composite nodes, or . + private DistributionSnapshot(Type type, long[] scalarBits, DistributionSnapshot[]? children) + { + _type = type; + _scalarBits = scalarBits; + _children = children; + } + + /// Captures the mutable state that determines a distribution's canonical configuration. + /// The distribution to capture. + /// An immutable snapshot, or when any node in the tree is not an exact supported built-in. + internal static DistributionSnapshot? TryCapture(UnivariateDistributionBase? distribution) + { + if (distribution is null || !IsSupportedTree(distribution)) return null; + return Capture(distribution); + } + + /// Compares live state bitwise without allocating wrappers, parameter arrays, or strings. + /// The distribution whose live state is compared with this snapshot. + /// when every captured scalar and child is bitwise unchanged; otherwise, . + internal bool Matches(UnivariateDistributionBase? distribution) + { + if (distribution is null || distribution.GetType() != _type) return false; + var cursor = new ScalarCursor(_scalarBits); + if (!VisitScalars(distribution, ref cursor) || cursor.Index != _scalarBits.Length) return false; + if (_children is null) return true; + var children = ChildrenOf(distribution); + if (children is null || children.Length != _children.Length) return false; + for (int i = 0; i < children.Length; i++) + if (!_children[i].Matches(children[i])) return false; + return true; + } + + /// Walks the tree checking that every node is an exact supported built-in, without allocating. + /// The root of the tree to check. + /// when every node is capturable; otherwise, . + private static bool IsSupportedTree(UnivariateDistributionBase? distribution) + { + if (distribution is null) return false; + Type type = distribution.GetType(); + if (type == typeof(Mixture) || type == typeof(CompetingRisks)) + { + var children = ChildrenOf(distribution); + if (children is null || children.Length == 0) return false; + for (int i = 0; i < children.Length; i++) + if (!IsSupportedTree(children[i])) return false; + return true; + } + return IsSupportedLeaf(type); + } + + /// Whether a leaf type is in the exact built-in set the scalar visitor understands. + /// The exact runtime type to check. + /// for a supported leaf family; otherwise, . + private static bool IsSupportedLeaf(Type type) + { + return type == typeof(Normal) || type == typeof(LogNormal) || type == typeof(LnNormal) + || type == typeof(LogPearsonTypeIII) || type == typeof(PearsonTypeIII) + || type == typeof(GammaDistribution) || type == typeof(Weibull) || type == typeof(Gumbel) + || type == typeof(GeneralizedExtremeValue) || type == typeof(GeneralizedLogistic) + || type == typeof(GeneralizedNormal) || type == typeof(GeneralizedPareto) + || type == typeof(Exponential) || type == typeof(KappaFour) || type == typeof(Uniform) + || type == typeof(Triangular) || type == typeof(Pert) || type == typeof(Deterministic) + || type == typeof(Logistic) || type == typeof(Cauchy); + } + + /// Returns a composite node's live child array without collection wrappers. + /// The composite distribution. + /// The live child array, or for a leaf or an unpopulated composite. + private static UnivariateDistributionBase[]? ChildrenOf(UnivariateDistributionBase distribution) + { + if (distribution is Mixture mixture && mixture.GetType() == typeof(Mixture)) return mixture.Distributions; + if (distribution is CompetingRisks competing && competing.GetType() == typeof(CompetingRisks)) return competing.ComponentArray; + return null; + } + + /// Builds a snapshot node for a tree already verified by . + /// The distribution to capture. + /// The captured node. + private static DistributionSnapshot Capture(UnivariateDistributionBase distribution) + { + var sink = new List(); + var cursor = new ScalarCursor(sink); + VisitScalars(distribution, ref cursor); + var children = ChildrenOf(distribution); + DistributionSnapshot[]? captured = null; + if (children is not null) + { + captured = new DistributionSnapshot[children.Length]; + for (int i = 0; i < children.Length; i++) + captured[i] = Capture(children[i]); + } + return new DistributionSnapshot(distribution.GetType(), sink.ToArray(), captured); + } + + /// A dual-mode scalar walker that either records bits or compares them against a stored array. + private struct ScalarCursor + { + /// The capture sink; in compare mode. + private readonly List? _sink; + + /// The stored bits to compare against; in capture mode. + private readonly long[]? _stored; + + /// The next compare index into the stored bits. + internal int Index; + + /// Initializes a capture-mode cursor. + /// The list receiving captured bits. + internal ScalarCursor(List sink) { _sink = sink; _stored = null; Index = 0; } + + /// Initializes a compare-mode cursor. + /// The stored bits to compare against. + internal ScalarCursor(long[] stored) { _sink = null; _stored = stored; Index = 0; } + + /// Records or compares one double as raw bits. + /// The live value. + /// to continue the walk; on a compare mismatch. + internal bool Visit(double value) => Visit(BitConverter.DoubleToInt64Bits(value)); + + /// Records or compares one integral value. + /// The live value. + /// to continue the walk; on a compare mismatch. + internal bool Visit(long value) + { + if (_sink is not null) { _sink.Add(value); return true; } + var stored = _stored!; + if (Index >= stored.Length || stored[Index] != value) { Index = int.MinValue; return false; } + Index++; + return true; + } + } + + /// Visits every scalar that determines a node's canonical configuration, in a fixed per-family order. + /// The node whose scalars are visited; its exact type must already be verified. + /// The dual-mode cursor receiving or comparing the scalars. + /// when the walk completes; on a compare mismatch or an unsupported type. + /// Leaf lists mirror each family's GetParameters order, extended by the + /// evaluation-affecting settings that sit outside the flattened parameters (the logarithm + /// base on the log families, the physical-moment surface on , and + /// the composite flags, weights, seed, and correlation entries). Capturing more than the + /// canonical string records is deliberate: extra strictness can only force a string + /// re-comparison, never a wrong match. + private static bool VisitScalars(UnivariateDistributionBase distribution, ref ScalarCursor cursor) + { + Type type = distribution.GetType(); + if (type == typeof(Normal)) + { + var x = (Normal)distribution; + return cursor.Visit(x.Mu) && cursor.Visit(x.Sigma); + } + if (type == typeof(LogNormal)) + { + var x = (LogNormal)distribution; + return cursor.Visit(x.Mu) && cursor.Visit(x.Sigma) && cursor.Visit(x.Base); + } + if (type == typeof(LnNormal)) + { + var x = (LnNormal)distribution; + return cursor.Visit(x.Mu) && cursor.Visit(x.Sigma) && cursor.Visit(x.Mean) && cursor.Visit(x.StandardDeviation); + } + if (type == typeof(LogPearsonTypeIII)) + { + var x = (LogPearsonTypeIII)distribution; + return cursor.Visit(x.Mu) && cursor.Visit(x.Sigma) && cursor.Visit(x.Gamma) && cursor.Visit(x.Base); + } + if (type == typeof(PearsonTypeIII)) + { + var x = (PearsonTypeIII)distribution; + return cursor.Visit(x.Mu) && cursor.Visit(x.Sigma) && cursor.Visit(x.Gamma); + } + if (type == typeof(GammaDistribution)) + { + var x = (GammaDistribution)distribution; + return cursor.Visit(x.Theta) && cursor.Visit(x.Kappa); + } + if (type == typeof(Weibull)) + { + var x = (Weibull)distribution; + return cursor.Visit(x.Lambda) && cursor.Visit(x.Kappa); + } + if (type == typeof(Gumbel)) + { + var x = (Gumbel)distribution; + return cursor.Visit(x.Xi) && cursor.Visit(x.Alpha); + } + if (type == typeof(GeneralizedExtremeValue)) + { + var x = (GeneralizedExtremeValue)distribution; + return cursor.Visit(x.Xi) && cursor.Visit(x.Alpha) && cursor.Visit(x.Kappa); + } + if (type == typeof(GeneralizedLogistic)) + { + var x = (GeneralizedLogistic)distribution; + return cursor.Visit(x.Xi) && cursor.Visit(x.Alpha) && cursor.Visit(x.Kappa); + } + if (type == typeof(GeneralizedNormal)) + { + var x = (GeneralizedNormal)distribution; + return cursor.Visit(x.Xi) && cursor.Visit(x.Alpha) && cursor.Visit(x.Kappa); + } + if (type == typeof(GeneralizedPareto)) + { + var x = (GeneralizedPareto)distribution; + return cursor.Visit(x.Xi) && cursor.Visit(x.Alpha) && cursor.Visit(x.Kappa); + } + if (type == typeof(Exponential)) + { + var x = (Exponential)distribution; + return cursor.Visit(x.Xi) && cursor.Visit(x.Alpha); + } + if (type == typeof(KappaFour)) + { + var x = (KappaFour)distribution; + return cursor.Visit(x.Xi) && cursor.Visit(x.Alpha) && cursor.Visit(x.Kappa) && cursor.Visit(x.Hondo); + } + if (type == typeof(Uniform)) + { + var x = (Uniform)distribution; + return cursor.Visit(x.Min) && cursor.Visit(x.Max); + } + if (type == typeof(Triangular)) + { + var x = (Triangular)distribution; + return cursor.Visit(x.Min) && cursor.Visit(x.MostLikely) && cursor.Visit(x.Max); + } + if (type == typeof(Pert)) + { + var x = (Pert)distribution; + return cursor.Visit(x.Min) && cursor.Visit(x.MostLikely) && cursor.Visit(x.Max); + } + if (type == typeof(Deterministic)) + { + var x = (Deterministic)distribution; + return cursor.Visit(x.Value); + } + if (type == typeof(Logistic)) + { + var x = (Logistic)distribution; + return cursor.Visit(x.Xi) && cursor.Visit(x.Alpha); + } + if (type == typeof(Cauchy)) + { + var x = (Cauchy)distribution; + return cursor.Visit(x.X0) && cursor.Visit(x.Gamma); + } + if (type == typeof(Mixture)) + { + var x = (Mixture)distribution; + var weights = x.Weights; + if (weights is null || !cursor.Visit(x.IsZeroInflated ? 1L : 0L) || !cursor.Visit((long)x.XTransform) + || !cursor.Visit((long)x.ProbabilityTransform) || !cursor.Visit(x.ZeroWeight) + || !cursor.Visit(weights.Length)) return false; + for (int i = 0; i < weights.Length; i++) + if (!cursor.Visit(weights[i])) return false; + return true; + } + if (type == typeof(CompetingRisks)) + { + var x = (CompetingRisks)distribution; + if (!cursor.Visit(x.MinimumOfRandomVariables ? 1L : 0L) || !cursor.Visit((long)x.Dependency) + || !cursor.Visit(x.PRNGSeed) || !cursor.Visit((long)x.XTransform) + || !cursor.Visit((long)x.ProbabilityTransform)) return false; + var matrix = x.CorrelationMatrixArray; + if (matrix is null) return cursor.Visit(0L); + int rows = matrix.GetLength(0), columns = matrix.GetLength(1); + if (!cursor.Visit(1L) || !cursor.Visit(rows) || !cursor.Visit(columns)) return false; + int rowStart = matrix.GetLowerBound(0), columnStart = matrix.GetLowerBound(1); + for (int row = 0; row < rows; row++) + for (int column = 0; column < columns; column++) + if (!cursor.Visit(matrix[rowStart + row, columnStart + column])) return false; + return true; + } + return false; + } + } +} diff --git a/Numerics/Distributions/Univariate/CompetingRisks.cs b/Numerics/Distributions/Univariate/CompetingRisks.cs index a188353d..881a58fb 100644 --- a/Numerics/Distributions/Univariate/CompetingRisks.cs +++ b/Numerics/Distributions/Univariate/CompetingRisks.cs @@ -63,20 +63,17 @@ public CompetingRisks(IUnivariateDistribution[] distributions) private int _prngSeed = MultivariateNormal.DefaultMVNUNISeed; private string? _cachedConfiguration; - [NonSerialized] private WeibullConfiguration? _weibullConfiguration; + [NonSerialized] private DependentConfigurationCache? _dependentConfiguration; - /// Immutable scalar state whose equality implies identical built-in Weibull configuration XML. - private sealed class WeibullConfiguration + /// Immutable configuration state whose equality implies identical canonical configuration XML, + /// carrying the lazily published dependent-density derivative step. + private sealed class DependentConfigurationCache { - private readonly bool _minimum; - private readonly Probability.DependencyType _dependency; - private readonly int _seed; - private readonly Transform _xTransform; - private readonly Transform _probabilityTransform; - private readonly long[] _parameterBits; - private readonly long[]? _matrixBits; - private readonly int _matrixRows; - private readonly int _matrixColumns; + private readonly DistributionSnapshot _state; + + /// Whether every component is an exact built-in Weibull, enabling the fixed-support dependent fast arm. + internal readonly bool AllExactWeibull; + private DensityStep? _densityStep; /// Publishes the lazily computed step atomically for concurrent readers of unchanged state. @@ -104,81 +101,42 @@ internal bool TryGetDensityStep(out double step) /// The component-derived finite-difference step to publish. internal void CacheDensityStep(double step) => Volatile.Write(ref _densityStep, new DensityStep(step)); - /// Captures the mutable state that affects the exact built-in Weibull evaluation path. - /// The competing-risks distribution whose current state is captured. - private WeibullConfiguration(CompetingRisks owner) - { - _minimum = owner.MinimumOfRandomVariables; - _dependency = owner.Dependency; - _seed = owner.PRNGSeed; - _xTransform = owner.XTransform; - _probabilityTransform = owner.ProbabilityTransform; - _parameterBits = new long[2 * owner._distributions.Length]; - for (int i = 0; i < owner._distributions.Length; i++) - { - var weibull = (Weibull)owner._distributions[i]; - _parameterBits[2 * i] = BitConverter.DoubleToInt64Bits(weibull.Lambda); - _parameterBits[2 * i + 1] = BitConverter.DoubleToInt64Bits(weibull.Kappa); - } - var matrix = owner._correlationMatrix; - if (matrix is not null) - { - _matrixRows = matrix.GetLength(0); - _matrixColumns = matrix.GetLength(1); - _matrixBits = new long[matrix.Length]; - int rowStart = matrix.GetLowerBound(0), columnStart = matrix.GetLowerBound(1), index = 0; - for (int row = 0; row < _matrixRows; row++) - for (int column = 0; column < _matrixColumns; column++) - _matrixBits[index++] = BitConverter.DoubleToInt64Bits(matrix[rowStart + row, columnStart + column]); - } + /// Initializes an immutable configuration cache around a captured snapshot. + /// The captured bitwise configuration snapshot. + /// Whether every component is an exact built-in Weibull. + private DependentConfigurationCache(DistributionSnapshot state, bool allExactWeibull) + { + _state = state; + AllExactWeibull = allExactWeibull; } - /// Captures only exact built-in Weibulls; derived and custom XML callbacks retain the generic path. + /// Captures exact supported built-ins; derived and custom XML callbacks retain the generic path. /// The competing-risks distribution to inspect. - /// An immutable snapshot for an exact built-in Weibull configuration, or when the optimized path is not applicable. - internal static WeibullConfiguration? Capture(CompetingRisks owner) + /// An immutable snapshot for an exact supported configuration, or when the optimized path is not applicable. + internal static DependentConfigurationCache? Capture(CompetingRisks owner) { - if (owner._distributions is null) return null; - foreach (var distribution in owner._distributions) - if (distribution is null || distribution.GetType() != typeof(Weibull)) return null; - return new WeibullConfiguration(owner); + var state = DistributionSnapshot.TryCapture(owner); + if (state is null) return null; + var components = owner._distributions; + bool allExactWeibull = true; + for (int i = 0; i < components.Length; i++) + if (components[i].GetType() != typeof(Weibull)) { allExactWeibull = false; break; } + return new DependentConfigurationCache(state, allExactWeibull); } /// Compares live values without allocating wrappers, parameter arrays, or XML. /// The competing-risks distribution whose live state is compared with this snapshot. - /// when every captured scalar, Weibull parameter, and correlation entry is bitwise unchanged; otherwise, . - internal bool Matches(CompetingRisks owner) - { - if (owner._distributions is null || owner._distributions.Length != _parameterBits.Length / 2 - || owner.MinimumOfRandomVariables != _minimum || owner.Dependency != _dependency - || owner.PRNGSeed != _seed || owner.XTransform != _xTransform - || owner.ProbabilityTransform != _probabilityTransform) return false; - for (int i = 0; i < owner._distributions.Length; i++) - { - var distribution = owner._distributions[i]; - if (distribution is null || distribution.GetType() != typeof(Weibull)) return false; - var weibull = (Weibull)distribution; - if (BitConverter.DoubleToInt64Bits(weibull.Lambda) != _parameterBits[2 * i] - || BitConverter.DoubleToInt64Bits(weibull.Kappa) != _parameterBits[2 * i + 1]) return false; - } - var matrix = owner._correlationMatrix; - if (matrix is null) return _matrixBits is null; - if (_matrixBits is null || matrix.GetLength(0) != _matrixRows || matrix.GetLength(1) != _matrixColumns) return false; - int rowStart = matrix.GetLowerBound(0), columnStart = matrix.GetLowerBound(1), index = 0; - for (int row = 0; row < _matrixRows; row++) - for (int column = 0; column < _matrixColumns; column++) - if (BitConverter.DoubleToInt64Bits(matrix[rowStart + row, columnStart + column]) != _matrixBits[index++]) return false; - return true; - } + /// when every captured scalar, component parameter, and correlation entry is bitwise unchanged; otherwise, . + internal bool Matches(CompetingRisks owner) => _state.Matches(owner); } /// Invalidates derived caches when mutable components or configuration change. private void RefreshCachedConfiguration() { - var previous = Volatile.Read(ref _weibullConfiguration); + var previous = Volatile.Read(ref _dependentConfiguration); if (previous is not null && previous.Matches(this)) return; // Capture before canonical serialization: fallback callbacks may mutate their configuration. - var next = WeibullConfiguration.Capture(this); + var next = DependentConfigurationCache.Capture(this); string configuration = DistributionNumerics.ConfigurationState(this); if (configuration != _cachedConfiguration) { @@ -187,7 +145,7 @@ private void RefreshCachedConfiguration() _empiricalCDFCreated = false; _mvnCreated = false; } - Volatile.Write(ref _weibullConfiguration, next); + Volatile.Write(ref _dependentConfiguration, next); } /// @@ -195,6 +153,12 @@ private void RefreshCachedConfiguration() /// public ReadOnlyCollection Distributions => new(_distributions); + /// The live component array, for snapshot capture without collection wrappers. + internal UnivariateDistributionBase[]? ComponentArray => _distributions; + + /// The live correlation matrix, for snapshot capture without cloning. + internal double[,]? CorrelationMatrixArray => _correlationMatrix; + /// /// The seed for the multivariate normal's quadrature randomizer, used by the dependent /// (perfectly negative and correlation-matrix) branches. @@ -724,11 +688,11 @@ private void ValidateEvaluation() /// . Negative boundary or nonfinite density remains a failure. private double DependentDensity(double x, double minimum, double maximum, out bool reuseBounds) { - var configuration = Volatile.Read(ref _weibullConfiguration); + var configuration = Volatile.Read(ref _dependentConfiguration); if (configuration is not null && !configuration.Matches(this)) configuration = null; // Exact built-in Weibulls have fixed support and no custom evaluation callbacks. // Derived owners and generic components retain every live support read. - reuseBounds = configuration is not null && GetType() == typeof(CompetingRisks); + reuseBounds = configuration is not null && configuration.AllExactWeibull && GetType() == typeof(CompetingRisks); double step; if (configuration is null || !configuration.TryGetDensityStep(out step)) { From 7b53a848da08e2fdd741333017923e4a021713d7 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 9 Sep 2026 16:33:02 -0600 Subject: [PATCH 209/222] Short-circuit the mixture configuration refresh with the bitwise snapshot Mixture.RefreshCachedConfiguration serialized the full canonical configuration string on every call - recursively rendering every component to XML - and then discarded it whenever nothing had changed, which every InverseCDF call, CreateEmpiricalCDF, and moment getter paid. A published DistributionSnapshot now short-circuits the unchanged path bitwise, exactly the CompetingRisks pattern; a mismatch or an uncapturable component tree still builds and compares the string, which remains the deciding authority for cache invalidation, so incomplete capture can only cost a string comparison, never a wrong match. Measured (four LnNormal components over the empirical-CDF fast path, Release, per call): InverseCDF 62,512 B -> 928 B (-98.5%) and 11.6 us -> 2.8 us (-76%). CDF is unchanged (its cost is per-call validation, addressed separately). All 47 Mixture family tests pass; values are bit-identical - a snapshot match implies the identical canonical string. --- Numerics/Distributions/Univariate/Mixture.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/Numerics/Distributions/Univariate/Mixture.cs b/Numerics/Distributions/Univariate/Mixture.cs index ddd3281a..90e1f6d8 100644 --- a/Numerics/Distributions/Univariate/Mixture.cs +++ b/Numerics/Distributions/Univariate/Mixture.cs @@ -270,15 +270,26 @@ private double PositiveConditionalQuantile(int componentIndex, double probabilit } private string? _cachedConfiguration; + [NonSerialized] private DistributionSnapshot? _configurationCache; /// Refreshes cached moments and interpolation when public arrays or nested components change. + /// A published bitwise snapshot short-circuits the canonical-string serialization on the + /// unchanged path; a mismatch or an uncapturable component tree falls back to the string + /// comparison, which remains the deciding authority for cache invalidation. private void RefreshCachedConfiguration() { + var previous = Volatile.Read(ref _configurationCache); + if (previous is not null && previous.Matches(this)) return; + // Capture before canonical serialization: fallback callbacks may mutate their configuration. + var next = DistributionSnapshot.TryCapture(this); string configuration = DistributionNumerics.ConfigurationState(this); - if (configuration == _cachedConfiguration) return; - _cachedConfiguration = configuration; - _momentsComputed = false; - _empiricalCDFCreated = false; + if (configuration != _cachedConfiguration) + { + _cachedConfiguration = configuration; + _momentsComputed = false; + _empiricalCDFCreated = false; + } + Volatile.Write(ref _configurationCache, next); } /// Checks mutable weights and current component validity before evaluation. From 73e1dfe78cb8fce1ebb48587925e69ab392fc881 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 9 Sep 2026 16:45:02 -0600 Subject: [PATCH 210/222] Gate mixture validation and support bounds on the certified snapshot Mixture evaluation validated every component on every call - allocating a parameter array per non-Normal component - and read its support bounds through capturing LINQ closures on every quantile clamp. A ValidationCertificate now publishes the exact bitwise snapshot that passed the full validator: a match skips re-validation without allocating, any mutation re-runs the verbatim validator with identical exceptions and precedence, and uncapturable component trees keep per-call validation unchanged. The certificate also carries lazily published support bounds (pure functions of the certified bits), and InverseCDF unifies its refresh and validation checks into one snapshot walk by reference identity with the refresh-published instance. The weight-log cache now serves LogCDF, LogCCDF, and interval probabilities as it already served LogPDF, hot loops index Length instead of LINQ Count(), and LnNormal exposes its stored physical-moment fields internally so snapshot capture pins the moment surface without evaluating it. The snapshot compare path is rewritten as inline per-family arms over the stored bits (capture keeps the shared cursor walk): the consuming libraries link this assembly's Debug build, whose minimal-optimization jitting keeps every helper call, so the hot path minimizes call count. Measured (four LnNormal components over the empirical-CDF fast path, Debug assembly, median of three, per call): InverseCDF 928 B -> 96 B and 2.8 us -> 0.79 us - now better than the pre-hardening baseline (144 B / 0.97 us) on both axes; CDF 160 B -> 0 B at wall parity with the prior state (the remaining gap to the pre-hardening direct sum is the retained log-sum-exp tail repair, deliberately unchanged). All 135 Mixture, CompetingRisks, and LnNormal family tests pass; values are bit-identical. --- .../Univariate/Base/DistributionSnapshot.cs | 358 +++++++++++++++--- Numerics/Distributions/Univariate/LnNormal.cs | 9 + Numerics/Distributions/Univariate/Mixture.cs | 167 ++++++-- 3 files changed, 462 insertions(+), 72 deletions(-) diff --git a/Numerics/Distributions/Univariate/Base/DistributionSnapshot.cs b/Numerics/Distributions/Univariate/Base/DistributionSnapshot.cs index 871899af..9283f089 100644 --- a/Numerics/Distributions/Univariate/Base/DistributionSnapshot.cs +++ b/Numerics/Distributions/Univariate/Base/DistributionSnapshot.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; namespace Numerics.Distributions { @@ -22,6 +23,9 @@ internal sealed class DistributionSnapshot /// The exact runtime type of the captured distribution. private readonly Type _type; + /// The jump-table family code for the captured type, from . + private readonly byte _family; + /// The captured scalar state, as raw bits, in the fixed per-family order. private readonly long[] _scalarBits; @@ -30,11 +34,13 @@ internal sealed class DistributionSnapshot /// Initializes an immutable snapshot node. /// The exact runtime type of the captured distribution. + /// The jump-table family code for the captured type. /// The captured scalar state in the fixed per-family order. /// Child snapshots for composite nodes, or . - private DistributionSnapshot(Type type, long[] scalarBits, DistributionSnapshot[]? children) + private DistributionSnapshot(Type type, byte family, long[] scalarBits, DistributionSnapshot[]? children) { _type = type; + _family = family; _scalarBits = scalarBits; _children = children; } @@ -51,11 +57,17 @@ private DistributionSnapshot(Type type, long[] scalarBits, DistributionSnapshot[ /// Compares live state bitwise without allocating wrappers, parameter arrays, or strings. /// The distribution whose live state is compared with this snapshot. /// when every captured scalar and child is bitwise unchanged; otherwise, . + /// The compare arms are written inline against the stored bits rather than through + /// the capture cursor: the consuming libraries link the Debug build of this assembly, whose + /// minimal-optimization jitting keeps every helper call, so the hot path minimizes call + /// count. A capture/compare round-trip test per supported family guards the two switches + /// against drifting apart. internal bool Matches(UnivariateDistributionBase? distribution) { if (distribution is null || distribution.GetType() != _type) return false; - var cursor = new ScalarCursor(_scalarBits); - if (!VisitScalars(distribution, ref cursor) || cursor.Index != _scalarBits.Length) return false; + var bits = _scalarBits; + int index = 0; + if (!MatchScalars(distribution, _family, bits, ref index) || index != bits.Length) return false; if (_children is null) return true; var children = ChildrenOf(distribution); if (children is null || children.Length != _children.Length) return false; @@ -64,6 +76,239 @@ internal bool Matches(UnivariateDistributionBase? distribution) return true; } + /// Compares one node's live scalars against stored bits with inline arithmetic. + /// The node whose scalars are compared; its exact type must already be verified. + /// The node's family code, stored at capture for jump-table dispatch. + /// The stored scalar bits. + /// The read position within , advanced past this node's scalars. + /// when every scalar is bitwise unchanged; otherwise, . + private static bool MatchScalars(UnivariateDistributionBase distribution, byte family, long[] bits, ref int index) + { + int k = index; + switch (family) + { + case 1: + { + var x = (Normal)distribution; + if (k + 2 > bits.Length || bits[k] != BitConverter.DoubleToInt64Bits(x.Mu) + || bits[k + 1] != BitConverter.DoubleToInt64Bits(x.Sigma)) return false; + index = k + 2; + return true; + } + case 2: + { + var x = (LogNormal)distribution; + if (k + 3 > bits.Length || bits[k] != BitConverter.DoubleToInt64Bits(x.Mu) + || bits[k + 1] != BitConverter.DoubleToInt64Bits(x.Sigma) + || bits[k + 2] != BitConverter.DoubleToInt64Bits(x.Base)) return false; + index = k + 3; + return true; + } + case 3: + { + var x = (LnNormal)distribution; + if (k + 5 > bits.Length || bits[k] != BitConverter.DoubleToInt64Bits(x.Mu) + || bits[k + 1] != BitConverter.DoubleToInt64Bits(x.Sigma) + || bits[k + 2] != (x.PhysicalMomentModeForSnapshot ? 1L : 0L) + || bits[k + 3] != BitConverter.DoubleToInt64Bits(x.PhysicalMeanForSnapshot) + || bits[k + 4] != BitConverter.DoubleToInt64Bits(x.PhysicalStandardDeviationForSnapshot)) return false; + index = k + 5; + return true; + } + case 4: + { + var x = (LogPearsonTypeIII)distribution; + if (k + 4 > bits.Length || bits[k] != BitConverter.DoubleToInt64Bits(x.Mu) + || bits[k + 1] != BitConverter.DoubleToInt64Bits(x.Sigma) + || bits[k + 2] != BitConverter.DoubleToInt64Bits(x.Gamma) + || bits[k + 3] != BitConverter.DoubleToInt64Bits(x.Base)) return false; + index = k + 4; + return true; + } + case 5: + { + var x = (PearsonTypeIII)distribution; + if (k + 3 > bits.Length || bits[k] != BitConverter.DoubleToInt64Bits(x.Mu) + || bits[k + 1] != BitConverter.DoubleToInt64Bits(x.Sigma) + || bits[k + 2] != BitConverter.DoubleToInt64Bits(x.Gamma)) return false; + index = k + 3; + return true; + } + case 6: + { + var x = (GammaDistribution)distribution; + if (k + 2 > bits.Length || bits[k] != BitConverter.DoubleToInt64Bits(x.Theta) + || bits[k + 1] != BitConverter.DoubleToInt64Bits(x.Kappa)) return false; + index = k + 2; + return true; + } + case 7: + { + var x = (Weibull)distribution; + if (k + 2 > bits.Length || bits[k] != BitConverter.DoubleToInt64Bits(x.Lambda) + || bits[k + 1] != BitConverter.DoubleToInt64Bits(x.Kappa)) return false; + index = k + 2; + return true; + } + case 8: + { + var x = (Gumbel)distribution; + if (k + 2 > bits.Length || bits[k] != BitConverter.DoubleToInt64Bits(x.Xi) + || bits[k + 1] != BitConverter.DoubleToInt64Bits(x.Alpha)) return false; + index = k + 2; + return true; + } + case 9: + { + var x = (GeneralizedExtremeValue)distribution; + if (k + 3 > bits.Length || bits[k] != BitConverter.DoubleToInt64Bits(x.Xi) + || bits[k + 1] != BitConverter.DoubleToInt64Bits(x.Alpha) + || bits[k + 2] != BitConverter.DoubleToInt64Bits(x.Kappa)) return false; + index = k + 3; + return true; + } + case 10: + { + var x = (GeneralizedLogistic)distribution; + if (k + 3 > bits.Length || bits[k] != BitConverter.DoubleToInt64Bits(x.Xi) + || bits[k + 1] != BitConverter.DoubleToInt64Bits(x.Alpha) + || bits[k + 2] != BitConverter.DoubleToInt64Bits(x.Kappa)) return false; + index = k + 3; + return true; + } + case 11: + { + var x = (GeneralizedNormal)distribution; + if (k + 3 > bits.Length || bits[k] != BitConverter.DoubleToInt64Bits(x.Xi) + || bits[k + 1] != BitConverter.DoubleToInt64Bits(x.Alpha) + || bits[k + 2] != BitConverter.DoubleToInt64Bits(x.Kappa)) return false; + index = k + 3; + return true; + } + case 12: + { + var x = (GeneralizedPareto)distribution; + if (k + 3 > bits.Length || bits[k] != BitConverter.DoubleToInt64Bits(x.Xi) + || bits[k + 1] != BitConverter.DoubleToInt64Bits(x.Alpha) + || bits[k + 2] != BitConverter.DoubleToInt64Bits(x.Kappa)) return false; + index = k + 3; + return true; + } + case 13: + { + var x = (Exponential)distribution; + if (k + 2 > bits.Length || bits[k] != BitConverter.DoubleToInt64Bits(x.Xi) + || bits[k + 1] != BitConverter.DoubleToInt64Bits(x.Alpha)) return false; + index = k + 2; + return true; + } + case 14: + { + var x = (KappaFour)distribution; + if (k + 4 > bits.Length || bits[k] != BitConverter.DoubleToInt64Bits(x.Xi) + || bits[k + 1] != BitConverter.DoubleToInt64Bits(x.Alpha) + || bits[k + 2] != BitConverter.DoubleToInt64Bits(x.Kappa) + || bits[k + 3] != BitConverter.DoubleToInt64Bits(x.Hondo)) return false; + index = k + 4; + return true; + } + case 15: + { + var x = (Uniform)distribution; + if (k + 2 > bits.Length || bits[k] != BitConverter.DoubleToInt64Bits(x.Min) + || bits[k + 1] != BitConverter.DoubleToInt64Bits(x.Max)) return false; + index = k + 2; + return true; + } + case 16: + { + var x = (Triangular)distribution; + if (k + 3 > bits.Length || bits[k] != BitConverter.DoubleToInt64Bits(x.Min) + || bits[k + 1] != BitConverter.DoubleToInt64Bits(x.MostLikely) + || bits[k + 2] != BitConverter.DoubleToInt64Bits(x.Max)) return false; + index = k + 3; + return true; + } + case 17: + { + var x = (Pert)distribution; + if (k + 3 > bits.Length || bits[k] != BitConverter.DoubleToInt64Bits(x.Min) + || bits[k + 1] != BitConverter.DoubleToInt64Bits(x.MostLikely) + || bits[k + 2] != BitConverter.DoubleToInt64Bits(x.Max)) return false; + index = k + 3; + return true; + } + case 18: + { + var x = (Deterministic)distribution; + if (k + 1 > bits.Length || bits[k] != BitConverter.DoubleToInt64Bits(x.Value)) return false; + index = k + 1; + return true; + } + case 19: + { + var x = (Logistic)distribution; + if (k + 2 > bits.Length || bits[k] != BitConverter.DoubleToInt64Bits(x.Xi) + || bits[k + 1] != BitConverter.DoubleToInt64Bits(x.Alpha)) return false; + index = k + 2; + return true; + } + case 20: + { + var x = (Cauchy)distribution; + if (k + 2 > bits.Length || bits[k] != BitConverter.DoubleToInt64Bits(x.X0) + || bits[k + 1] != BitConverter.DoubleToInt64Bits(x.Gamma)) return false; + index = k + 2; + return true; + } + case 21: + { + var x = (Mixture)distribution; + var weights = x.Weights; + if (weights is null || k + 5 + weights.Length > bits.Length + || bits[k] != (x.IsZeroInflated ? 1L : 0L) + || bits[k + 1] != (long)x.XTransform + || bits[k + 2] != (long)x.ProbabilityTransform + || bits[k + 3] != BitConverter.DoubleToInt64Bits(x.ZeroWeight) + || bits[k + 4] != weights.Length) return false; + k += 5; + for (int i = 0; i < weights.Length; i++, k++) + if (bits[k] != BitConverter.DoubleToInt64Bits(weights[i])) return false; + index = k; + return true; + } + case 22: + { + var x = (CompetingRisks)distribution; + if (k + 6 > bits.Length + || bits[k] != (x.MinimumOfRandomVariables ? 1L : 0L) + || bits[k + 1] != (long)x.Dependency + || bits[k + 2] != x.PRNGSeed + || bits[k + 3] != (long)x.XTransform + || bits[k + 4] != (long)x.ProbabilityTransform) return false; + k += 5; + var matrix = x.CorrelationMatrixArray; + if (matrix is null) + { + if (bits[k] != 0L) return false; + index = k + 1; + return true; + } + int rows = matrix.GetLength(0), columns = matrix.GetLength(1); + if (k + 3 + rows * columns > bits.Length || bits[k] != 1L + || bits[k + 1] != rows || bits[k + 2] != columns) return false; + k += 3; + int rowStart = matrix.GetLowerBound(0), columnStart = matrix.GetLowerBound(1); + for (int row = 0; row < rows; row++) + for (int column = 0; column < columns; column++, k++) + if (bits[k] != BitConverter.DoubleToInt64Bits(matrix[rowStart + row, columnStart + column])) return false; + index = k; + return true; + } + } + return false; + } + /// Walks the tree checking that every node is an exact supported built-in, without allocating. /// The root of the tree to check. /// when every node is capturable; otherwise, . @@ -79,22 +324,37 @@ private static bool IsSupportedTree(UnivariateDistributionBase? distribution) if (!IsSupportedTree(children[i])) return false; return true; } - return IsSupportedLeaf(type); + return FamilyOf(type) != 0; } - /// Whether a leaf type is in the exact built-in set the scalar visitor understands. - /// The exact runtime type to check. - /// for a supported leaf family; otherwise, . - private static bool IsSupportedLeaf(Type type) + /// Maps an exact runtime type to its jump-table family code, or zero when unsupported. + /// The exact runtime type to classify. + /// The family code for a supported type; otherwise, zero. + private static byte FamilyOf(Type type) { - return type == typeof(Normal) || type == typeof(LogNormal) || type == typeof(LnNormal) - || type == typeof(LogPearsonTypeIII) || type == typeof(PearsonTypeIII) - || type == typeof(GammaDistribution) || type == typeof(Weibull) || type == typeof(Gumbel) - || type == typeof(GeneralizedExtremeValue) || type == typeof(GeneralizedLogistic) - || type == typeof(GeneralizedNormal) || type == typeof(GeneralizedPareto) - || type == typeof(Exponential) || type == typeof(KappaFour) || type == typeof(Uniform) - || type == typeof(Triangular) || type == typeof(Pert) || type == typeof(Deterministic) - || type == typeof(Logistic) || type == typeof(Cauchy); + if (type == typeof(Normal)) return 1; + if (type == typeof(LogNormal)) return 2; + if (type == typeof(LnNormal)) return 3; + if (type == typeof(LogPearsonTypeIII)) return 4; + if (type == typeof(PearsonTypeIII)) return 5; + if (type == typeof(GammaDistribution)) return 6; + if (type == typeof(Weibull)) return 7; + if (type == typeof(Gumbel)) return 8; + if (type == typeof(GeneralizedExtremeValue)) return 9; + if (type == typeof(GeneralizedLogistic)) return 10; + if (type == typeof(GeneralizedNormal)) return 11; + if (type == typeof(GeneralizedPareto)) return 12; + if (type == typeof(Exponential)) return 13; + if (type == typeof(KappaFour)) return 14; + if (type == typeof(Uniform)) return 15; + if (type == typeof(Triangular)) return 16; + if (type == typeof(Pert)) return 17; + if (type == typeof(Deterministic)) return 18; + if (type == typeof(Logistic)) return 19; + if (type == typeof(Cauchy)) return 20; + if (type == typeof(Mixture)) return 21; + if (type == typeof(CompetingRisks)) return 22; + return 0; } /// Returns a composite node's live child array without collection wrappers. @@ -112,9 +372,10 @@ private static bool IsSupportedLeaf(Type type) /// The captured node. private static DistributionSnapshot Capture(UnivariateDistributionBase distribution) { + byte family = FamilyOf(distribution.GetType()); var sink = new List(); var cursor = new ScalarCursor(sink); - VisitScalars(distribution, ref cursor); + VisitScalars(distribution, family, ref cursor); var children = ChildrenOf(distribution); DistributionSnapshot[]? captured = null; if (children is not null) @@ -123,7 +384,7 @@ private static DistributionSnapshot Capture(UnivariateDistributionBase distribut for (int i = 0; i < children.Length; i++) captured[i] = Capture(children[i]); } - return new DistributionSnapshot(distribution.GetType(), sink.ToArray(), captured); + return new DistributionSnapshot(distribution.GetType(), family, sink.ToArray(), captured); } /// A dual-mode scalar walker that either records bits or compares them against a stored array. @@ -149,11 +410,13 @@ private struct ScalarCursor /// Records or compares one double as raw bits. /// The live value. /// to continue the walk; on a compare mismatch. + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal bool Visit(double value) => Visit(BitConverter.DoubleToInt64Bits(value)); /// Records or compares one integral value. /// The live value. /// to continue the walk; on a compare mismatch. + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal bool Visit(long value) { if (_sink is not null) { _sink.Add(value); return true; } @@ -166,6 +429,7 @@ internal bool Visit(long value) /// Visits every scalar that determines a node's canonical configuration, in a fixed per-family order. /// The node whose scalars are visited; its exact type must already be verified. + /// The node's family code, stored at capture for jump-table dispatch. /// The dual-mode cursor receiving or comparing the scalars. /// when the walk completes; on a compare mismatch or an unsupported type. /// Leaf lists mirror each family's GetParameters order, extended by the @@ -174,110 +438,117 @@ internal bool Visit(long value) /// the composite flags, weights, seed, and correlation entries). Capturing more than the /// canonical string records is deliberate: extra strictness can only force a string /// re-comparison, never a wrong match. - private static bool VisitScalars(UnivariateDistributionBase distribution, ref ScalarCursor cursor) + private static bool VisitScalars(UnivariateDistributionBase distribution, byte family, ref ScalarCursor cursor) { - Type type = distribution.GetType(); - if (type == typeof(Normal)) + switch (family) + { + case 1: { var x = (Normal)distribution; return cursor.Visit(x.Mu) && cursor.Visit(x.Sigma); } - if (type == typeof(LogNormal)) + case 2: { var x = (LogNormal)distribution; return cursor.Visit(x.Mu) && cursor.Visit(x.Sigma) && cursor.Visit(x.Base); } - if (type == typeof(LnNormal)) + case 3: { + // The physical-moment fields are captured raw so the reported moment surface is + // pinned without evaluating it: two distinct physical pairs can round to identical + // log coordinates, so the log coordinates alone would under-determine the + // canonical form. var x = (LnNormal)distribution; - return cursor.Visit(x.Mu) && cursor.Visit(x.Sigma) && cursor.Visit(x.Mean) && cursor.Visit(x.StandardDeviation); + return cursor.Visit(x.Mu) && cursor.Visit(x.Sigma) + && cursor.Visit(x.PhysicalMomentModeForSnapshot ? 1L : 0L) + && cursor.Visit(x.PhysicalMeanForSnapshot) && cursor.Visit(x.PhysicalStandardDeviationForSnapshot); } - if (type == typeof(LogPearsonTypeIII)) + case 4: { var x = (LogPearsonTypeIII)distribution; return cursor.Visit(x.Mu) && cursor.Visit(x.Sigma) && cursor.Visit(x.Gamma) && cursor.Visit(x.Base); } - if (type == typeof(PearsonTypeIII)) + case 5: { var x = (PearsonTypeIII)distribution; return cursor.Visit(x.Mu) && cursor.Visit(x.Sigma) && cursor.Visit(x.Gamma); } - if (type == typeof(GammaDistribution)) + case 6: { var x = (GammaDistribution)distribution; return cursor.Visit(x.Theta) && cursor.Visit(x.Kappa); } - if (type == typeof(Weibull)) + case 7: { var x = (Weibull)distribution; return cursor.Visit(x.Lambda) && cursor.Visit(x.Kappa); } - if (type == typeof(Gumbel)) + case 8: { var x = (Gumbel)distribution; return cursor.Visit(x.Xi) && cursor.Visit(x.Alpha); } - if (type == typeof(GeneralizedExtremeValue)) + case 9: { var x = (GeneralizedExtremeValue)distribution; return cursor.Visit(x.Xi) && cursor.Visit(x.Alpha) && cursor.Visit(x.Kappa); } - if (type == typeof(GeneralizedLogistic)) + case 10: { var x = (GeneralizedLogistic)distribution; return cursor.Visit(x.Xi) && cursor.Visit(x.Alpha) && cursor.Visit(x.Kappa); } - if (type == typeof(GeneralizedNormal)) + case 11: { var x = (GeneralizedNormal)distribution; return cursor.Visit(x.Xi) && cursor.Visit(x.Alpha) && cursor.Visit(x.Kappa); } - if (type == typeof(GeneralizedPareto)) + case 12: { var x = (GeneralizedPareto)distribution; return cursor.Visit(x.Xi) && cursor.Visit(x.Alpha) && cursor.Visit(x.Kappa); } - if (type == typeof(Exponential)) + case 13: { var x = (Exponential)distribution; return cursor.Visit(x.Xi) && cursor.Visit(x.Alpha); } - if (type == typeof(KappaFour)) + case 14: { var x = (KappaFour)distribution; return cursor.Visit(x.Xi) && cursor.Visit(x.Alpha) && cursor.Visit(x.Kappa) && cursor.Visit(x.Hondo); } - if (type == typeof(Uniform)) + case 15: { var x = (Uniform)distribution; return cursor.Visit(x.Min) && cursor.Visit(x.Max); } - if (type == typeof(Triangular)) + case 16: { var x = (Triangular)distribution; return cursor.Visit(x.Min) && cursor.Visit(x.MostLikely) && cursor.Visit(x.Max); } - if (type == typeof(Pert)) + case 17: { var x = (Pert)distribution; return cursor.Visit(x.Min) && cursor.Visit(x.MostLikely) && cursor.Visit(x.Max); } - if (type == typeof(Deterministic)) + case 18: { var x = (Deterministic)distribution; return cursor.Visit(x.Value); } - if (type == typeof(Logistic)) + case 19: { var x = (Logistic)distribution; return cursor.Visit(x.Xi) && cursor.Visit(x.Alpha); } - if (type == typeof(Cauchy)) + case 20: { var x = (Cauchy)distribution; return cursor.Visit(x.X0) && cursor.Visit(x.Gamma); } - if (type == typeof(Mixture)) + case 21: { var x = (Mixture)distribution; var weights = x.Weights; @@ -288,7 +559,7 @@ private static bool VisitScalars(UnivariateDistributionBase distribution, ref Sc if (!cursor.Visit(weights[i])) return false; return true; } - if (type == typeof(CompetingRisks)) + case 22: { var x = (CompetingRisks)distribution; if (!cursor.Visit(x.MinimumOfRandomVariables ? 1L : 0L) || !cursor.Visit((long)x.Dependency) @@ -304,6 +575,7 @@ private static bool VisitScalars(UnivariateDistributionBase distribution, ref Sc if (!cursor.Visit(matrix[rowStart + row, columnStart + column])) return false; return true; } + } return false; } } diff --git a/Numerics/Distributions/Univariate/LnNormal.cs b/Numerics/Distributions/Univariate/LnNormal.cs index ee1f9cd0..b6be8f13 100644 --- a/Numerics/Distributions/Univariate/LnNormal.cs +++ b/Numerics/Distributions/Univariate/LnNormal.cs @@ -64,6 +64,15 @@ public LnNormal(double mean, double standardDeviation) private double _physicalMean; private double _physicalStandardDeviation; + /// Whether the stored physical-moment surface is active, for snapshot capture without computing moments. + internal bool PhysicalMomentModeForSnapshot => _hasPhysicalMoments; + + /// The stored physical mean field, for snapshot capture without computing moments. + internal double PhysicalMeanForSnapshot => _physicalMean; + + /// The stored physical standard-deviation field, for snapshot capture without computing moments. + internal double PhysicalStandardDeviationForSnapshot => _physicalStandardDeviation; + /// /// Gets and sets the mean µ (Mu) of the natural logarithm of the observation. /// diff --git a/Numerics/Distributions/Univariate/Mixture.cs b/Numerics/Distributions/Univariate/Mixture.cs index 90e1f6d8..eefdfc34 100644 --- a/Numerics/Distributions/Univariate/Mixture.cs +++ b/Numerics/Distributions/Univariate/Mixture.cs @@ -292,13 +292,92 @@ private void RefreshCachedConfiguration() Volatile.Write(ref _configurationCache, next); } + [NonSerialized] private ValidationCertificate? _validationCertificate; + + /// Publishes the exact bitwise state that has already passed full evaluation validation. + /// A certificate exists only for capturable component trees, so a bitwise match + /// proves the full validator - including the zero-inflated positive-mass checks, which are + /// pure functions of the captured component parameters - already accepted exactly this + /// state. Any mutation changes the bits and routes the next evaluation back through the + /// full validator, preserving every exception and its precedence. + private sealed class ValidationCertificate + { + /// The captured configuration whose bits passed the full validator. + internal readonly DistributionSnapshot State; + + /// The lazily published support minimum for the certified state. + private CachedValue? _minimum; + + /// The lazily published support maximum for the certified state. + private CachedValue? _maximum; + + /// An immutable value publication for concurrent readers of unchanged state. + private sealed class CachedValue + { + /// The cached value. + internal readonly double Value; + + /// Initializes an immutable value publication. + /// The value to publish. + internal CachedValue(double value) { Value = value; } + } + + /// Initializes a certificate for a validated state. + /// The captured configuration that passed validation. + internal ValidationCertificate(DistributionSnapshot state) { State = state; } + + /// Returns the cached support minimum for the certified state. + /// The cached minimum, or zero when none has been published. + /// when a minimum is available; otherwise, . + internal bool TryGetMinimum(out double value) + { + var cached = Volatile.Read(ref _minimum); + value = cached is null ? 0d : cached.Value; + return cached is not null; + } + + /// Publishes the support minimum for the certified state. + /// The computed minimum. + internal void CacheMinimum(double value) => Volatile.Write(ref _minimum, new CachedValue(value)); + + /// Returns the cached support maximum for the certified state. + /// The cached maximum, or zero when none has been published. + /// when a maximum is available; otherwise, . + internal bool TryGetMaximum(out double value) + { + var cached = Volatile.Read(ref _maximum); + value = cached is null ? 0d : cached.Value; + return cached is not null; + } + + /// Publishes the support maximum for the certified state. + /// The computed maximum. + internal void CacheMaximum(double value) => Volatile.Write(ref _maximum, new CachedValue(value)); + } + /// Checks mutable weights and current component validity before evaluation. /// The component collection, weights, zero weight, total mass, or a component's parameters are invalid. + /// A published certificate for the bitwise-identical state skips re-validation + /// without allocating; any mutation, and every uncapturable component tree, runs the full + /// validator exactly as before. + private void ValidateEvaluation() + { + var certificate = Volatile.Read(ref _validationCertificate); + if (certificate is not null && certificate.State.Matches(this)) return; + ValidateEvaluationSlow(); + // Prefer the refresh-published snapshot instance so steady-state callers can unify + // the two checks by reference identity instead of walking twice. + var state = Volatile.Read(ref _configurationCache); + if (state is null || !state.Matches(this)) state = DistributionSnapshot.TryCapture(this); + if (state is not null) Volatile.Write(ref _validationCertificate, new ValidationCertificate(state)); + } + + /// Runs the full evaluation validator against live state. + /// The component collection, weights, zero weight, total mass, or a component's parameters are invalid. /// Validates live component parameters directly so evaluation does not flatten and re-slice /// the same state. Sealed Normal components delegate to their existing scalar validator without - /// allocating parameter arrays; other components retain their list validator. Every component is - /// checked on every call because public arrays and nested component settings remain mutable. - private void ValidateEvaluation() + /// allocating parameter arrays; other components retain their list validator. + private void ValidateEvaluationSlow() { ArgumentOutOfRangeException? error = null; if (_distributions is null || _weights is null || _distributions.Length == 0 @@ -389,7 +468,7 @@ public override int NumberOfParameters { int sum = 0; sum += Distributions.Count(); - for (int i = 0; i < Distributions.Count(); i++) + for (int i = 0; i < Distributions.Length; i++) sum += Distributions[i].NumberOfParameters; return sum; } @@ -442,7 +521,7 @@ public override string[] ParameterNames { result.Add("Weight " + i.ToString()); } - for (int i = 0; i < Distributions.Count(); i++) + for (int i = 0; i < Distributions.Length; i++) { for (int j = 0; j < Distributions[i].ParameterNames.Length; j++) { @@ -464,7 +543,7 @@ public override string[] ParameterNamesShortForm { result.Add("W" + i.ToString()); } - for (int i = 0; i < Distributions.Count(); i++) + for (int i = 0; i < Distributions.Length; i++) { for (int j = 0; j < Distributions[i].ParameterNamesShortForm.Length; j++) { @@ -482,7 +561,7 @@ public override double[] GetParameters { var result = new List(); result.AddRange(Weights); - for (int i = 0; i < Distributions.Count(); i++) + for (int i = 0; i < Distributions.Length; i++) { result.AddRange(Distributions[i].GetParameters); } @@ -618,10 +697,20 @@ public override double Minimum { get { + var certificate = Volatile.Read(ref _validationCertificate); + if (certificate is not null && certificate.TryGetMinimum(out double cached) + && certificate.State.Matches(this)) return cached; ValidateEvaluation(); - if (IsZeroInflated && ZeroWeight > 0) return 0; - double minimum = Distributions.Where((d, i) => Weights[i] > 0).Min(d => d.Minimum); - return IsZeroInflated ? Math.Max(0, minimum) : minimum; + double minimum; + if (IsZeroInflated && ZeroWeight > 0) minimum = 0; + else + { + minimum = Distributions.Where((d, i) => Weights[i] > 0).Min(d => d.Minimum); + if (IsZeroInflated) minimum = Math.Max(0, minimum); + } + certificate = Volatile.Read(ref _validationCertificate); + if (certificate is not null && certificate.State.Matches(this)) certificate.CacheMinimum(minimum); + return minimum; } } @@ -630,8 +719,14 @@ public override double Maximum { get { + var certificate = Volatile.Read(ref _validationCertificate); + if (certificate is not null && certificate.TryGetMaximum(out double cached) + && certificate.State.Matches(this)) return cached; ValidateEvaluation(); - return Distributions.Where((d, i) => Weights[i] > 0).Max(d => d.Maximum); + double maximum = Distributions.Where((d, i) => Weights[i] > 0).Max(d => d.Maximum); + certificate = Volatile.Read(ref _validationCertificate); + if (certificate is not null && certificate.State.Matches(this)) certificate.CacheMaximum(maximum); + return maximum; } } @@ -642,11 +737,11 @@ public override double[] MinimumOfParameters { var result = new List(); if (IsZeroInflated) { result.Add(0.0); } - for (int i = 0; i < Distributions.Count(); i++) + for (int i = 0; i < Distributions.Length; i++) { result.Add(0.0); } - for (int i = 0; i < Distributions.Count(); i++) + for (int i = 0; i < Distributions.Length; i++) { result.AddRange(Distributions[i].MinimumOfParameters); } @@ -661,11 +756,11 @@ public override double[] MaximumOfParameters { var result = new List(); if (IsZeroInflated) { result.Add(1.0); } - for (int i = 0; i < Distributions.Count(); i++) + for (int i = 0; i < Distributions.Length; i++) { result.Add(1.0); } - for (int i = 0; i < Distributions.Count(); i++) + for (int i = 0; i < Distributions.Length; i++) { result.AddRange(Distributions[i].MaximumOfParameters); } @@ -761,7 +856,7 @@ public void SetParameters(double[] weights, double[] parameters) _weights = weights.ToArray(); // Set distribution parameters int t = 0; - for (int i = 0; i < Distributions.Count(); i++) + for (int i = 0; i < Distributions.Length; i++) { var parms = new List(); for (int j = t; j < t + Distributions[i].NumberOfParameters; j++) @@ -789,13 +884,13 @@ public override void SetParameters(IList parameters) // Set the weights. int parameterIndex = 0; - for (int i = 0; i < Distributions.Count(); i++) + for (int i = 0; i < Distributions.Length; i++) { Weights[i] = parameterCopy[parameterIndex++]; } // Set the distribution parameters. - for (int i = 0; i < Distributions.Count(); i++) + for (int i = 0; i < Distributions.Length; i++) { double[] distributionParameters = parameterCopy .Skip(parameterIndex) @@ -921,7 +1016,7 @@ public Tuple GetParameterConstraints(IList // Weights are first int t = 0; - for (int i = 0; i < Distributions.Count(); i++) + for (int i = 0; i < Distributions.Length; i++) { initialVals[i] = IsZeroInflated ? (1d - ZeroWeight) / Distributions.Count() : 1d / Distributions.Count(); lowerVals[i] = 0.0; @@ -929,7 +1024,7 @@ public Tuple GetParameterConstraints(IList t += 1; } - for (int i = 0; i < Distributions.Count(); i++) + for (int i = 0; i < Distributions.Length; i++) { var tuple = ((IMaximumLikelihoodEstimation)Distributions[i]).GetParameterConstraints(sample); var initials = tuple.Item1; @@ -1159,7 +1254,7 @@ public override double LogCDF(double x) { if (Weights[i] == 0) continue; double log = IsZeroInflated ? PositiveConditionalLogCDF(i, x) : Distributions[i].LogCDF(x); - total = DistributionNumerics.LogSum(total, Math.Log(Weights[i]) + log); + total = DistributionNumerics.LogSum(total, LogWeight(i, Weights[i]) + log); } return Math.Min(0, total); } @@ -1177,7 +1272,7 @@ public override double LogCCDF(double x) { if (Weights[i] == 0) continue; double log = IsZeroInflated ? PositiveConditionalLogCCDF(i, x) : Distributions[i].LogCCDF(x); - total = DistributionNumerics.LogSum(total, Math.Log(Weights[i]) + log); + total = DistributionNumerics.LogSum(total, LogWeight(i, Weights[i]) + log); } return Math.Min(0, total); } @@ -1197,7 +1292,7 @@ internal double LogIntervalProbability(double lower, double upper) if (Weights[i] == 0) continue; double log = Distributions[i].LogLikelihood_Intervals(IsZeroInflated ? Math.Max(0, lower) : lower, upper); if (IsZeroInflated) log -= Distributions[i].LogCCDF(0); - total = DistributionNumerics.LogSum(total, Math.Log(Weights[i]) + log); + total = DistributionNumerics.LogSum(total, LogWeight(i, Weights[i]) + log); } return total; } @@ -1205,14 +1300,24 @@ internal double LogIntervalProbability(double lower, double upper) /// public override double InverseCDF(double probability) { - RefreshCachedConfiguration(); + // One snapshot walk covers refresh and validation on the unchanged path: the + // certificate holding the refresh-published instance proves both checks at once. + var certificate = Volatile.Read(ref _validationCertificate); + bool certified = certificate is not null + && ReferenceEquals(Volatile.Read(ref _configurationCache), certificate.State) + && certificate.State.Matches(this); + if (!certified) + { + RefreshCachedConfiguration(); + certificate = null; + } if (!(probability >= 0.0 && probability <= 1.0)) throw new ArgumentOutOfRangeException(nameof(probability), "Probability must be between 0 and 1."); - ValidateEvaluation(); + if (!certified) ValidateEvaluation(); if (probability == 0.0) return Minimum; if (probability == 1.0) return Maximum; if (IsZeroInflated && probability <= ZeroWeight) return 0.0; - if (Distributions.Count() == 1 && !IsZeroInflated) + if (Distributions.Length == 1 && !IsZeroInflated) { return Distributions[0].InverseCDF(probability); } @@ -1220,14 +1325,18 @@ public override double InverseCDF(double probability) if (_empiricalCDFCreated) { double empiricalValue = _empiricalCDF.InverseCDF(probability); - return Tools.Clamp(empiricalValue, Minimum, Maximum); + double clampMinimum = certificate is not null && certificate.TryGetMinimum(out double cachedMinimum) + ? cachedMinimum : Minimum; + double clampMaximum = certificate is not null && certificate.TryGetMaximum(out double cachedMaximum) + ? cachedMaximum : Maximum; + return Tools.Clamp(empiricalValue, clampMinimum, clampMaximum); } double componentProbability = IsZeroInflated ? (probability - ZeroWeight) / (1.0 - ZeroWeight) : probability; var componentQuantiles = new List(); - for (int i = 0; i < Distributions.Count(); i++) + for (int i = 0; i < Distributions.Length; i++) { if (Weights[i] == 0) continue; componentQuantiles.Add(IsZeroInflated ? PositiveConditionalQuantile(i, componentProbability) @@ -1349,7 +1458,7 @@ public override double[] GenerateRandomValues(int sampleSize, int seed = -1) public override UnivariateDistributionBase Clone() { var dists = new UnivariateDistributionBase[Distributions.Count()]; - for (int i = 0; i < Distributions.Count(); i++) + for (int i = 0; i < Distributions.Length; i++) dists[i] = Distributions[i].Clone(); return new Mixture(Weights.ToArray(), dists) From 3a9e5981811d0da7671db5014024fe27174f1ca2 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 9 Sep 2026 16:47:52 -0600 Subject: [PATCH 211/222] Guard the configuration snapshot with round-trip and completeness sweeps Adds the snapshot regression suite: a capture/compare round trip per supported family (pinning the capture and inline-compare switches together), a reflection sweep asserting every public settable double, int, bool, and enum property on a supported family flips the compare (the completeness guard for the per-family scalar lists), nested-grandchild invalidation through composite recursion, the derived-component capture refusal, and correlation-matrix entry participation including the bitwise-identical clone case. The sweep immediately caught GeneralizedPareto.Lambda - carried peaks-per-block metadata outside the flattened parameters - which is now captured in both arms so the uniform invariant holds with no exception list. Lambda does not enter evaluation or the canonical string, so the addition is strictness only; extra strictness can only force a string re-comparison, never a wrong match. --- .../Univariate/Base/DistributionSnapshot.cs | 11 +- .../Test_DistributionSnapshotRegressions.cs | 197 ++++++++++++++++++ 2 files changed, 204 insertions(+), 4 deletions(-) create mode 100644 Test_Numerics/Distributions/Univariate/Test_DistributionSnapshotRegressions.cs diff --git a/Numerics/Distributions/Univariate/Base/DistributionSnapshot.cs b/Numerics/Distributions/Univariate/Base/DistributionSnapshot.cs index 9283f089..d1ac21ee 100644 --- a/Numerics/Distributions/Univariate/Base/DistributionSnapshot.cs +++ b/Numerics/Distributions/Univariate/Base/DistributionSnapshot.cs @@ -188,10 +188,11 @@ private static bool MatchScalars(UnivariateDistributionBase distribution, byte f case 12: { var x = (GeneralizedPareto)distribution; - if (k + 3 > bits.Length || bits[k] != BitConverter.DoubleToInt64Bits(x.Xi) + if (k + 4 > bits.Length || bits[k] != BitConverter.DoubleToInt64Bits(x.Xi) || bits[k + 1] != BitConverter.DoubleToInt64Bits(x.Alpha) - || bits[k + 2] != BitConverter.DoubleToInt64Bits(x.Kappa)) return false; - index = k + 3; + || bits[k + 2] != BitConverter.DoubleToInt64Bits(x.Kappa) + || bits[k + 3] != BitConverter.DoubleToInt64Bits(x.Lambda)) return false; + index = k + 4; return true; } case 13: @@ -505,8 +506,10 @@ private static bool VisitScalars(UnivariateDistributionBase distribution, byte f } case 12: { + // Lambda is carried peaks-per-block metadata outside the flattened parameters; + // it is captured so every public settable scalar participates uniformly. var x = (GeneralizedPareto)distribution; - return cursor.Visit(x.Xi) && cursor.Visit(x.Alpha) && cursor.Visit(x.Kappa); + return cursor.Visit(x.Xi) && cursor.Visit(x.Alpha) && cursor.Visit(x.Kappa) && cursor.Visit(x.Lambda); } case 13: { diff --git a/Test_Numerics/Distributions/Univariate/Test_DistributionSnapshotRegressions.cs b/Test_Numerics/Distributions/Univariate/Test_DistributionSnapshotRegressions.cs new file mode 100644 index 00000000..a52130fd --- /dev/null +++ b/Test_Numerics/Distributions/Univariate/Test_DistributionSnapshotRegressions.cs @@ -0,0 +1,197 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Data.Statistics; +using Numerics.Distributions; + +namespace Distributions.Univariate +{ + /// + /// Regression tests for the bitwise configuration snapshot behind the composite + /// configuration and validation caches. + /// + /// + /// The snapshot has two switches that must stay in lockstep: the capture walk and the + /// inline compare walk. The round-trip test guards them against drifting apart, and the + /// reflection sweep guards the per-family scalar lists against new mutable state being + /// added to a supported family without extending its snapshot arm. + /// + [TestClass] + public class Test_DistributionSnapshotRegressions + { + /// One representative, validly parameterized instance per supported family. + /// The instances the snapshot must capture. + private static UnivariateDistributionBase[] SupportedInstances() + { + return + [ + new Normal(10, 2), + new LogNormal() { Mu = 3, Sigma = 0.5 }, + new LnNormal() { Mu = 3, Sigma = 0.5 }, + new LogPearsonTypeIII(3, 0.4, 0.2), + new PearsonTypeIII(100, 20, 0.4), + new GammaDistribution(10, 4), + new Weibull(8, 2.5), + new Gumbel(100, 12), + new GeneralizedExtremeValue(100, 12, 0.1), + new GeneralizedLogistic(100, 12, 0.1), + new GeneralizedNormal(100, 12, 0.1), + new GeneralizedPareto(100, 12, 0.1), + new Exponential(50, 10), + new KappaFour(100, 12, 0.1, 0.2), + new Uniform(2, 12), + new Triangular(2, 5, 12), + new Pert(2, 5, 12), + new Deterministic(7), + new Logistic(100, 12), + new Cauchy(100, 12), + ]; + } + + /// + /// Capture succeeds for every supported family and the compare walk accepts the + /// unchanged instance - the round trip that pins the two switches together. + /// + [TestMethod] + public void CaptureAndCompare_RoundTripEveryFamily() + { + foreach (var distribution in SupportedInstances()) + { + var snapshot = DistributionSnapshot.TryCapture(distribution); + Assert.IsNotNull(snapshot, distribution.GetType().Name + " must be capturable."); + Assert.IsTrue(snapshot.Matches(distribution), distribution.GetType().Name + " must match its own capture."); + } + } + + /// + /// Mutating any public settable double, int, bool, or enum property on a supported + /// family flips the compare to a mismatch - the completeness guard for the per-family + /// scalar lists. + /// + [TestMethod] + public void Capture_EveryPublicSettableScalarFlipsTheMatch() + { + foreach (var distribution in SupportedInstances()) + { + foreach (var property in MutableScalarProperties(distribution.GetType())) + { + var snapshot = DistributionSnapshot.TryCapture(distribution); + Assert.IsNotNull(snapshot, distribution.GetType().Name + " must be capturable."); + object original = property.GetValue(distribution)!; + object mutated = Mutate(property.PropertyType, original); + property.SetValue(distribution, mutated); + try + { + Assert.IsFalse(snapshot.Matches(distribution), + distribution.GetType().Name + "." + property.Name + " must be part of the snapshot."); + } + finally + { + property.SetValue(distribution, original); + } + Assert.IsTrue(snapshot.Matches(distribution), + distribution.GetType().Name + "." + property.Name + " must match again after restoration."); + } + } + } + + /// + /// Composite trees capture recursively: mutating a nested grandchild parameter flips + /// the root compare, and restoring it restores the match. + /// + [TestMethod] + public void Capture_NestedCompositeGrandchildMutationFlipsTheRoot() + { + var grandchild = new Weibull(8, 2.5); + var nested = new Mixture([0.4, 0.6], new UnivariateDistributionBase[] { grandchild, new LnNormal() { Mu = 3, Sigma = 0.5 } }); + var root = new CompetingRisks(new UnivariateDistributionBase[] { nested, new Weibull(6, 3) }); + + var snapshot = DistributionSnapshot.TryCapture(root); + Assert.IsNotNull(snapshot); + Assert.IsTrue(snapshot.Matches(root)); + + double original = grandchild.Kappa; + grandchild.Kappa = original + 0.25; + Assert.IsFalse(snapshot.Matches(root), "A nested grandchild mutation must flip the root compare."); + grandchild.Kappa = original; + Assert.IsTrue(snapshot.Matches(root), "Restoring the grandchild must restore the root match."); + } + + /// + /// A derived component defeats capture entirely, keeping the generic canonical-string + /// path for owners whose components may carry live callbacks. + /// + [TestMethod] + public void Capture_DerivedComponentDefeatsCapture() + { + var root = new Mixture([0.5, 0.5], new UnivariateDistributionBase[] { new DerivedWeibull(8, 2.5), new Weibull(6, 3) }); + Assert.IsNull(DistributionSnapshot.TryCapture(root), "A derived component must defeat capture."); + } + + /// + /// The competing-risks correlation matrix participates in the compare: replacing the + /// matrix with a bitwise-identical clone still matches, while changing one entry flips it. + /// + [TestMethod] + public void Capture_CorrelationMatrixEntriesParticipate() + { + var root = new CompetingRisks(new UnivariateDistributionBase[] { new Weibull(8, 2.5), new Weibull(6, 3) }) + { + Dependency = Probability.DependencyType.CorrelationMatrix, + CorrelationMatrix = new[,] { { 1d, 0.5d }, { 0.5d, 1d } }, + }; + var snapshot = DistributionSnapshot.TryCapture(root); + Assert.IsNotNull(snapshot); + Assert.IsTrue(snapshot.Matches(root)); + + root.CorrelationMatrix = new[,] { { 1d, 0.5d }, { 0.5d, 1d } }; + Assert.IsTrue(snapshot.Matches(root), "A bitwise-identical matrix clone must still match."); + root.CorrelationMatrix = new[,] { { 1d, 0.6d }, { 0.6d, 1d } }; + Assert.IsFalse(snapshot.Matches(root), "A changed correlation entry must flip the compare."); + } + + /// A minimal Weibull subclass for the derived-component refusal. + private sealed class DerivedWeibull : Weibull + { + /// Initializes the derived test distribution. + /// The scale. + /// The shape. + internal DerivedWeibull(double lambda, double kappa) : base(lambda, kappa) { } + } + + /// Enumerates the public settable scalar-like properties of a family. + /// The family type. + /// The properties whose mutation must flip the snapshot compare. + private static IEnumerable MutableScalarProperties(Type type) + { + foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) + { + if (property.SetMethod is null || !property.SetMethod.IsPublic) continue; + var propertyType = property.PropertyType; + if (propertyType != typeof(double) && propertyType != typeof(int) + && propertyType != typeof(bool) && !propertyType.IsEnum) continue; + yield return property; + } + } + + /// Produces a valid, distinct replacement value for a scalar property. + /// The property type. + /// The original value. + /// A value guaranteed to differ from the original. + private static object Mutate(Type type, object original) + { + if (type == typeof(double)) + { + double value = (double)original; + return value * 1.25 + 0.375; + } + if (type == typeof(int)) return (int)original + 1; + if (type == typeof(bool)) return !(bool)original; + var values = Enum.GetValues(type); + foreach (var candidate in values) + if (!candidate.Equals(original)) return candidate; + return original; + } + } +} From ab0ee2865acdbdae1da9516d4e686fd1b5ded70f Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 9 Sep 2026 16:51:39 -0600 Subject: [PATCH 212/222] Route the log-Pearson hot paths through static Pearson evaluators LogPearsonTypeIII allocated and validated a fresh PearsonTypeIII on every LogCDF, LogCCDF, CDF, and InverseCDF call. Following the existing static LogPDF precedent, the Pearson tail and quantile interiors are extracted as internal statics over already-validated parameters (the instance methods keep their guards and delegate verbatim), and the wrapper's hot sites call them directly - its own validation and support short-circuits already guarantee the preconditions, and both classes validate the identical constraint set, so the bypassed instance guards were unreachable. The statics compute the gamma shape once per call where the instance path re-evaluated the Alpha property expression; both evaluations of the same pure expression produce identical bits, so results are unchanged. Measured (Debug assembly, per call): wrapper CDF 56 B -> 0 B and 1,029 ns -> 934 ns; wrapper InverseCDF 56 B -> 0 B and 4,566 ns -> 4,358 ns. The uncertainty and fitting paths keep their per-call construction (cold by design). All 84 Pearson and log-Pearson family tests pass. --- .../Univariate/LogPearsonTypeIII.cs | 6 +- .../Univariate/PearsonTypeIII.cs | 78 ++++++++++++++----- 2 files changed, 62 insertions(+), 22 deletions(-) diff --git a/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs b/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs index 397232bb..8b04d07c 100644 --- a/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs +++ b/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs @@ -145,7 +145,7 @@ public override double LogCDF(double x) if (!_parametersValid) ValidateParameters(Mu, Sigma, Gamma, true); if (x <= Minimum) return double.NegativeInfinity; if (x >= Maximum) return 0d; - return new PearsonTypeIII(Mu, Sigma, Gamma).LogCDF(Math.Log(x, Base)); + return PearsonTypeIII.LogTail(Mu, Sigma, Gamma, Math.Log(x, Base), false); } /// @@ -157,7 +157,7 @@ public override double LogCCDF(double x) if (!_parametersValid) ValidateParameters(Mu, Sigma, Gamma, true); if (x <= Minimum) return 0d; if (x >= Maximum) return double.NegativeInfinity; - return new PearsonTypeIII(Mu, Sigma, Gamma).LogCCDF(Math.Log(x, Base)); + return PearsonTypeIII.LogTail(Mu, Sigma, Gamma, Math.Log(x, Base), true); } /// Log raw-moment contribution after removing the location, with each moment's existence checked separately. @@ -787,7 +787,7 @@ public override double InverseCDF(double probability) if (!_parametersValid) ValidateParameters(Mu, Sigma, Gamma, true); if (probability == 0d) return Minimum; if (probability == 1d) return Maximum; - return Math.Exp(new PearsonTypeIII(Mu, Sigma, Gamma).InverseCDF(probability) * Math.Log(Base)); + return Math.Exp(PearsonTypeIII.InverseCDF(Mu, Sigma, Gamma, probability) * Math.Log(Base)); } /// diff --git a/Numerics/Distributions/Univariate/PearsonTypeIII.cs b/Numerics/Distributions/Univariate/PearsonTypeIII.cs index 233f38ba..1793d91b 100644 --- a/Numerics/Distributions/Univariate/PearsonTypeIII.cs +++ b/Numerics/Distributions/Univariate/PearsonTypeIII.cs @@ -130,26 +130,39 @@ public double Alpha private double LogTail(double x, bool upper) { if (!_parametersValid) ValidateParameters(Mu, Sigma, Gamma, true); - if (x <= Minimum) return upper ? 0d : double.NegativeInfinity; - if (x >= Maximum) return upper ? double.NegativeInfinity : 0d; - double z = DistributionNumerics.Standardize(x, Mu, Sigma); + return LogTail(Mu, Sigma, Gamma, x, upper); + } + + /// Evaluates the signed gamma tail from parameters already validated by an equivalent public contract. + /// The finite mean. + /// The finite positive standard deviation. + /// The finite skew in the supported interval. + /// The observation in physical coordinates. + /// to evaluate the survival probability; to evaluate the cumulative probability. + /// The requested log probability, including support-endpoint limits. + internal static double LogTail(double mu, double sigma, double gamma, double x, bool upper) + { + if (x <= (gamma > 0d ? mu - sigma * (2d / gamma) : double.NegativeInfinity)) return upper ? 0d : double.NegativeInfinity; + if (x >= (gamma < 0d ? mu - sigma * (2d / gamma) : double.PositiveInfinity)) return upper ? double.NegativeInfinity : 0d; + double z = DistributionNumerics.Standardize(x, mu, sigma); double normal = upper ? DistributionNumerics.NormalLogSurvival(z) : DistributionNumerics.NormalLogCDF(z); - if (Gamma == 0d) return normal; - if (UseLocalTailExpansion(z)) + if (gamma == 0d) return normal; + if (UseLocalTailExpansion(gamma, z)) { // Integrate the gamma cumulant expansion analytically using probabilists' Hermite polynomials. - double z2 = z * z, g2 = Gamma * Gamma; + double z2 = z * z, g2 = gamma * gamma; double h2 = z2 - 1d, h3 = z * (z2 - 3d), h4 = z2 * z2 - 6d * z2 + 3d; double h5 = z * (z2 * z2 - 10d * z2 + 15d); double h6 = z2 * z2 * z2 - 15d * z2 * z2 + 45d * z2 - 15d; double h8 = z2 * z2 * z2 * z2 - 28d * z2 * z2 * z2 + 210d * z2 * z2 - 420d * z2 + 105d; - double correction = Gamma * h2 / 6d + g2 * (h3 / 16d + h5 / 72d) - + g2 * Gamma * (h4 / 40d + h6 / 96d + h8 / 1296d); + double correction = gamma * h2 / 6d + g2 * (h3 / 16d + h5 / 72d) + + g2 * gamma * (h4 / 40d + h6 / 96d + h8 / 1296d); double relative = Math.Exp(-0.5d * z2 - Math.Log(Tools.Sqrt2PI) - normal) * correction; return normal + Tools.Log1p(upper ? relative : -relative); } - double unit = UnitGammaValue(x, z); - return upper == (Gamma > 0d) ? DistributionNumerics.GammaLogSurvival(Alpha, unit) : DistributionNumerics.GammaLogCDF(Alpha, unit); + double unit = UnitGammaValue(mu, sigma, gamma, x, z); + double alpha = Math.Pow(2d / gamma, 2d); + return upper == (gamma > 0d) ? DistributionNumerics.GammaLogSurvival(alpha, unit) : DistributionNumerics.GammaLogCDF(alpha, unit); } /// Forms the unit gamma coordinate in centered form for large shape. @@ -192,8 +205,15 @@ private static bool UseLocalTailExpansion(double gamma, double z) /// The standard Normal quantile. /// when the configured skew and quantile satisfy the local quantile-expansion error bound; otherwise, . private bool UseLocalQuantileExpansion(double z) + => UseLocalQuantileExpansion(Gamma, z); + + /// Bounds local quantile-expansion error directly from a validated skew. + /// The validated Pearson skew. + /// The standard Normal quantile. + /// when the skew and quantile satisfy the local quantile-expansion error bound; otherwise, . + private static bool UseLocalQuantileExpansion(double gamma, double z) { - return Math.Abs(Gamma) <= 1E-3 && Math.Abs(Gamma) * Math.Pow(1d + Math.Abs(z), 3d) <= 0.02d; + return Math.Abs(gamma) <= 1E-3 && Math.Abs(gamma) * Math.Pow(1d + Math.Abs(z), 3d) <= 0.02d; } /// Evaluates the smooth gamma quantile expansion and its skew derivative through cubic order. @@ -201,13 +221,21 @@ private bool UseLocalQuantileExpansion(double z) /// The derivative of the returned standardized quantile with respect to the configured skew. /// The standardized Pearson quantile from the local skew expansion. private double LocalStandardQuantile(double z, out double derivative) + => LocalStandardQuantile(Gamma, z, out derivative); + + /// Evaluates the smooth gamma quantile expansion directly from a validated skew. + /// The validated Pearson skew. + /// The standard Normal quantile. + /// The derivative of the returned standardized quantile with respect to the skew. + /// The standardized Pearson quantile from the local skew expansion. + private static double LocalStandardQuantile(double gamma, double z, out double derivative) { double z2 = z * z; double first = (z2 - 1d) / 6d; double second = z * (z2 - 7d) / 144d; double third = -(3d * z2 * z2 + 7d * z2 - 16d) / 6480d; - derivative = first + Gamma * (2d * second + 3d * Gamma * third); - return z + Gamma * (first + Gamma * (second + Gamma * third)); + derivative = first + gamma * (2d * second + 3d * gamma * third); + return z + gamma * (first + gamma * (second + gamma * third)); } /// @@ -699,13 +727,25 @@ public override double InverseCDF(double probability) if (double.IsNaN(probability) || probability < 0d || probability > 1d) throw new ArgumentOutOfRangeException(nameof(probability), "Probability must be between zero and one."); if (!_parametersValid) ValidateParameters(Mu, Sigma, Gamma, true); - if (probability == 0d) return Minimum; - if (probability == 1d) return Maximum; + return InverseCDF(Mu, Sigma, Gamma, probability); + } + + /// Evaluates the quantile from parameters already validated by an equivalent public contract. + /// The finite mean. + /// The finite positive standard deviation. + /// The finite skew in the supported interval. + /// The nonexceedance probability, in the closed unit interval. + /// The Pearson quantile, including the support-endpoint limits. + internal static double InverseCDF(double mu, double sigma, double gamma, double probability) + { + if (probability == 0d) return gamma > 0d ? mu - sigma * (2d / gamma) : double.NegativeInfinity; + if (probability == 1d) return gamma < 0d ? mu - sigma * (2d / gamma) : double.PositiveInfinity; double z = Normal.StandardZ(probability); - if (Gamma == 0d) return Mu + Sigma * z; - if (UseLocalQuantileExpansion(z)) return Mu + Sigma * LocalStandardQuantile(z, out _); - double unit = DistributionNumerics.GammaInverseCDF(Alpha, probability, Gamma < 0d); - return Mu + Sigma * ((Gamma / 2d) * (unit - Alpha)); + if (gamma == 0d) return mu + sigma * z; + if (UseLocalQuantileExpansion(gamma, z)) return mu + sigma * LocalStandardQuantile(gamma, z, out _); + double alpha = Math.Pow(2d / gamma, 2d); + double unit = DistributionNumerics.GammaInverseCDF(alpha, probability, gamma < 0d); + return mu + sigma * ((gamma / 2d) * (unit - alpha)); } /// From 7a80e354a1feb80bfe00e241cc1db46d15d5fb20 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Wed, 9 Sep 2026 16:53:49 -0600 Subject: [PATCH 213/222] Co-locate single-consumer helpers and move the kappa kernels to Base DistributionEndpointTail is consumed only by CompetingRisks and MixtureLogWeights only by Mixture; both internal helpers move into their consumers' source files verbatim. KappaFourBoundary and KappaExpectedInformation are shared kernels - the boundary transform serves KappaFour, GeneralizedLogistic, and GeneralizedNormal, and the expected-information integration serves KappaFour, GeneralizedExtremeValue, and GeneralizedLogistic - so their files move to the Base folder beside the other shared distribution numerics rather than into KappaFour. StandardErrorExtensions stays in its own file: it is a public extension API surface. Pure file organization; no code changes, both target frameworks build clean, and all 305 touched-family tests pass. --- .../Base/DistributionEndpointTail.cs | 101 ------------------ .../{ => Base}/KappaExpectedInformation.cs | 0 .../{ => Base}/KappaFourBoundary.cs | 0 .../Univariate/Base/MixtureLogWeights.cs | 34 ------ .../Univariate/CompetingRisks.cs | 98 +++++++++++++++++ Numerics/Distributions/Univariate/Mixture.cs | 31 ++++++ 6 files changed, 129 insertions(+), 135 deletions(-) delete mode 100644 Numerics/Distributions/Univariate/Base/DistributionEndpointTail.cs rename Numerics/Distributions/Univariate/{ => Base}/KappaExpectedInformation.cs (100%) rename Numerics/Distributions/Univariate/{ => Base}/KappaFourBoundary.cs (100%) delete mode 100644 Numerics/Distributions/Univariate/Base/MixtureLogWeights.cs diff --git a/Numerics/Distributions/Univariate/Base/DistributionEndpointTail.cs b/Numerics/Distributions/Univariate/Base/DistributionEndpointTail.cs deleted file mode 100644 index 27c25e8e..00000000 --- a/Numerics/Distributions/Univariate/Base/DistributionEndpointTail.cs +++ /dev/null @@ -1,101 +0,0 @@ -using System; -using Numerics.Mathematics.SpecialFunctions; - -namespace Numerics.Distributions -{ - /// One-sided endpoint expansions for independent products of zero tails and infinite densities. - internal static class DistributionEndpointTail - { - /// Returns tail ~ exp(logCoefficient)*distance^power*log(1/distance)^logPower. - /// The distribution whose finite endpoint behavior is requested. - /// to describe the lower CDF tail; to describe the upper survival tail. - /// The exponent applied to distance from the finite endpoint. - /// The exponent applied to the logarithm of the reciprocal endpoint distance. - /// The logarithm of the expansion coefficient. - /// when the distribution has a recognized expansion; otherwise, . - /// These are finite lower CDF or upper survival endpoint limits. Infinite power denotes - /// faster-than-polynomial decay. No numerical endpoint offset or density floor is used. - internal static bool TryExpansion(UnivariateDistributionBase distribution, bool lower, - out double power, out double logPower, out double logCoefficient) - { - power = logPower = logCoefficient = 0; - switch (distribution) - { - case GammaDistribution gamma when lower: - power = gamma.Kappa; - logCoefficient = -Gamma.LogGamma(power + 1) - power * Math.Log(gamma.Theta); - return true; - case Weibull weibull when lower: - power = weibull.Kappa; - logCoefficient = -power * Math.Log(weibull.Lambda); - return true; - case Exponential exponential when lower: - power = 1; logCoefficient = -Math.Log(exponential.Alpha); - return true; - case Uniform uniform: - power = 1; logCoefficient = -Math.Log(uniform.Max - uniform.Min); - return true; - case GeneralizedPareto pareto: - power = lower ? 1 : 1 / pareto.Kappa; - logCoefficient = lower ? -Math.Log(pareto.Alpha) : power * (Math.Log(pareto.Kappa) - Math.Log(pareto.Alpha)); - return true; - case GeneralizedExtremeValue extreme: - power = lower ? double.PositiveInfinity : 1 / extreme.Kappa; - logCoefficient = lower ? 0 : power * (Math.Log(extreme.Kappa) - Math.Log(extreme.Alpha)); - return true; - case GeneralizedLogistic logistic: - power = 1 / Math.Abs(logistic.Kappa); - logCoefficient = power * (Math.Log(Math.Abs(logistic.Kappa)) - Math.Log(logistic.Alpha)); - return true; - case GeneralizedNormal _: - case LnNormal _: - case LogNormal _: - power = double.PositiveInfinity; - return true; - case PearsonTypeIII pearson: - power = pearson.Alpha; - logCoefficient = -Gamma.LogGamma(power + 1) - power * Math.Log(Math.Abs(pearson.Beta)); - return true; - case LogPearsonTypeIII pearson: - if (pearson.Gamma == 0) { power = double.PositiveInfinity; return true; } - if (lower && pearson.Gamma < 0) - { - // A reflected gamma survival becomes an algebraic-logarithmic lower tail after exponentiation. - power = 1 / (Math.Abs(pearson.Beta) * Math.Log(pearson.Base)); - logPower = pearson.Alpha - 1; - logCoefficient = -power * pearson.Xi * Math.Log(pearson.Base) - + logPower * Math.Log(power) - Gamma.LogGamma(pearson.Alpha); - } - else - { - power = pearson.Alpha; - double logEndpoint = pearson.Xi * Math.Log(pearson.Base); - logCoefficient = -Gamma.LogGamma(power + 1) - - power * (Math.Log(Math.Abs(pearson.Beta)) + Math.Log(Math.Log(pearson.Base)) + logEndpoint); - } - return true; - case KappaFour kappa: - if (!lower) - { - power = 1 / kappa.Kappa; - logCoefficient = power * (Math.Log(kappa.Kappa) - Math.Log(kappa.Alpha)); - } - else if (kappa.Hondo > 0) - { - power = 1 / kappa.Hondo; - logCoefficient = power * (kappa.Kappa * Math.Log(kappa.Hondo) - Math.Log(kappa.Alpha)); - } - else if (kappa.Hondo < 0) - { - power = 1 / (kappa.Kappa * kappa.Hondo); - logCoefficient = Math.Log(-kappa.Hondo) / kappa.Hondo - + power * (Math.Log(-kappa.Kappa) - Math.Log(kappa.Alpha)); - } - else power = double.PositiveInfinity; - return true; - default: - return false; - } - } - } -} diff --git a/Numerics/Distributions/Univariate/KappaExpectedInformation.cs b/Numerics/Distributions/Univariate/Base/KappaExpectedInformation.cs similarity index 100% rename from Numerics/Distributions/Univariate/KappaExpectedInformation.cs rename to Numerics/Distributions/Univariate/Base/KappaExpectedInformation.cs diff --git a/Numerics/Distributions/Univariate/KappaFourBoundary.cs b/Numerics/Distributions/Univariate/Base/KappaFourBoundary.cs similarity index 100% rename from Numerics/Distributions/Univariate/KappaFourBoundary.cs rename to Numerics/Distributions/Univariate/Base/KappaFourBoundary.cs diff --git a/Numerics/Distributions/Univariate/Base/MixtureLogWeights.cs b/Numerics/Distributions/Univariate/Base/MixtureLogWeights.cs deleted file mode 100644 index 89fe96b7..00000000 --- a/Numerics/Distributions/Univariate/Base/MixtureLogWeights.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; - -namespace Numerics.Distributions -{ - /// Normalizes component weights for a single logarithmic mixture observation. - internal static class MixtureLogWeights - { - /// Returns the row log probability and corresponding responsibilities. - /// The component log probabilities for one observation. - /// The corresponding nonnegative mixture weights. - /// The destination populated with normalized component responsibilities. - /// The logarithm of the weighted row probability, or a nonfinite value when the row cannot be normalized. - /// Nonfinite or impossible rows are returned as nonfinite log probabilities so the - /// caller can retain its observation-specific error message and aggregate likelihood convention. - internal static double Normalize(double[] logDensities, double[] weights, double[] responsibilities) - { - double maximum = double.NegativeInfinity; - for (int i = 0; i < weights.Length; i++) - if (weights[i] > 0) maximum = Math.Max(maximum, logDensities[i]); - if (!Tools.IsFinite(maximum)) return maximum; - double weightedMaximum = double.NegativeInfinity; - for (int i = 0; i < weights.Length; i++) - { - responsibilities[i] = weights[i] > 0 ? (logDensities[i] - maximum) + Math.Log(weights[i]) : double.NegativeInfinity; - weightedMaximum = Math.Max(weightedMaximum, responsibilities[i]); - } - double sum = 0; - for (int i = 0; i < weights.Length; i++) sum += Math.Exp(responsibilities[i] - weightedMaximum); - double centeredLogRow = weightedMaximum + Math.Log(sum); - for (int i = 0; i < weights.Length; i++) responsibilities[i] = Math.Exp(responsibilities[i] - centeredLogRow); - return maximum + centeredLogRow; - } - } -} diff --git a/Numerics/Distributions/Univariate/CompetingRisks.cs b/Numerics/Distributions/Univariate/CompetingRisks.cs index 881a58fb..e20756ac 100644 --- a/Numerics/Distributions/Univariate/CompetingRisks.cs +++ b/Numerics/Distributions/Univariate/CompetingRisks.cs @@ -13,6 +13,7 @@ using System.Threading; using System.Xml.Linq; +using Numerics.Mathematics.SpecialFunctions; namespace Numerics.Distributions { /// @@ -1524,4 +1525,101 @@ public override XElement ToXElement() } } + + + /// One-sided endpoint expansions for independent products of zero tails and infinite densities. + internal static class DistributionEndpointTail + { + /// Returns tail ~ exp(logCoefficient)*distance^power*log(1/distance)^logPower. + /// The distribution whose finite endpoint behavior is requested. + /// to describe the lower CDF tail; to describe the upper survival tail. + /// The exponent applied to distance from the finite endpoint. + /// The exponent applied to the logarithm of the reciprocal endpoint distance. + /// The logarithm of the expansion coefficient. + /// when the distribution has a recognized expansion; otherwise, . + /// These are finite lower CDF or upper survival endpoint limits. Infinite power denotes + /// faster-than-polynomial decay. No numerical endpoint offset or density floor is used. + internal static bool TryExpansion(UnivariateDistributionBase distribution, bool lower, + out double power, out double logPower, out double logCoefficient) + { + power = logPower = logCoefficient = 0; + switch (distribution) + { + case GammaDistribution gamma when lower: + power = gamma.Kappa; + logCoefficient = -Gamma.LogGamma(power + 1) - power * Math.Log(gamma.Theta); + return true; + case Weibull weibull when lower: + power = weibull.Kappa; + logCoefficient = -power * Math.Log(weibull.Lambda); + return true; + case Exponential exponential when lower: + power = 1; logCoefficient = -Math.Log(exponential.Alpha); + return true; + case Uniform uniform: + power = 1; logCoefficient = -Math.Log(uniform.Max - uniform.Min); + return true; + case GeneralizedPareto pareto: + power = lower ? 1 : 1 / pareto.Kappa; + logCoefficient = lower ? -Math.Log(pareto.Alpha) : power * (Math.Log(pareto.Kappa) - Math.Log(pareto.Alpha)); + return true; + case GeneralizedExtremeValue extreme: + power = lower ? double.PositiveInfinity : 1 / extreme.Kappa; + logCoefficient = lower ? 0 : power * (Math.Log(extreme.Kappa) - Math.Log(extreme.Alpha)); + return true; + case GeneralizedLogistic logistic: + power = 1 / Math.Abs(logistic.Kappa); + logCoefficient = power * (Math.Log(Math.Abs(logistic.Kappa)) - Math.Log(logistic.Alpha)); + return true; + case GeneralizedNormal _: + case LnNormal _: + case LogNormal _: + power = double.PositiveInfinity; + return true; + case PearsonTypeIII pearson: + power = pearson.Alpha; + logCoefficient = -Gamma.LogGamma(power + 1) - power * Math.Log(Math.Abs(pearson.Beta)); + return true; + case LogPearsonTypeIII pearson: + if (pearson.Gamma == 0) { power = double.PositiveInfinity; return true; } + if (lower && pearson.Gamma < 0) + { + // A reflected gamma survival becomes an algebraic-logarithmic lower tail after exponentiation. + power = 1 / (Math.Abs(pearson.Beta) * Math.Log(pearson.Base)); + logPower = pearson.Alpha - 1; + logCoefficient = -power * pearson.Xi * Math.Log(pearson.Base) + + logPower * Math.Log(power) - Gamma.LogGamma(pearson.Alpha); + } + else + { + power = pearson.Alpha; + double logEndpoint = pearson.Xi * Math.Log(pearson.Base); + logCoefficient = -Gamma.LogGamma(power + 1) + - power * (Math.Log(Math.Abs(pearson.Beta)) + Math.Log(Math.Log(pearson.Base)) + logEndpoint); + } + return true; + case KappaFour kappa: + if (!lower) + { + power = 1 / kappa.Kappa; + logCoefficient = power * (Math.Log(kappa.Kappa) - Math.Log(kappa.Alpha)); + } + else if (kappa.Hondo > 0) + { + power = 1 / kappa.Hondo; + logCoefficient = power * (kappa.Kappa * Math.Log(kappa.Hondo) - Math.Log(kappa.Alpha)); + } + else if (kappa.Hondo < 0) + { + power = 1 / (kappa.Kappa * kappa.Hondo); + logCoefficient = Math.Log(-kappa.Hondo) / kappa.Hondo + + power * (Math.Log(-kappa.Kappa) - Math.Log(kappa.Alpha)); + } + else power = double.PositiveInfinity; + return true; + default: + return false; + } + } + } } diff --git a/Numerics/Distributions/Univariate/Mixture.cs b/Numerics/Distributions/Univariate/Mixture.cs index eefdfc34..3b4bc12d 100644 --- a/Numerics/Distributions/Univariate/Mixture.cs +++ b/Numerics/Distributions/Univariate/Mixture.cs @@ -1585,4 +1585,35 @@ public override XElement ToXElement() } } + + + /// Normalizes component weights for a single logarithmic mixture observation. + internal static class MixtureLogWeights + { + /// Returns the row log probability and corresponding responsibilities. + /// The component log probabilities for one observation. + /// The corresponding nonnegative mixture weights. + /// The destination populated with normalized component responsibilities. + /// The logarithm of the weighted row probability, or a nonfinite value when the row cannot be normalized. + /// Nonfinite or impossible rows are returned as nonfinite log probabilities so the + /// caller can retain its observation-specific error message and aggregate likelihood convention. + internal static double Normalize(double[] logDensities, double[] weights, double[] responsibilities) + { + double maximum = double.NegativeInfinity; + for (int i = 0; i < weights.Length; i++) + if (weights[i] > 0) maximum = Math.Max(maximum, logDensities[i]); + if (!Tools.IsFinite(maximum)) return maximum; + double weightedMaximum = double.NegativeInfinity; + for (int i = 0; i < weights.Length; i++) + { + responsibilities[i] = weights[i] > 0 ? (logDensities[i] - maximum) + Math.Log(weights[i]) : double.NegativeInfinity; + weightedMaximum = Math.Max(weightedMaximum, responsibilities[i]); + } + double sum = 0; + for (int i = 0; i < weights.Length; i++) sum += Math.Exp(responsibilities[i] - weightedMaximum); + double centeredLogRow = weightedMaximum + Math.Log(sum); + for (int i = 0; i < weights.Length; i++) responsibilities[i] = Math.Exp(responsibilities[i] - centeredLogRow); + return maximum + centeredLogRow; + } + } } From 202095a4e4ba6f2876e262057d15997dbc1d1e1a Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 11 Sep 2026 16:27:30 -0600 Subject: [PATCH 214/222] Minor changes to parameter constraints --- Numerics/Distributions/Univariate/Exponential.cs | 2 +- Numerics/Distributions/Univariate/GammaDistribution.cs | 2 +- Numerics/Distributions/Univariate/LogNormal.cs | 7 ++++--- Numerics/Distributions/Univariate/LogPearsonTypeIII.cs | 7 ++++--- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Numerics/Distributions/Univariate/Exponential.cs b/Numerics/Distributions/Univariate/Exponential.cs index 4c8a23c1..f16facf7 100644 --- a/Numerics/Distributions/Univariate/Exponential.cs +++ b/Numerics/Distributions/Univariate/Exponential.cs @@ -353,7 +353,7 @@ private Tuple GetLegacyParameterConstraints(IList< // Get bounds of location if (initialVals[0] == 0d) initialVals[0] = Tools.DoubleMachineEpsilon; lowerVals[0] = initialVals[0] - Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])))); - upperVals[0] = minData; + upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[0]) + 1d)); // Get bounds of scale lowerVals[1] = Tools.DoubleMachineEpsilon; diff --git a/Numerics/Distributions/Univariate/GammaDistribution.cs b/Numerics/Distributions/Univariate/GammaDistribution.cs index a6cc2e32..1c371a1c 100644 --- a/Numerics/Distributions/Univariate/GammaDistribution.cs +++ b/Numerics/Distributions/Univariate/GammaDistribution.cs @@ -500,7 +500,7 @@ private Tuple GetLegacyParameterConstraints(IList< var lowerVals = new double[NumberOfParameters]; var upperVals = new double[NumberOfParameters]; // Get initial values - initialVals = LegacyConstraintParametersFromMoments(Statistics.ProductMoments(sample)); + initialVals = ParametersFromMoments(Statistics.ProductMoments(sample)); // Get bounds of scale lowerVals[0] = Tools.DoubleMachineEpsilon; upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[0]) + 1d)); diff --git a/Numerics/Distributions/Univariate/LogNormal.cs b/Numerics/Distributions/Univariate/LogNormal.cs index 9b1befc8..092cd585 100644 --- a/Numerics/Distributions/Univariate/LogNormal.cs +++ b/Numerics/Distributions/Univariate/LogNormal.cs @@ -448,11 +448,12 @@ private Tuple GetLegacyParameterConstraints(IList< // symmetric about zero from the magnitude of the initial value, matching Normal's // location bounds. A machine-epsilon floor here would reject any sub-unity sample // before a fit could start. + double real = Math.Exp(initialVals[0] / K); if (initialVals[0] == 0d) initialVals[0] = Tools.DoubleMachineEpsilon; - lowerVals[0] = -Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); - upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); + lowerVals[0] = Math.Floor(Math.Log(Math.Pow(10d, Math.Floor(Math.Log10(real)) - 1d), Base)); + upperVals[0] = Math.Ceiling(Math.Log(Math.Pow(10d, Math.Ceiling(Math.Log10(real)) + 1d), Base)); // Get bounds of standard deviation - double real = Math.Exp(initialVals[1] / K); + real = Math.Exp(initialVals[1] / K); lowerVals[1] = Tools.DoubleMachineEpsilon; upperVals[1] = Math.Ceiling(Math.Log(Math.Pow(10d, Math.Ceiling(Math.Log10(real) + 1d)), Base)); upperVals[1] = double.IsNaN(upperVals[1]) ? 4 : upperVals[1]; diff --git a/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs b/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs index 8b04d07c..2eace433 100644 --- a/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs +++ b/Numerics/Distributions/Univariate/LogPearsonTypeIII.cs @@ -683,11 +683,12 @@ private Tuple GetLegacyParameterConstraints(IList< // symmetric about zero from the magnitude of the initial value, matching Normal's // location bounds. A machine-epsilon floor here would reject any sub-unity sample // before a fit could start. + double real = Math.Exp(initialVals[0] / K); if (initialVals[0] == 0d) initialVals[0] = Tools.DoubleMachineEpsilon; - lowerVals[0] = -Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); - upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])) + 1d)); + lowerVals[0] = Math.Floor(Math.Log(Math.Pow(10d, Math.Floor(Math.Log10(real)) - 1d), Base)); + upperVals[0] = Math.Ceiling(Math.Log(Math.Pow(10d, Math.Ceiling(Math.Log10(real)) + 1d), Base)); // Get bounds of standard deviation - double real = Math.Exp(initialVals[1] / K); + real = Math.Exp(initialVals[1] / K); lowerVals[1] = Tools.DoubleMachineEpsilon; upperVals[1] = Math.Ceiling(Math.Log(Math.Pow(10d, Math.Ceiling(Math.Log10(real) + 1d)), Base)); upperVals[1] = double.IsNaN(upperVals[1]) ? 4 : upperVals[1]; From 2bba50035e8f31f7d56159d76192b9b0d94cfc74 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Tue, 15 Sep 2026 08:59:41 -0600 Subject: [PATCH 215/222] Fixing exponential back to MLE bounds for location. --- Numerics/Distributions/Univariate/Exponential.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Numerics/Distributions/Univariate/Exponential.cs b/Numerics/Distributions/Univariate/Exponential.cs index f16facf7..f46dcc8f 100644 --- a/Numerics/Distributions/Univariate/Exponential.cs +++ b/Numerics/Distributions/Univariate/Exponential.cs @@ -353,7 +353,7 @@ private Tuple GetLegacyParameterConstraints(IList< // Get bounds of location if (initialVals[0] == 0d) initialVals[0] = Tools.DoubleMachineEpsilon; lowerVals[0] = initialVals[0] - Math.Pow(10d, Math.Ceiling(Math.Log10(Math.Abs(initialVals[0])))); - upperVals[0] = Math.Pow(10d, Math.Ceiling(Math.Log10(initialVals[0]) + 1d)); + upperVals[0] = minData + Tools.DoubleMachineEpsilon; // Get bounds of scale lowerVals[1] = Tools.DoubleMachineEpsilon; From e48564099185edd05c717606bd71d7ee5724c1ad Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Tue, 15 Sep 2026 09:25:34 -0600 Subject: [PATCH 216/222] Repin parameter constraint test baselines --- .../Univariate/Test_Exponential.cs | 3 +- .../Test_LegacyParameterConstraints.cs | 84 ++++++++++--------- .../Test_NonpositiveConstraintRegressions.cs | 17 ++-- 3 files changed, 54 insertions(+), 50 deletions(-) diff --git a/Test_Numerics/Distributions/Univariate/Test_Exponential.cs b/Test_Numerics/Distributions/Univariate/Test_Exponential.cs index 1ef09ac2..594f5517 100644 --- a/Test_Numerics/Distributions/Univariate/Test_Exponential.cs +++ b/Test_Numerics/Distributions/Univariate/Test_Exponential.cs @@ -114,7 +114,8 @@ public void Test_EXP_ParameterConstraints_NegativeLocationInitial_HasFiniteBound Assert.IsLessThan(0d, constraints.Item1[0]); Assert.IsFalse(double.IsNaN(constraints.Item2[0]) || double.IsInfinity(constraints.Item2[0])); Assert.IsFalse(double.IsNaN(constraints.Item3[0]) || double.IsInfinity(constraints.Item3[0])); - Assert.AreEqual(0.1d, constraints.Item3[0]); + // Repinned to 2bba500: the sample minimum plus machine epsilon. + Assert.AreEqual(0.10000000000000012d, constraints.Item3[0]); Assert.IsLessThan(constraints.Item3[0], constraints.Item1[0]); Assert.IsGreaterThan(constraints.Item2[0], constraints.Item1[0]); } diff --git a/Test_Numerics/Distributions/Univariate/Test_LegacyParameterConstraints.cs b/Test_Numerics/Distributions/Univariate/Test_LegacyParameterConstraints.cs index 32da24d9..8212d10c 100644 --- a/Test_Numerics/Distributions/Univariate/Test_LegacyParameterConstraints.cs +++ b/Test_Numerics/Distributions/Univariate/Test_LegacyParameterConstraints.cs @@ -2,11 +2,13 @@ namespace Distributions { - /// Freezes valid prior envelopes and initial values from the pre-hardening baseline. + /// Pins approved prior envelopes and initial values for the legacy constraint fixtures. [TestClass] public class Test_LegacyParameterConstraints { - // Captured from d80bfa8621c48a78cf4ebad7f01326841fac37aa on net8.0. + // Original fixtures captured from d80bfa8621c48a78cf4ebad7f01326841fac37aa on net8.0. + // Gamma initializers and LogNormal/LogPearsonTypeIII location bounds repinned to 202095a. + // Exponential location upper bounds repinned to 2bba500. // These fixtures freeze valid initialization and family-specific prior envelopes. /// Checks initialization and bounds against the literal baseline fixture. [TestMethod] @@ -25,7 +27,7 @@ public void Exponential_skewed_PreservesBaselineConstraints() var result = new Exponential().GetParameterConstraints(new double[] { 1d, 2d, 4d, 8d, 16d, 32d }); AssertArray(new double[] { -0.9d, 11.4d }, result.Item1, "initial"); CollectionAssert.AreEqual(new double[] { -1.9d, 1.11022302462516e-16d }, result.Item2, "lower"); - CollectionAssert.AreEqual(new double[] { 1d, 1000d }, result.Item3, "upper"); + CollectionAssert.AreEqual(new double[] { 1.0000000000000002d, 1000d }, result.Item3, "upper"); } /// Checks initialization and bounds against the literal baseline fixture. @@ -35,7 +37,7 @@ public void Exponential_nearUnity_PreservesBaselineConstraints() var result = new Exponential().GetParameterConstraints(new double[] { 1.001d, 1.002d, 1.003d, 1.004d }); AssertArray(new double[] { 1.0005d, 0.002000000000000076d }, result.Item1, "initial"); CollectionAssert.AreEqual(new double[] { -8.9995d, 1.11022302462516e-16d }, result.Item2, "lower"); - CollectionAssert.AreEqual(new double[] { 1.001d, 0.1d }, result.Item3, "upper"); + CollectionAssert.AreEqual(new double[] { 1.0010000000000001d, 0.1d }, result.Item3, "upper"); } /// Checks initialization and bounds against the literal baseline fixture. @@ -65,7 +67,7 @@ public void Exponential_subUnity_PreservesBaselineConstraints() var result = new Exponential().GetParameterConstraints(new double[] { 0.1d, 0.2d, 0.4d, 0.8d }); AssertArray(new double[] { 0.00833333333333334d, 0.3666666666666667d }, result.Item1, "initial"); CollectionAssert.AreEqual(new double[] { -0.00166666666666666d, 1.11022302462516e-16d }, result.Item2, "lower"); - CollectionAssert.AreEqual(new double[] { 0.1d, 10d }, result.Item3, "upper"); + CollectionAssert.AreEqual(new double[] { 0.10000000000000012d, 10d }, result.Item3, "upper"); } /// Checks initialization and bounds against the literal baseline fixture. @@ -73,7 +75,7 @@ public void Exponential_subUnity_PreservesBaselineConstraints() public void GammaDistribution_ordinary_PreservesBaselineConstraints() { var result = new GammaDistribution().GetParameterConstraints(new double[] { 10d, 12d, 14d, 16d }); - AssertArray(new double[] { 0.5128205128205128d, 25.35d }, result.Item1, "initial"); + AssertArray(new double[] { 0.5128205128205128d, 25.349999999999998d }, result.Item1, "initial"); CollectionAssert.AreEqual(new double[] { 1.11022302462516e-16d, 1.11022302462516e-16d }, result.Item2, "lower"); CollectionAssert.AreEqual(new double[] { 10d, 1000d }, result.Item3, "upper"); } @@ -83,7 +85,7 @@ public void GammaDistribution_ordinary_PreservesBaselineConstraints() public void GammaDistribution_skewed_PreservesBaselineConstraints() { var result = new GammaDistribution().GetParameterConstraints(new double[] { 1d, 2d, 4d, 8d, 16d, 32d }); - AssertArray(new double[] { 13.399999999999999d, 0.7835820895522388d }, result.Item1, "initial"); + AssertArray(new double[] { 13.4d, 0.7835820895522387d }, result.Item1, "initial"); CollectionAssert.AreEqual(new double[] { 1.11022302462516e-16d, 1.11022302462516e-16d }, result.Item2, "lower"); CollectionAssert.AreEqual(new double[] { 1000d, 10d }, result.Item3, "upper"); } @@ -93,7 +95,7 @@ public void GammaDistribution_skewed_PreservesBaselineConstraints() public void GammaDistribution_nearUnity_PreservesBaselineConstraints() { var result = new GammaDistribution().GetParameterConstraints(new double[] { 1.001d, 1.002d, 1.003d, 1.004d }); - AssertArray(new double[] { 1.662510390690019e-06d, 603003.749999972d }, result.Item1, "initial"); + AssertArray(new double[] { 1.6625103906900188e-06d, 603003.7499999721d }, result.Item1, "initial"); CollectionAssert.AreEqual(new double[] { 1.11022302462516e-16d, 1.11022302462516e-16d }, result.Item2, "lower"); CollectionAssert.AreEqual(new double[] { 0.0001d, 10000000d }, result.Item3, "upper"); } @@ -103,7 +105,7 @@ public void GammaDistribution_nearUnity_PreservesBaselineConstraints() public void GammaDistribution_subUnity_PreservesBaselineConstraints() { var result = new GammaDistribution().GetParameterConstraints(new double[] { 0.1d, 0.2d, 0.4d, 0.8d }); - AssertArray(new double[] { 0.2555555555555556d, 1.467391304347826d }, result.Item1, "initial"); + AssertArray(new double[] { 0.25555555555555554d, 1.467391304347826d }, result.Item1, "initial"); CollectionAssert.AreEqual(new double[] { 1.11022302462516e-16d, 1.11022302462516e-16d }, result.Item2, "lower"); CollectionAssert.AreEqual(new double[] { 10d, 100d }, result.Item3, "upper"); } @@ -544,8 +546,8 @@ public void LogNormal_ordinary_PreservesBaselineConstraints() { var result = new LogNormal().GetParameterConstraints(new double[] { 10d, 12d, 14d, 16d }); AssertArray(new double[] { 1.1073573160954466d, 0.08791220351278327d }, result.Item1, "initial"); - CollectionAssert.AreEqual(new double[] { -100d, 1.11022302462516e-16d }, result.Item2, "lower"); - CollectionAssert.AreEqual(new double[] { 100d, 2d }, result.Item3, "upper"); + CollectionAssert.AreEqual(new double[] { 0d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 3d, 2d }, result.Item3, "upper"); } /// Checks initialization and bounds against the literal baseline fixture. @@ -554,8 +556,8 @@ public void LogNormal_skewed_PreservesBaselineConstraints() { var result = new LogNormal().GetParameterConstraints(new double[] { 1d, 2d, 4d, 8d, 16d, 32d }); AssertArray(new double[] { 0.7525749891599528d, 0.5631755534583315d }, result.Item1, "initial"); - CollectionAssert.AreEqual(new double[] { -10d, 1.11022302462516e-16d }, result.Item2, "lower"); - CollectionAssert.AreEqual(new double[] { 10d, 2d }, result.Item3, "upper"); + CollectionAssert.AreEqual(new double[] { -1d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 2d, 2d }, result.Item3, "upper"); } /// Checks initialization and bounds against the literal baseline fixture. @@ -564,8 +566,8 @@ public void LogNormal_nearUnity_PreservesBaselineConstraints() { var result = new LogNormal().GetParameterConstraints(new double[] { 1.001d, 1.002d, 1.003d, 1.004d }); AssertArray(new double[] { 0.0010841112099910272d, 0.0005592740172164043d }, result.Item1, "initial"); - CollectionAssert.AreEqual(new double[] { -0.1d, 1.11022302462516e-16d }, result.Item2, "lower"); - CollectionAssert.AreEqual(new double[] { 0.1d, 2d }, result.Item3, "upper"); + CollectionAssert.AreEqual(new double[] { -1d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 2d, 2d }, result.Item3, "upper"); } /// Checks initialization and bounds against the literal baseline fixture. @@ -574,8 +576,8 @@ public void LogNormal_subUnity_PreservesBaselineConstraints() { var result = new LogNormal().GetParameterConstraints(new double[] { 0.1d, 0.2d, 0.4d, 0.8d }); AssertArray(new double[] { -0.5484550065040281d, 0.38862805330516337d }, result.Item1, "initial"); - CollectionAssert.AreEqual(new double[] { -10d, 1.11022302462516e-16d }, result.Item2, "lower"); - CollectionAssert.AreEqual(new double[] { 10d, 2d }, result.Item3, "upper"); + CollectionAssert.AreEqual(new double[] { -2d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1d, 2d }, result.Item3, "upper"); } /// Checks initialization and bounds against the literal baseline fixture. @@ -584,8 +586,8 @@ public void LogPearsonTypeIII_ordinary_PreservesBaselineConstraints() { var result = new LogPearsonTypeIII().GetParameterConstraints(new double[] { 10d, 12d, 14d, 16d }); AssertArray(new double[] { 1.1073573160954466d, 0.08791220351278327d, -0.2899042849970034d }, result.Item1, "initial"); - CollectionAssert.AreEqual(new double[] { -100d, 1.11022302462516e-16d, -6d }, result.Item2, "lower"); - CollectionAssert.AreEqual(new double[] { 100d, 2d, 6d }, result.Item3, "upper"); + CollectionAssert.AreEqual(new double[] { 0d, 1.11022302462516e-16d, -6d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 3d, 2d, 6d }, result.Item3, "upper"); } /// Checks initialization and bounds against the literal baseline fixture. @@ -594,8 +596,8 @@ public void LogPearsonTypeIII_skewed_PreservesBaselineConstraints() { var result = new LogPearsonTypeIII().GetParameterConstraints(new double[] { 1d, 2d, 4d, 8d, 16d, 32d }); AssertArray(new double[] { 0.7525749891599528d, 0.5631755534583315d, -1.1187971499007316e-15d }, result.Item1, "initial"); - CollectionAssert.AreEqual(new double[] { -10d, 1.11022302462516e-16d, -6d }, result.Item2, "lower"); - CollectionAssert.AreEqual(new double[] { 10d, 2d, 6d }, result.Item3, "upper"); + CollectionAssert.AreEqual(new double[] { -1d, 1.11022302462516e-16d, -6d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 2d, 2d, 6d }, result.Item3, "upper"); } /// Checks initialization and bounds against the literal baseline fixture. @@ -604,8 +606,8 @@ public void LogPearsonTypeIII_nearUnity_PreservesBaselineConstraints() { var result = new LogPearsonTypeIII().GetParameterConstraints(new double[] { 1.001d, 1.002d, 1.003d, 1.004d }); AssertArray(new double[] { 0.0010841112099910272d, 0.0005592740172164043d, -0.0018543970617275146d }, result.Item1, "initial"); - CollectionAssert.AreEqual(new double[] { -0.1d, 1.11022302462516e-16d, -6d }, result.Item2, "lower"); - CollectionAssert.AreEqual(new double[] { 0.1d, 2d, 6d }, result.Item3, "upper"); + CollectionAssert.AreEqual(new double[] { -1d, 1.11022302462516e-16d, -6d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 2d, 2d, 6d }, result.Item3, "upper"); } /// Checks initialization and bounds against the literal baseline fixture. @@ -614,8 +616,8 @@ public void LogPearsonTypeIII_subUnity_PreservesBaselineConstraints() { var result = new LogPearsonTypeIII().GetParameterConstraints(new double[] { 0.1d, 0.2d, 0.4d, 0.8d }); AssertArray(new double[] { -0.5484550065040281d, 0.38862805330516337d, -1.2610041890269048e-15d }, result.Item1, "initial"); - CollectionAssert.AreEqual(new double[] { -10d, 1.11022302462516e-16d, -6d }, result.Item2, "lower"); - CollectionAssert.AreEqual(new double[] { 10d, 2d, 6d }, result.Item3, "upper"); + CollectionAssert.AreEqual(new double[] { -2d, 1.11022302462516e-16d, -6d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 1d, 2d, 6d }, result.Item3, "upper"); } /// Checks initialization and bounds against the literal baseline fixture. @@ -769,8 +771,8 @@ public void LogNormal_nearUnity_BaseTwo_PreservesBaselineConstraints() { var result = new LogNormal { Base = 2d }.GetParameterConstraints(new double[] { 1.001d, 1.002d, 1.003d, 1.004d }); AssertArray(new double[] { 0.003601339486451527d, 0.0018578680705316926d }, result.Item1, "initial"); - CollectionAssert.AreEqual(new double[] { -0.1d, 1.11022302462516e-16d }, result.Item2, "lower"); - CollectionAssert.AreEqual(new double[] { 0.1d, 7d }, result.Item3, "upper"); + CollectionAssert.AreEqual(new double[] { -4d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 7d, 7d }, result.Item3, "upper"); } /// Preserves physical-scale decade rounding before conversion to the configured log base. @@ -779,8 +781,8 @@ public void LogNormal_subUnity_BaseTwo_PreservesBaselineConstraints() { var result = new LogNormal { Base = 2d }.GetParameterConstraints(new double[] { 0.1d, 0.2d, 0.4d, 0.8d }); AssertArray(new double[] { -1.8219280948873622d, 1.2909944487358056d }, result.Item1, "initial"); - CollectionAssert.AreEqual(new double[] { -100d, 1.11022302462516e-16d }, result.Item2, "lower"); - CollectionAssert.AreEqual(new double[] { 100d, 7d }, result.Item3, "upper"); + CollectionAssert.AreEqual(new double[] { -7d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 4d, 7d }, result.Item3, "upper"); } /// Preserves physical-scale decade rounding before conversion to the configured log base. @@ -789,8 +791,8 @@ public void LogNormal_nearUnity_NaturalBase_PreservesBaselineConstraints() { var result = new LogNormal { Base = 2.718281828459045d }.GetParameterConstraints(new double[] { 1.001d, 1.002d, 1.003d, 1.004d }); AssertArray(new double[] { 0.002496258311273077d, 0.0012877760149413882d }, result.Item1, "initial"); - CollectionAssert.AreEqual(new double[] { -0.1d, 1.11022302462516e-16d }, result.Item2, "lower"); - CollectionAssert.AreEqual(new double[] { 0.1d, 5d }, result.Item3, "upper"); + CollectionAssert.AreEqual(new double[] { -3d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 5d, 5d }, result.Item3, "upper"); } /// Preserves physical-scale decade rounding before conversion to the configured log base. @@ -799,8 +801,8 @@ public void LogNormal_subUnity_NaturalBase_PreservesBaselineConstraints() { var result = new LogNormal { Base = 2.718281828459045d }.GetParameterConstraints(new double[] { 0.1d, 0.2d, 0.4d, 0.8d }); AssertArray(new double[] { -1.2628643221541278d, 0.8948491622597645d }, result.Item1, "initial"); - CollectionAssert.AreEqual(new double[] { -100d, 1.11022302462516e-16d }, result.Item2, "lower"); - CollectionAssert.AreEqual(new double[] { 100d, 5d }, result.Item3, "upper"); + CollectionAssert.AreEqual(new double[] { -5d, 1.11022302462516e-16d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 3d, 5d }, result.Item3, "upper"); } /// Preserves physical-scale decade rounding before conversion to the configured log base. @@ -809,8 +811,8 @@ public void LogPearsonTypeIII_nearUnity_BaseTwo_PreservesBaselineConstraints() { var result = new LogPearsonTypeIII { Base = 2d }.GetParameterConstraints(new double[] { 1.001d, 1.002d, 1.003d, 1.004d }); AssertArray(new double[] { 0.003601339486451527d, 0.0018578680705316926d, -0.0018543970617339045d }, result.Item1, "initial"); - CollectionAssert.AreEqual(new double[] { -0.1d, 1.11022302462516e-16d, -6d }, result.Item2, "lower"); - CollectionAssert.AreEqual(new double[] { 0.1d, 7d, 6d }, result.Item3, "upper"); + CollectionAssert.AreEqual(new double[] { -4d, 1.11022302462516e-16d, -6d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 7d, 7d, 6d }, result.Item3, "upper"); } /// Preserves physical-scale decade rounding before conversion to the configured log base. @@ -819,8 +821,8 @@ public void LogPearsonTypeIII_subUnity_BaseTwo_PreservesBaselineConstraints() { var result = new LogPearsonTypeIII { Base = 2d }.GetParameterConstraints(new double[] { 0.1d, 0.2d, 0.4d, 0.8d }); AssertArray(new double[] { -1.8219280948873622d, 1.2909944487358056d, 0d }, result.Item1, "initial"); - CollectionAssert.AreEqual(new double[] { -100d, 1.11022302462516e-16d, -6d }, result.Item2, "lower"); - CollectionAssert.AreEqual(new double[] { 100d, 7d, 6d }, result.Item3, "upper"); + CollectionAssert.AreEqual(new double[] { -7d, 1.11022302462516e-16d, -6d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 4d, 7d, 6d }, result.Item3, "upper"); } /// Preserves physical-scale decade rounding before conversion to the configured log base. @@ -829,8 +831,8 @@ public void LogPearsonTypeIII_nearUnity_NaturalBase_PreservesBaselineConstraints { var result = new LogPearsonTypeIII { Base = 2.718281828459045d }.GetParameterConstraints(new double[] { 1.001d, 1.002d, 1.003d, 1.004d }); AssertArray(new double[] { 0.002496258311273077d, 0.0012877760149413882d, -0.001854397061727415d }, result.Item1, "initial"); - CollectionAssert.AreEqual(new double[] { -0.1d, 1.11022302462516e-16d, -6d }, result.Item2, "lower"); - CollectionAssert.AreEqual(new double[] { 0.1d, 5d, 6d }, result.Item3, "upper"); + CollectionAssert.AreEqual(new double[] { -3d, 1.11022302462516e-16d, -6d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 5d, 5d, 6d }, result.Item3, "upper"); } /// Preserves physical-scale decade rounding before conversion to the configured log base. @@ -839,8 +841,8 @@ public void LogPearsonTypeIII_subUnity_NaturalBase_PreservesBaselineConstraints( { var result = new LogPearsonTypeIII { Base = 2.718281828459045d }.GetParameterConstraints(new double[] { 0.1d, 0.2d, 0.4d, 0.8d }); AssertArray(new double[] { -1.2628643221541278d, 0.8948491622597645d, 0d }, result.Item1, "initial"); - CollectionAssert.AreEqual(new double[] { -100d, 1.11022302462516e-16d, -6d }, result.Item2, "lower"); - CollectionAssert.AreEqual(new double[] { 100d, 5d, 6d }, result.Item3, "upper"); + CollectionAssert.AreEqual(new double[] { -5d, 1.11022302462516e-16d, -6d }, result.Item2, "lower"); + CollectionAssert.AreEqual(new double[] { 3d, 5d, 6d }, result.Item3, "upper"); } /// Retains hardened zero-center bounds when this family's old location envelope was invalid. diff --git a/Test_Numerics/Distributions/Univariate/Test_NonpositiveConstraintRegressions.cs b/Test_Numerics/Distributions/Univariate/Test_NonpositiveConstraintRegressions.cs index 5a8c391d..dc79bbc3 100644 --- a/Test_Numerics/Distributions/Univariate/Test_NonpositiveConstraintRegressions.cs +++ b/Test_Numerics/Distributions/Univariate/Test_NonpositiveConstraintRegressions.cs @@ -2,11 +2,12 @@ namespace Distributions { - /// Preserves usable constraint envelopes for nonpositive observations from the pre-hardening baseline. + /// Pins approved constraint envelopes and initialization for nonpositive observations. [TestClass] public class Test_NonpositiveConstraintRegressions { - /// Checks literal d80bfa8 initialization and rounded bounds without changing estimation data. + // Original d80bfa8 fixtures, with Gamma initialization and log-location bounds repinned to 202095a. + /// Checks approved literal initialization and rounded bounds without changing estimation data. [TestMethod] [DataRow("GammaDistribution", 0d)] [DataRow("GammaDistribution", -1d)] @@ -31,8 +32,8 @@ public void ConstraintsPreserveUsableLegacyNonpositiveSamples(string family, dou switch (family) { case "GammaDistribution": - initial = first == 0d ? new[] { 84.33333333333334, 0.3290513833992095 } - : new[] { 85.78181818181818, 0.3205807545570157 }; + initial = first == 0d ? new[] { 84.33333333333333, 0.3290513833992095 } + : new[] { 85.78181818181818, 0.32058075455701573 }; lower = new[] { epsilon, epsilon }; upper = new[] { 1000d, 10d }; break; @@ -44,13 +45,13 @@ public void ConstraintsPreserveUsableLegacyNonpositiveSamples(string family, dou break; case "LogNormal": initial = new[] { 0.5000000000000002, 1.2909944487358054 }; - lower = new[] { -10d, epsilon }; - upper = new[] { 10d, 3d }; + lower = new[] { -1d, epsilon }; + upper = new[] { 2d, 3d }; break; case "LogPearsonTypeIII": initial = new[] { 0.25, 1.707825127659933, -0.7528371991317255 }; - lower = new[] { -10d, epsilon, -6d }; - upper = new[] { 10d, 3d, 6d }; + lower = new[] { -1d, epsilon, -6d }; + upper = new[] { 2d, 3d, 6d }; break; default: initial = new[] { 12.336441557126482, 0.493577181580963 }; From 156204fa2e6257616cca197175b95c363939f100 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Thu, 17 Sep 2026 12:13:43 -0600 Subject: [PATCH 217/222] Repair BFGS convergence and safeguarded bounded line search --- .../Mathematics/Optimization/Local/BFGS.cs | 591 +++++++++--------- .../Optimization/Local/Test_BFGS.cs | 33 +- .../Optimization/Local/Test_BFGSRegression.cs | 200 ++++++ 3 files changed, 503 insertions(+), 321 deletions(-) create mode 100644 Test_Numerics/Mathematics/Optimization/Local/Test_BFGSRegression.cs diff --git a/Numerics/Mathematics/Optimization/Local/BFGS.cs b/Numerics/Mathematics/Optimization/Local/BFGS.cs index e8cc2f5a..6a883e91 100644 --- a/Numerics/Mathematics/Optimization/Local/BFGS.cs +++ b/Numerics/Mathematics/Optimization/Local/BFGS.cs @@ -1,4 +1,4 @@ -using Numerics.Mathematics.LinearAlgebra; +using Numerics.Mathematics.LinearAlgebra; using System; using System.Collections.Generic; using System.Linq; @@ -102,358 +102,359 @@ public BFGS(Func objectiveFunction, int numberOfParameters, /// protected override void Optimize() { - int D = NumberOfParameters; - double EPS = Tools.DoubleMachineEpsilon; - // TOLX is Numerical Recipes' dfpmin outer parameter-change tolerance: after an accepted - // line-search step, the loop below exits when the largest relative parameter step falls - // below it. A line search that cannot satisfy the strong Wolfe conditions reports - // LineSearchFailed separately. (The TOLX local in LineSearchArmijo is dfpmin's unrelated - // inner step-size floor, and that routine is not called by Optimize.) - double TOLX = 4 * EPS, STPMX = 100.0; - bool cancel = false, check = false; - - var p = InitialValues.ToArray(); - var pnew = new double[D]; - - - // Calculate the starting function value and gradient, and initialize the inverse Hessian to the unit matrix. - double fp = Evaluate(p, ref cancel); - var g = Gradient != null ? Gradient(p) : NumericalDerivative.Gradient(x => Evaluate(x, ref cancel), p, LowerBounds, UpperBounds); - var dg = new double[D]; - var hdg = new double[D]; - var xi = new double[D]; - var hessin = new Matrix(D, D); - - double sum = 0.0; - for (int i = 0; i < D; i++) + int n = NumberOfParameters; + bool cancel = false; + var x = (double[])InitialValues.Clone(); + double f = EvaluateObjective(x, ref cancel); + if (cancel) return; + if (!Tools.IsFinite(f)) + throw new ArgumentException("The initial objective value must be finite.", nameof(ObjectiveFunction)); + var g = EvaluateGradient(x, ref cancel); + if (cancel) return; + + var inverseHessian = Matrix.Identity(n); + var projected = new double[n]; + var direction = new double[n]; + double stpmax = 100 * Math.Max(Math.Sqrt(Tools.SumProduct(x, x)), n); + + while (true) { - for (int j = 0; j < D; j++) hessin[i, j] = 0.0; - hessin[i, i] = 1.0; - xi[i] = -g[i]; - sum += p[i] * p[i]; - } - - double fret = 0.0; - double stpmax = STPMX * Math.Max(Math.Sqrt(sum), D); - - while (Iterations < MaxIterations) - { - // Perform line search - LineSearch(p, fp, g, xi, pnew, ref fret, stpmax, ref check, ref cancel); - if (cancel) return; - if (check) - { - UpdateStatus(OptimizationStatus.LineSearchFailed); - return; - } - - // Check convergence. - if (CheckConvergence(fp, fret)) + if (ProjectedGradient(x, g, projected) <= AbsoluteTolerance) { + // Objective probes (including finite differences) can have a slightly lower rounded + // value. Return the accepted point whose convergence was actually established. + BestParameterSet = new ParameterSet((double[])x.Clone(), f); UpdateStatus(OptimizationStatus.Success); return; } - - // The new function evaluation occurs in line search; save the function value in fp for the next line search. - fp = fret; - for (int i = 0; i < D; i++) + if (Iterations >= MaxIterations) { - xi[i] = pnew[i] - p[i]; - p[i] = pnew[i]; + UpdateStatus(OptimizationStatus.MaximumIterationsReached); + return; } - // Numerical Recipes dfpmin: exit when the largest relative parameter step falls below - // TOLX. Without this test a stalled line search leaves xi and dg at zero, the inverse - // Hessian update is skipped, the search direction never changes, and the loop repeats - // the identical iteration until the evaluation budget is exhausted. - double test = 0.0; - for (int i = 0; i < D; i++) + for (int i = 0; i < n; i++) { - double temp = Math.Abs(xi[i]) / Math.Max(Math.Abs(p[i]), 1.0); - if (temp > test) test = temp; + direction[i] = 0; + for (int j = 0; j < n; j++) direction[i] -= inverseHessian[i, j] * projected[j]; } - if (test < TOLX) + MakeFeasible(x, direction); + double slope = Tools.SumProduct(g, direction); + if (!Tools.IsFinite(slope) || slope >= 0) { - UpdateStatus(OptimizationStatus.Success); - return; + inverseHessian = Matrix.Identity(n); + for (int i = 0; i < n; i++) direction[i] = -projected[i]; } - // Save the old gradient, and get the new gradient. - for (int i = 0; i < D; i++) - dg[i] = g[i]; - g = Gradient != null ? Gradient(p) : NumericalDerivative.Gradient((x) => Evaluate(x, ref cancel), p, LowerBounds, UpperBounds); - if (cancel) return; - - // Compute difference of gradients. - for (int i = 0; i < D; i++) - dg[i] = g[i] - dg[i]; - - // And difference times current matrix. - for (int i = 0; i < D; i++) - { - hdg[i] = 0.0; - for (int j = 0; j < D; j++) - hdg[i] += hessin[i, j] * dg[j]; - } - - // Calculate dot products for the denominators. - double fac = 0.0, fae = 0.0, sumdg = 0.0, sumxi = 0.0; - for (int i = 0; i < D; i++) + bool canRestart = false; + for (int i = 0; i < n; i++) canRestart |= direction[i] != -projected[i]; + bool accepted = LineSearch(x, f, g, direction, stpmax, out var nextX, out double nextF, out var nextG, ref cancel); + if (!accepted && !cancel && canRestart) { - fac += dg[i] * xi[i]; - fae += dg[i] * hdg[i]; - sumdg += Tools.Sqr(dg[i]); - sumxi += Tools.Sqr(xi[i]); + // A stale metric can exhaust the search even with a negative slope. As in + // classical BFGS implementations (for example R's vmmin), restart the metric + // once at this point. Both searches must satisfy the same Wolfe conditions. + inverseHessian = Matrix.Identity(n); + for (int i = 0; i < n; i++) direction[i] = -projected[i]; + accepted = LineSearch(x, f, g, direction, stpmax, out nextX, out nextF, out nextG, ref cancel); } - - // Skip update if fac not sufficiently positive. - if (fac > Math.Sqrt(EPS * sumdg * sumxi)) + if (cancel) return; + if (!accepted) { - fac = 1.0 / fac; - double fad = 1.0 / fae; - for (int i = 0; i < D; i++) - dg[i] = fac * xi[i] - fad * hdg[i]; - for (int i = 0; i < D; i++) - { - for (int j = i; j < D; j++) - { - hessin[i, j] += fac * xi[i] * xi[j] - fad * hdg[i] * hdg[j] + fae * dg[i] * dg[j]; - hessin[j, i] = hessin[i, j]; - } - } + UpdateStatus(OptimizationStatus.LineSearchFailed); + return; } + Iterations++; - for (int i = 0; i < D; i++) + var step = new double[n]; + var change = new double[n]; + for (int i = 0; i < n; i++) { - xi[i] = 0.0; - for (int j = 0; j < D; j++) - xi[i] -= hessin[i, j] * g[j]; + step[i] = nextX[i] - x[i]; + change[i] = nextG[i] - g[i]; } - - Iterations += 1; + UpdateInverseHessian(ref inverseHessian, step, change); + x = nextX; + f = nextF; + g = nextG; } + } - // If we made it to here, the maximum iterations were reached. - UpdateStatus(OptimizationStatus.MaximumIterationsReached); - + /// Evaluates an objective trial while retaining only finite incumbents. + /// The trial point. + /// The evaluation-budget cancellation flag. + /// The scaled objective value, including a non-finite rejection value. + private double EvaluateObjective(double[] x, ref bool cancel) + { + var incumbent = BestParameterSet; + double f = Evaluate(x, ref cancel); + if (!Tools.IsFinite(f)) BestParameterSet = incumbent; + return f; } - /// - /// Auxiliary function for searching a line. - /// - /// n-dimensional point [0..n-1]. - /// Value of the function at xold. - /// Gradient of function at xold. - /// A direction to search. - /// A new point x[0..n-1] - /// The new function value. - /// Limits the length of steps. - /// Check is false on a normal exit, true when x is too close to xold. - /// Determines if the solver should be canceled. - private void LineSearchArmijo(double[] xold, double fold, double[] g, ref double[] p, ref double[] x, ref double f, double stpmax, ref bool check, ref bool cancel) + /// Evaluates and validates a gradient in minimization coordinates. + /// The point at which to differentiate. + /// The evaluation-budget cancellation flag. + /// A private copy of the scaled gradient. + /// The gradient has an invalid dimension or non-finite component. + private double[] EvaluateGradient(double[] x, ref bool cancel) { - double ALF = 1.0e-4, TOLX = Tools.DoubleMachineEpsilon; - double a, alam, alam2 = 0.0, alamin, b, disc, f2 = 0.0; - double rhs1, rhs2, slope = 0.0, sum = 0.0, temp, test, tmplam; - int i, n = xold.Length; - check = false; - for (i = 0; i < n; i++) sum += p[i] * p[i]; - sum = Math.Sqrt(sum); - if (sum > stpmax) - for (i = 0; i < n; i++) - p[i] *= stpmax / sum; - for (i = 0; i < n; i++) - slope += g[i] * p[i]; - if (slope == 0.0) return; // If the slope is zero, it is on a flat spit. Exit the routine - if (slope > 0.0) throw new Exception("Roundoff problem in line search."); - test = 0.0; - for (i = 0; i < n; i++) + double[] g; + if (Gradient != null) { - temp = Math.Abs(p[i]) / Math.Max(Math.Abs(xold[i]), 1.0); - if (temp > test) test = temp; + var supplied = Gradient(x); + if (supplied == null || supplied.Length != NumberOfParameters) + throw new ArgumentException("The gradient must contain one value per parameter.", nameof(Gradient)); + g = (double[])supplied.Clone(); + for (int i = 0; i < g.Length; i++) g[i] *= functionScale; } - alamin = TOLX / test; - alam = 1.0; - for (; ; ) + else { - for (i = 0; i < n; i++) - { - x[i] = xold[i] + alam * p[i]; - // Make sure the parameters are within the bounds. - x[i] = RepairParameter(x[i], LowerBounds[i], UpperBounds[i]); - } - f = Evaluate(x, ref cancel); - if (cancel) return; - if (alam < alamin) - { - for (i = 0; i < n; i++) x[i] = xold[i]; - check = true; - return; - } - else if (f <= fold + ALF * alam * slope) return; - else - { - if (alam == 1.0) - tmplam = -slope / (2.0 * (f - fold - slope)); - else - { - rhs1 = f - fold - alam * slope; - rhs2 = f2 - fold - alam2 * slope; - a = (rhs1 / (alam * alam) - rhs2 / (alam2 * alam2)) / (alam - alam2); - b = (-alam2 * rhs1 / (alam * alam) + alam * rhs2 / (alam2 * alam2)) / (alam - alam2); - if (a == 0.0) tmplam = -slope / (2.0 * b); - else - { - disc = b * b - 3.0 * a * slope; - if (disc < 0.0) tmplam = 0.5 * alam; - else if (b <= 0.0) tmplam = (-b + Math.Sqrt(disc)) / (3.0 * a); - else tmplam = -slope / (b + Math.Sqrt(disc)); - } - if (tmplam > 0.5 * alam) - tmplam = 0.5 * alam; - } - } - alam2 = alam; - f2 = f; - alam = Math.Max(tmplam, 0.1 * alam); + bool stopped = cancel; + // Finite differences may request more probes after cancellation. Do not spend beyond + // the budget, and leave its status intact instead of misclassifying the partial gradient. + g = NumericalDerivative.Gradient(p => stopped ? double.NaN : EvaluateObjective(p, ref stopped), + x, LowerBounds, UpperBounds); + cancel = stopped; + if (cancel) return g; } - + for (int i = 0; i < g.Length; i++) + if (!Tools.IsFinite(g[i])) + throw new ArgumentException("The gradient must contain only finite values.", nameof(Gradient)); + return g; } - /// - /// Performs a strong Wolfe line search to find a step size that satisfies both the sufficient decrease (Armijo) and curvature conditions. - /// - /// The current parameter vector. - /// The objective function value at . - /// The gradient at . - /// The search direction. - /// The output parameter vector at the accepted step size. - /// The objective function value at . - /// The maximum allowable step length. - /// Returns true if the search failed to find an acceptable step; otherwise, false. - /// Set to true if cancellation is requested or a cancel condition occurs during evaluation. - - private void LineSearch(double[] x0, double f0, double[] g0, double[] p, double[] x, ref double f, double stpmax, ref bool check, ref bool cancel) + /// Projects the gradient onto feasible descent coordinates and returns its infinity norm. + /// The feasible point. + /// Its objective gradient. + /// The projected gradient buffer. + /// The largest absolute projected component. + private double ProjectedGradient(double[] x, double[] g, double[] projected) { - const double c1 = 1e-4, c2 = 0.9; - check = false; - double alpha = 1.0, alphaPrev = 0.0; - double fPrev = f0; - double slope0 = Tools.SumProduct(g0, p); - double[] g = new double[p.Length]; - double[] xTemp = new double[p.Length]; - - double normP = Math.Sqrt(p.Sum(pi => pi * pi)); - if (normP > stpmax) + double norm = 0; + for (int i = 0; i < x.Length; i++) { - double scale = stpmax / normP; - for (int i = 0; i < p.Length; i++) - p[i] *= scale; + projected[i] = (x[i] <= LowerBounds[i] && g[i] > 0) || + (x[i] >= UpperBounds[i] && g[i] < 0) || LowerBounds[i] == UpperBounds[i] ? 0 : g[i]; + norm = Math.Max(norm, Math.Abs(projected[i])); } + return norm; + } - for (int iter = 0; iter < 20; iter++) - { - for (int i = 0; i < x0.Length; i++) - { - xTemp[i] = x0[i] + alpha * p[i]; - xTemp[i] = RepairParameter(xTemp[i], LowerBounds[i], UpperBounds[i]); - } - - f = Evaluate(xTemp, ref cancel); - if (cancel) return; + /// Removes direction components that would immediately leave the feasible box. + /// The current point. + /// The search direction, modified in place. + private void MakeFeasible(double[] x, double[] direction) + { + for (int i = 0; i < x.Length; i++) + if ((x[i] <= LowerBounds[i] && direction[i] < 0) || (x[i] >= UpperBounds[i] && direction[i] > 0)) + direction[i] = 0; + } - if (f > f0 + c1 * alpha * slope0 || (iter > 0 && f >= fPrev)) + /// Applies the inverse BFGS update only when its curvature denominators are reliable. + /// The inverse Hessian, reset if arithmetic becomes non-finite. + /// The accepted parameter step. + /// The change in gradients. + /// Uses the existing Numerical Recipes symmetric BFGS formula with positive-curvature guards. + private static void UpdateInverseHessian(ref Matrix h, double[] s, double[] y) + { + int n = s.Length; + var hy = new double[n]; + for (int i = 0; i < n; i++) + for (int j = 0; j < n; j++) hy[i] += h[i, j] * y[j]; + double ys = Tools.SumProduct(y, s), yhy = Tools.SumProduct(y, hy); + double floor = Math.Sqrt(Tools.DoubleMachineEpsilon) * Math.Sqrt(Tools.SumProduct(y, y)) * Math.Sqrt(Tools.SumProduct(s, s)); + if (!Tools.IsFinite(ys) || !Tools.IsFinite(yhy) || yhy <= 0 || ys <= floor) return; + var v = new double[n]; + for (int i = 0; i < n; i++) v[i] = s[i] / ys - hy[i] / yhy; + for (int i = 0; i < n; i++) + for (int j = i; j < n; j++) { - Zoom(x0, f0, slope0, p, alphaPrev, alpha, ref f, x, ref check, ref cancel); - return; + double value = h[i, j] + s[i] * s[j] / ys - hy[i] * hy[j] / yhy + yhy * v[i] * v[j]; + if (!Tools.IsFinite(value)) + { + h = Matrix.Identity(n); + return; + } + h[i, j] = h[j, i] = value; } + } - bool cancelFlag = cancel; - g = Gradient != null ? Gradient(xTemp) : NumericalDerivative.Gradient(x => Evaluate(x, ref cancelFlag), xTemp, LowerBounds, UpperBounds); - cancel = cancelFlag; - if (cancel) return; - - double slope = Tools.SumProduct(g, p); + /// Searches a feasible ray using strong Wolfe conditions, or sufficient decrease at its bound. + /// The current point. + /// Its scaled objective. + /// Its scaled gradient. + /// The feasible direction, scaled in place. + /// The maximum direction length. + /// The accepted point, or the starting point on failure. + /// The objective at the returned point. + /// The gradient at the returned point. + /// The evaluation-budget cancellation flag. + /// Whether a step was accepted. + /// A bound can truncate a descending ray before Wolfe curvature is attainable; subsequent + /// convergence still requires the projected gradient tolerance. Interior searches use c1=1e-4 and c2=0.9. + private bool LineSearch(double[] x0, double f0, double[] g0, double[] p, double stpmax, + out double[] x, out double f, out double[] g, ref bool cancel) + { + x = x0; f = f0; g = g0; + double norm = Math.Sqrt(Tools.SumProduct(p, p)); + if (norm > stpmax) + for (int i = 0; i < p.Length; i++) p[i] *= stpmax / norm; + double slope0 = Tools.SumProduct(g0, p); + if (!Tools.IsFinite(slope0) || slope0 >= 0) return false; - if (Math.Abs(slope) <= -c2 * slope0) + double limit = double.PositiveInfinity; + for (int i = 0; i < p.Length; i++) + { + if (p[i] > 0) limit = Math.Min(limit, (UpperBounds[i] - x0[i]) / p[i]); + else if (p[i] < 0) limit = Math.Min(limit, (LowerBounds[i] - x0[i]) / p[i]); + } + double alpha = Math.Min(1, limit), previous = 0, fPrevious = f0, slopePrevious = slope0; + for (int iteration = 0; iteration < 20; iteration++) + { + var trial = TrialPoint(x0, p, alpha); + if (alpha <= 0 || trial.SequenceEqual(x0)) return false; + double value = EvaluateObjective(trial, ref cancel); + if (cancel) return false; + if (!Tools.IsFinite(value) || value > f0 + 1e-4 * alpha * slope0) + return Zoom(x0, f0, g0, p, slope0, previous, fPrevious, slopePrevious, + alpha, value, double.NaN, out x, out f, out g, ref cancel); + + var gradient = EvaluateGradient(trial, ref cancel); + if (cancel) return false; + double slope = Tools.SumProduct(gradient, p); + if (Math.Abs(slope) <= -0.9 * slope0 || (alpha == limit && slope < 0)) { - Array.Copy(xTemp, x, x.Length); - return; + x = trial; f = value; g = gradient; + return true; } - + if (!Tools.IsFinite(slope)) return false; + // Equal rounded values can still satisfy both Wolfe conditions. Check their + // gradients before reducing the bracket, without relaxing either condition. + if (iteration > 0 && value >= fPrevious) + return Zoom(x0, f0, g0, p, slope0, previous, fPrevious, slopePrevious, + alpha, value, slope, out x, out f, out g, ref cancel); if (slope >= 0) - { - Zoom(x0, f0, slope0, p, alpha, alphaPrev, ref f, x, ref check, ref cancel); - return; - } - - alphaPrev = alpha; - fPrev = f; - alpha *= 2.0; + return Zoom(x0, f0, g0, p, slope0, alpha, value, slope, + previous, fPrevious, slopePrevious, out x, out f, out g, ref cancel); + previous = alpha; + fPrevious = value; + slopePrevious = slope; + alpha = Math.Min(2 * alpha, limit); + if (alpha == previous) return false; } - - Array.Copy(x0, x, x.Length); - check = true; + return false; } - /// - /// Zoom phase of the strong Wolfe line search that performs bisection between two step sizes to find an acceptable step satisfying Wolfe conditions. - /// - /// The initial parameter vector. - /// The objective function value at . - /// The directional derivative (slope) at along the search direction. - /// The search direction vector. - /// The lower bound of the step size interval. - /// The upper bound of the step size interval. - /// The objective function value at the final accepted point. - /// The parameter vector at the final accepted step size. - /// Returns true if the zoom search exhausts its attempts without finding an acceptable step. - /// Set to true if cancellation is requested or a cancel condition occurs during evaluation. - - private void Zoom(double[] x0, double f0, double slope0, double[] p, double alphaLow, double alphaHigh, ref double f, double[] x, ref bool check, ref bool cancel) + /// Constructs a point on a feasible ray, correcting only boundary roundoff. + /// The ray origin. + /// The feasible direction. + /// A step no greater than the feasible limit. + /// The trial coordinates. + private double[] TrialPoint(double[] x0, double[] p, double alpha) { - const double c1 = 1e-4, c2 = 0.9; - double[] g = new double[p.Length]; - double[] xTemp = new double[p.Length]; + var x = new double[x0.Length]; + for (int i = 0; i < x.Length; i++) + x[i] = RepairParameter(x0[i] + alpha * p[i], LowerBounds[i], UpperBounds[i]); + return x; + } - for (int iter = 0; iter < 20; iter++) + /// Refines a Wolfe bracket while retaining both endpoint values and available slopes. + /// The initial point. + /// Its objective. + /// Its gradient. + /// The feasible search direction. + /// The initial directional derivative. + /// The endpoint with sufficient decrease. + /// Its function value. + /// Its directional derivative. + /// The other bracket endpoint, which may precede low. + /// Its function value. + /// Its derivative, or NaN if not evaluated. + /// The accepted point, or initial point on failure. + /// The returned point's objective. + /// The returned point's gradient. + /// The evaluation-budget cancellation flag. + /// Whether a Wolfe step was found. + /// Uses the bracket logic of Nocedal and Wright, Numerical Optimization, algorithm 3.6; + /// compare SciPy 1.16.2 optimize/_linesearch.py. Interpolation is safeguarded away from both endpoints. + private bool Zoom(double[] x0, double f0, double[] g0, double[] p, double slope0, + double low, double fLow, double slopeLow, double high, double fHigh, double slopeHigh, + out double[] x, out double f, out double[] g, ref bool cancel) + { + x = x0; f = f0; g = g0; + double[]? previousTrial = null; + for (int iteration = 0; iteration < 20; iteration++) { - double alpha = 0.5 * (alphaLow + alphaHigh); - for (int i = 0; i < x0.Length; i++) + double alpha = Interpolate(low, fLow, slopeLow, high, fHigh, slopeHigh); + if (alpha == low || alpha == high) return false; + var trial = TrialPoint(x0, p, alpha); + if (trial.SequenceEqual(x0) || (previousTrial != null && trial.SequenceEqual(previousTrial))) return false; + previousTrial = trial; + double value = EvaluateObjective(trial, ref cancel); + if (cancel) return false; + if (!Tools.IsFinite(value) || value > f0 + 1e-4 * alpha * slope0) { - xTemp[i] = x0[i] + alpha * p[i]; - xTemp[i] = RepairParameter(xTemp[i], LowerBounds[i], UpperBounds[i]); + high = alpha; fHigh = value; slopeHigh = double.NaN; } - - f = Evaluate(xTemp, ref cancel); - if (cancel) return; - - if (f > f0 + c1 * alpha * slope0) - alphaHigh = alpha; else { - bool cancelFlag = cancel; - g = Gradient != null ? Gradient(xTemp) : NumericalDerivative.Gradient(x => Evaluate(x, ref cancelFlag), xTemp, LowerBounds, UpperBounds); - cancel = cancelFlag; - if (cancel) return; - - double slope = Tools.SumProduct(g, p); - - if (Math.Abs(slope) <= -c2 * slope0) + var gradient = EvaluateGradient(trial, ref cancel); + if (cancel) return false; + double slope = Tools.SumProduct(gradient, p); + if (Math.Abs(slope) <= -0.9 * slope0) { - Array.Copy(xTemp, x, x.Length); - return; + x = trial; f = value; g = gradient; + return true; } - - if (slope * (alphaHigh - alphaLow) >= 0) - alphaHigh = alphaLow; - - alphaLow = alpha; + if (!Tools.IsFinite(slope)) return false; + if (value >= fLow) + { + high = alpha; fHigh = value; slopeHigh = slope; + continue; + } + if (slope * (high - low) >= 0) + { + high = low; fHigh = fLow; slopeHigh = slopeLow; + } + low = alpha; fLow = value; slopeLow = slope; } } + return false; + } - Array.Copy(x0, x, x.Length); - check = true; + /// Chooses a safeguarded cubic or quadratic interpolant, falling back to bisection. + /// The first bracket endpoint. + /// Its function value. + /// Its slope. + /// The second endpoint. + /// Its function value. + /// Its slope, or NaN when unavailable. + /// An interior trial step. + private static double Interpolate(double a, double fa, double ga, double b, double fb, double gb) + { + double width = b - a; + double left = Math.Min(a, b) + 0.1 * Math.Abs(width); + double right = Math.Max(a, b) - 0.1 * Math.Abs(width); + double candidate = double.NaN; + if (Tools.IsFinite(gb) && Tools.IsFinite(fb)) + { + double d1 = ga + gb - 3 * (fb - fa) / width; + double radicand = d1 * d1 - ga * gb; + if (radicand >= 0) + { + double d2 = Math.Sign(width) * Math.Sqrt(radicand); + candidate = b - width * (gb + d2 - d1) / (gb - ga + 2 * d2); + } + } + if (!Tools.IsFinite(candidate) || candidate <= left || candidate >= right) + candidate = a - ga * width * width / (2 * (fb - fa - ga * width)); + if (!Tools.IsFinite(candidate) || candidate <= Math.Min(a, b) || candidate >= Math.Max(a, b)) + return a + 0.5 * width; + // Preserve useful interpolation on very steep objectives while guaranteeing a + // contraction of at least ten percent. Repeated bisection can exhaust the bracket + // budget before reaching a perfectly representable, very short Wolfe step. + return Math.Max(left, Math.Min(right, candidate)); } } diff --git a/Test_Numerics/Mathematics/Optimization/Local/Test_BFGS.cs b/Test_Numerics/Mathematics/Optimization/Local/Test_BFGS.cs index 36576594..b46aaeee 100644 --- a/Test_Numerics/Mathematics/Optimization/Local/Test_BFGS.cs +++ b/Test_Numerics/Mathematics/Optimization/Local/Test_BFGS.cs @@ -1,4 +1,4 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Mathematics.Optimization; namespace Mathematics.Optimization @@ -226,37 +226,18 @@ public void Test_GradientDoesNotProbeOutsideBounds() } /// - /// An exhausted strong-Wolfe search reports a distinct failure instead of successful convergence. + /// A descending linear objective attains its constrained optimum at the upper bound. /// - /// - /// The linear objective is minimized at the upper bound. Projection keeps every zoom trial at that - /// bound, where the unprojected slope cannot satisfy the Wolfe curvature condition. The optimizer - /// must retain the best evaluated bound point without treating the returned start coordinates as a - /// converged parameter step. The requested Hessian remains available for compatibility with callers - /// whose objective wrappers observe those evaluations. - /// [TestMethod] - public void Test_WolfeSearchExhaustionReportsLineSearchFailed() + public void Test_LinearBoundaryOptimumReportsSuccess() { - var solver = new BFGS( - x => -x[0], - 1, - new[] { 0d }, - new[] { 0d }, - new[] { 1d }, - _ => new[] { -1d }) - { - ReportFailure = false, - RecordTraces = false, - ComputeHessian = true - }; - + var solver = new BFGS(x => -x[0], 1, new[] { 0d }, new[] { 0d }, new[] { 1d }, + _ => new[] { -1d }); solver.Minimize(); - - Assert.AreEqual(OptimizationStatus.LineSearchFailed, solver.Status); + Assert.AreEqual(OptimizationStatus.Success, solver.Status); Assert.AreEqual(1d, solver.BestParameterSet.Values[0]); + Assert.AreEqual(1, solver.Iterations); Assert.IsNotNull(solver.Hessian); } - } } diff --git a/Test_Numerics/Mathematics/Optimization/Local/Test_BFGSRegression.cs b/Test_Numerics/Mathematics/Optimization/Local/Test_BFGSRegression.cs new file mode 100644 index 00000000..73a57bf1 --- /dev/null +++ b/Test_Numerics/Mathematics/Optimization/Local/Test_BFGSRegression.cs @@ -0,0 +1,200 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Numerics.Mathematics.Optimization; + +namespace Mathematics.Optimization; + +/// Analytical regression tests for BFGS termination, bounds and evaluation contracts. +[TestClass] +public class Test_BFGSRegression +{ + /// A small objective change must not hide a nonzero gradient, including a warm start. + /// Initial first coordinate. + /// Initial second coordinate. + /// SciPy 1.16.2 BFGS with gtol=1e-8 independently returns the analytical minimum (0,0). + [TestMethod] + [DataRow(1e-9, 1e-4)] + [DataRow(1e-6, 1e-3)] + public void ScaledQuadratic_RequiresStationarity(double x, double y) + { + var solver = new BFGS(p => 0.001 + 0.5 * (1e6 * p[0] * p[0] + p[1] * p[1]), 2, + new[] { x, y }, new[] { -10d, -10d }, new[] { 10d, 10d }, + p => new[] { 1e6 * p[0], p[1] }) { ComputeHessian = false }; + solver.Minimize(); + Assert.AreEqual(OptimizationStatus.Success, solver.Status); + Assert.IsLessThanOrEqualTo(solver.AbsoluteTolerance, Math.Abs(1e6 * solver.BestParameterSet.Values[0])); + Assert.IsLessThanOrEqualTo(solver.AbsoluteTolerance, Math.Abs(solver.BestParameterSet.Values[1])); + Assert.IsGreaterThan(0, solver.Iterations); + } + + /// A stationary initial point succeeds without an accepted step or redundant gradient. + [TestMethod] + public void StationaryStart_DoesNotSearch() + { + int gradients = 0; + var solver = new BFGS(p => p[0] * p[0], 1, new[] { 0d }, new[] { -10d }, new[] { 10d }, + p => { gradients++; return new[] { 2 * p[0] }; }) { ComputeHessian = false }; + solver.Minimize(); + Assert.AreEqual(OptimizationStatus.Success, solver.Status); + Assert.AreEqual(0, solver.Iterations); + Assert.AreEqual(1, solver.FunctionEvaluations); + Assert.AreEqual(1, gradients); + } + + /// Maximization applies the objective sign to a supplied gradient without mutating it. + [TestMethod] + public void Maximize_UsesSuppliedGradientSign() + { + var buffer = new double[1]; + var solver = new BFGS(p => -(p[0] - 2) * (p[0] - 2), 1, + new[] { 0d }, new[] { -10d }, new[] { 10d }, + p => { buffer[0] = -2 * (p[0] - 2); return buffer; }) { ComputeHessian = false }; + solver.Maximize(); + Assert.AreEqual(OptimizationStatus.Success, solver.Status); + Assert.AreEqual(2d, solver.BestParameterSet.Values[0], 1e-8); + } + + /// The standard Rosenbrock minimum must satisfy the requested gradient tolerance. + /// SciPy 1.16.2 BFGS at gtol=1e-8 reaches (1,1) from (-1.2,1). + [TestMethod] + public void Rosenbrock_ReportsStationarySolution() + { + var solver = RosenbrockSolver(); + solver.Minimize(); + Assert.AreEqual(OptimizationStatus.Success, solver.Status); + var p = solver.BestParameterSet.Values; + Assert.AreEqual(1d, p[0], 1e-8); + Assert.AreEqual(1d, p[1], 1e-8); + foreach (double g in solver.Gradient!(p)) Assert.IsLessThanOrEqualTo(solver.AbsoluteTolerance, Math.Abs(g)); + } + + /// A blocked coordinate must leave the other coordinate free to reach its constrained optimum. + [TestMethod] + public void BoundaryOptimum_UsesProjectedGradient() + { + var solver = new BFGS(p => Math.Pow(p[0] - 2, 2) + Math.Pow(p[1] - 0.3, 2), 2, + new[] { 0d, 0d }, new[] { 0d, 0d }, new[] { 1d, 1d }, + p => new[] { 2 * (p[0] - 2), 2 * (p[1] - 0.3) }); + solver.Minimize(); + Assert.AreEqual(OptimizationStatus.Success, solver.Status); + Assert.AreEqual(1d, solver.BestParameterSet.Values[0]); + Assert.AreEqual(0.3, solver.BestParameterSet.Values[1], 1e-8); + Assert.IsNotNull(solver.Hessian); + } + + /// Strong-Wolfe slope calculations must use the direction after step scaling. + [TestMethod] + public void LargeDirection_IsScaledConsistently() + { + var solver = new BFGS(p => 1e8 * Math.Pow(p[0] - 3, 2), 1, new[] { 0d }, + new[] { -1000d }, new[] { 1000d }, p => new[] { 2e8 * (p[0] - 3) }) { ComputeHessian = false }; + solver.Minimize(); + Assert.AreEqual(OptimizationStatus.Success, solver.Status); + Assert.AreEqual(3d, solver.BestParameterSet.Values[0], 1e-12); + } + + /// A wrong derivative cannot satisfy strong Wolfe for a constant objective. + [TestMethod] + public void InconsistentGradient_ReportsLineSearchFailure() + { + var solver = new BFGS(_ => 1d, 1, new[] { 0d }, new[] { -10d }, new[] { 10d }, + _ => new[] { 1d }) { ReportFailure = false }; + solver.Minimize(); + Assert.AreEqual(OptimizationStatus.LineSearchFailed, solver.Status); + Assert.AreEqual(0, solver.Iterations); + Assert.IsNotNull(solver.Hessian); + } + + /// Non-finite gradients cannot be reported as converged. + /// The invalid gradient component. + [TestMethod] + [DataRow(double.NaN)] + [DataRow(double.PositiveInfinity)] + public void InvalidGradient_ReportsFailure(double value) + { + var solver = new BFGS(p => p[0] * p[0], 1, new[] { 1d }, new[] { -10d }, new[] { 10d }, + _ => new[] { value }) { ReportFailure = false, ComputeHessian = false }; + solver.Minimize(); + Assert.AreEqual(OptimizationStatus.Failure, solver.Status); + } + + /// An invalid initial objective must not produce a successful result. + [TestMethod] + public void InvalidInitialObjective_ReportsFailure() + { + var solver = new BFGS(_ => double.NaN, 1, new[] { 1d }, new[] { -10d }, new[] { 10d }, + _ => new[] { 0d }) { ReportFailure = false, ComputeHessian = false }; + solver.Minimize(); + Assert.AreEqual(OptimizationStatus.Failure, solver.Status); + } + + /// An invalid trial brackets the valid part of the search rather than poisoning the iterate. + [TestMethod] + public void InvalidTrialObjective_Backtracks() + { + var solver = new BFGS(p => p[0] > 0 ? double.NaN : Math.Pow(p[0] + 0.1, 2), 1, + new[] { -1d }, new[] { -10d }, new[] { 10d }, p => new[] { 2 * (p[0] + 0.1) }) + { ComputeHessian = false }; + solver.Minimize(); + Assert.AreEqual(OptimizationStatus.Success, solver.Status); + Assert.AreEqual(-0.1, solver.BestParameterSet.Values[0], 1e-8); + } + + /// Evaluation exhaustion remains distinct from line-search failure with either derivative source. + /// Whether to use an analytical gradient. + [TestMethod] + [DataRow(true)] + [DataRow(false)] + public void EvaluationBudget_IsPreserved(bool analytic) + { + var solver = new BFGS(p => -p[0], 1, new[] { 0d }, new[] { double.NegativeInfinity }, + new[] { double.PositiveInfinity }, analytic ? _ => new[] { -1d } : null) + { MaxFunctionEvaluations = 10, ReportFailure = false, ComputeHessian = false }; + solver.Minimize(); + Assert.AreEqual(OptimizationStatus.MaximumFunctionEvaluationsReached, solver.Status); + Assert.AreEqual(10, solver.FunctionEvaluations); + } + + /// Accepted iterations, including the last allowed step, are counted exactly. + [TestMethod] + public void IterationBudget_IsPreserved() + { + var solver = RosenbrockSolver(); + solver.MaxIterations = 10; + solver.ReportFailure = false; + solver.Minimize(); + Assert.AreEqual(OptimizationStatus.MaximumIterationsReached, solver.Status); + Assert.AreEqual(10, solver.Iterations); + } + + /// Rounded-equal function values must not prevent acceptance of a point satisfying both Wolfe conditions. + [TestMethod] + public void RoundedObjective_StillChecksWolfeGradient() + { + var solver = new BFGS(p => 1e12 + Math.Pow(p[0] - 1, 2), 1, + new[] { 1.0001 }, new[] { -10d }, new[] { 10d }, p => new[] { 2 * (p[0] - 1) }) + { ComputeHessian = false }; + solver.Minimize(); + Assert.AreEqual(OptimizationStatus.Success, solver.Status); + Assert.AreEqual(1d, solver.BestParameterSet.Values[0], 1e-12); + Assert.AreEqual(1, solver.Iterations); + } + + /// Safeguarded interpolation must resolve a very short step on a steep quadratic. + [TestMethod] + public void SteepQuadratic_ResolvesSmallWolfeStep() + { + var solver = new BFGS(p => 0.5e12 * p[0] * p[0], 1, new[] { 1e-8 }, + new[] { -10d }, new[] { 10d }, p => new[] { 1e12 * p[0] }) { ComputeHessian = false }; + solver.Minimize(); + Assert.AreEqual(OptimizationStatus.Success, solver.Status); + Assert.IsLessThanOrEqualTo(solver.AbsoluteTolerance, Math.Abs(1e12 * solver.BestParameterSet.Values[0])); + } + + /// Creates a Rosenbrock problem with its analytical derivative. + /// The configured optimizer. + private static BFGS RosenbrockSolver() => new BFGS( + p => Math.Pow(1 - p[0], 2) + 100 * Math.Pow(p[1] - p[0] * p[0], 2), 2, + new[] { -1.2, 1d }, new[] { -10d, -10d }, new[] { 10d, 10d }, + p => new[] { 2 * (p[0] - 1) - 400 * p[0] * (p[1] - p[0] * p[0]), 200 * (p[1] - p[0] * p[0]) }) + { ComputeHessian = false }; +} From 189a5973559cade7725f33cef4ded8fce4e16ccc Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Thu, 17 Sep 2026 13:03:09 -0600 Subject: [PATCH 218/222] Avoid Cholesky probe exceptions and verify every ridge candidate --- .../Linear Algebra/CholeskyDecomposition.cs | 110 +++++++---- .../Linear Algebra/MatrixRegularization.cs | 75 +++++--- .../Test_CholeskyDecomposition.cs | 71 +++++++ .../Test_MatrixRegularization.cs | 174 ++++++++++++++++-- 4 files changed, 353 insertions(+), 77 deletions(-) diff --git a/Numerics/Mathematics/Linear Algebra/CholeskyDecomposition.cs b/Numerics/Mathematics/Linear Algebra/CholeskyDecomposition.cs index f73aa5b0..27cd1b0d 100644 --- a/Numerics/Mathematics/Linear Algebra/CholeskyDecomposition.cs +++ b/Numerics/Mathematics/Linear Algebra/CholeskyDecomposition.cs @@ -113,64 +113,94 @@ public CholeskyDecomposition(Matrix A, double relativeTolerance) { IsPositiveDefinite = false; - int i, j, k; - if (double.IsNaN(relativeTolerance) || double.IsInfinity(relativeTolerance) || relativeTolerance < 0d || relativeTolerance >= 1d) - { - throw new ArgumentOutOfRangeException(nameof(relativeTolerance), "The relative tolerance must be a finite value in the interval [0, 1)."); - } - RelativeTolerance = relativeTolerance; + ValidateRelativeTolerance(relativeTolerance); n = A.NumberOfRows; this.A = new Matrix(A.ToArray()); - L = new Matrix(A.ToArray()); // Lower triangular matrix - double sum; - if (A.NumberOfColumns != A.NumberOfRows) + RelativeTolerance = relativeTolerance; + if (!TryFactorize(this.A, relativeTolerance, out Matrix lower, out int failedRow, out double failedPivot)) { - throw new ArgumentOutOfRangeException(nameof(A), "The matrix A must be square."); + if (double.IsNaN(failedPivot) || failedPivot <= 0d) + throw new Exception("Cholesky Decomposition failed. The input matrix is not positive-definite."); + throw new Exception("Cholesky Decomposition failed. The input matrix is not positive-definite. The pivot at row " + + failedRow.ToString(CultureInfo.InvariantCulture) + " is " + + (failedPivot / this.A[failedRow, failedRow]).ToString("E6", CultureInfo.InvariantCulture) + + " times its diagonal entry, at or below the relative tolerance " + + relativeTolerance.ToString("E6", CultureInfo.InvariantCulture) + + ", so the matrix is numerically rank-deficient."); } + L = lower; + IsPositiveDefinite = true; + } - //Decomposing a matrix into Lower triangular - for (i = 0; i < n; i++) + /// + /// Factors a symmetric matrix using the same pivot decisions as the public constructor, + /// without throwing for an expected rejected pivot. + /// + /// The symmetric input matrix, which is not modified. + /// The finite pivot tolerance in [0, 1). + /// The lower triangular factor on success; an incomplete factor on failure. + /// The rejected pivot's row, or -1 on success. + /// The rejected pivot before its square root, or NaN on success. + /// True when every pivot passes the existing positive-definiteness checks. + /// + /// Thrown when the matrix is not square or the tolerance is outside its permitted range. + /// + /// + /// Preserves the constructor's upper-triangle input convention, reverse-order inner products, + /// and diagonal-relative threshold. Invalid arguments remain distinct from rejected pivots. + /// + internal static bool TryFactorize(Matrix matrix, double relativeTolerance, out Matrix lower, + out int failedRow, out double failedPivot) + { + ValidateRelativeTolerance(relativeTolerance); + if (matrix.NumberOfColumns != matrix.NumberOfRows) + throw new ArgumentOutOfRangeException("A", "The matrix A must be square."); + + int dimension = matrix.NumberOfRows; + lower = new Matrix(matrix.ToArray()); + failedRow = -1; + failedPivot = double.NaN; + for (int i = 0; i < dimension; i++) { - for (j = i; j < n; j++) + for (int j = i; j < dimension; j++) { - sum = L[i, j]; - - for (k = i - 1; k >= 0; k -= 1) - sum -= L[i, k] * L[j, k]; // Cholesky formula + double sum = lower[i, j]; + for (int k = i - 1; k >= 0; k--) + sum -= lower[i, k] * lower[j, k]; if (i == j) { - // Reject a pivot that is negligible relative to its own diagonal entry. The diagonal - // guard keeps the threshold at zero (the absolute test) whenever A[i,i] is not a - // positive finite number. - double diagonal = this.A[i, i]; + double diagonal = matrix[i, i]; double threshold = diagonal > 0d && !double.IsInfinity(diagonal) ? relativeTolerance * diagonal : 0d; - if (double.IsNaN(sum) || sum <= 0d) - throw new Exception("Cholesky Decomposition failed. The input matrix is not positive-definite."); - if (sum <= threshold) - throw new Exception("Cholesky Decomposition failed. The input matrix is not positive-definite. The pivot at row " - + i.ToString(CultureInfo.InvariantCulture) + " is " - + (sum / diagonal).ToString("E6", CultureInfo.InvariantCulture) - + " times its diagonal entry, at or below the relative tolerance " - + relativeTolerance.ToString("E6", CultureInfo.InvariantCulture) - + ", so the matrix is numerically rank-deficient."); - L[i, i] = Math.Sqrt(sum); + if (double.IsNaN(sum) || sum <= 0d || sum <= threshold) + { + failedRow = i; + failedPivot = sum; + return false; + } + lower[i, i] = Math.Sqrt(sum); } else { - L[j, i] = sum / L[i, i]; // Upper Triangular matrix + lower[j, i] = sum / lower[i, i]; } } } - - // Making sure 0 entries for upper triangular matrix - for (i = 0; i < n; i++) + for (int i = 0; i < dimension; i++) { - for (j = 0; j < i; j++) - L[j, i] = 0.0d; + for (int j = 0; j < i; j++) + lower[j, i] = 0.0d; } - // Failure of the decomposition indicates that the matrix A is not positive-definite. - // Success, means it is. - IsPositiveDefinite = true; + return true; + } + + /// Validates the pivot tolerance before accessing the input matrix. + /// The finite relative pivot tolerance in [0, 1). + /// Thrown when the tolerance is invalid. + /// Preserves the public constructor's original argument-validation order. + private static void ValidateRelativeTolerance(double relativeTolerance) + { + if (double.IsNaN(relativeTolerance) || double.IsInfinity(relativeTolerance) || relativeTolerance < 0d || relativeTolerance >= 1d) + throw new ArgumentOutOfRangeException(nameof(relativeTolerance), "The relative tolerance must be a finite value in the interval [0, 1)."); } /// diff --git a/Numerics/Mathematics/Linear Algebra/MatrixRegularization.cs b/Numerics/Mathematics/Linear Algebra/MatrixRegularization.cs index 8f790a00..2d410b7d 100644 --- a/Numerics/Mathematics/Linear Algebra/MatrixRegularization.cs +++ b/Numerics/Mathematics/Linear Algebra/MatrixRegularization.cs @@ -110,51 +110,78 @@ private static double MedianFromVector(Vector v) /// when the decomposition succeeds; otherwise, . private static bool CholeskyAccepts(Matrix matrix) { - try - { - _ = new CholeskyDecomposition(matrix); - return true; - } - catch (Exception) - { - return false; - } + return CholeskyDecomposition.TryFactorize(matrix, CholeskyDecomposition.DefaultRelativeTolerance(matrix.NumberOfRows), out _, out _, out _); } /// /// Makes the matrix symmetric and, when necessary, adds a ridge until Cholesky accepts it as /// positive definite. /// - /// The matrix to adjust. - /// A symmetric and positive definite matrix. + /// The finite square matrix to adjust. + /// A finite symmetric matrix accepted by the default Cholesky pivot test. + /// Thrown when M is null. + /// Thrown when M is not square or contains non-finite entries. + /// + /// Thrown when symmetrization or the ridge scale is not representable, or no finite candidate + /// on the ridge ladder passes the Cholesky test before a ridge or diagonal overflows. + /// /// /// The symmetric input is returned without a ridge when its Cholesky decomposition succeeds. - /// A failed decomposition enters the existing trace-scaled ridge escalation. + /// Otherwise the base ridge remains 1E-10 times the mean diagonal for a positive trace, or + /// 1E-10 for a non-positive trace. The original first eight candidates retain their arithmetic; + /// subsequent candidates multiply the previous ridge by ten. Every returned candidate is checked. + /// The maximum permitted ridge is the largest finite value on this decade ladder for which all + /// adjusted diagonal entries remain finite. Exhaustion throws rather than returning an unchecked + /// or smaller ridge. No pivot tolerance or eigenvalue floor is changed. /// public static Matrix MakeSymmetricPositiveDefinite(Matrix M) { - // Symmetrize + if (M == null) throw new ArgumentNullException(nameof(M)); + if (M.NumberOfRows != M.NumberOfColumns) + throw new ArgumentException("The matrix must be square.", nameof(M)); + for (int i = 0; i < M.NumberOfRows; i++) + { + for (int j = 0; j < M.NumberOfColumns; j++) + { + if (!Tools.IsFinite(M[i, j])) + throw new ArgumentException("The matrix must contain only finite entries.", nameof(M)); + } + } + var S = 0.5 * (M + M.Transpose()); + for (int i = 0; i < S.NumberOfRows; i++) + { + for (int j = 0; j < S.NumberOfColumns; j++) + { + if (!Tools.IsFinite(S[i, j])) + throw new InvalidOperationException("Matrix symmetrization produced a non-finite entry."); + } + } if (CholeskyAccepts(S)) return S; - // Tiny trace-scaled ridge double tr = 0.0; for (int i = 0; i < S.NumberOfRows; i++) tr += S[i, i]; double baseRidge = (tr > 0 ? 1e-10 * tr / S.NumberOfRows : 1e-10); + if (!Tools.IsFinite(tr) || !Tools.IsFinite(baseRidge) || baseRidge <= 0d) + throw new InvalidOperationException("The trace-scaled matrix ridge is not a positive finite number."); - // Try increasing ridge until Cholesky succeeds - for (int k = 0; k < 8; k++) + double ridge = baseRidge; + for (int k = 0; Tools.IsFinite(ridge); k++) { var T = S.Clone(); - double ridge = baseRidge * Math.Pow(10.0, k); - for (int i = 0; i < T.NumberOfRows; i++) T[i, i] += ridge; + for (int i = 0; i < T.NumberOfRows; i++) + { + T[i, i] += ridge; + if (!Tools.IsFinite(T[i, i])) + throw new InvalidOperationException("Matrix ridge escalation overflowed a diagonal before positive definiteness was achieved."); + } if (CholeskyAccepts(T)) return T; + + // Preserve established candidates exactly, then continue monotonically. Multiplying the + // ridge itself avoids overflowing an unscaled power of ten for very small trace scales. + ridge = k < 7 ? baseRidge * Math.Pow(10.0, k + 1) : ridge * 10.0; } - // Last resort: add a biggish ridge - var U = S.Clone(); - double big = (tr > 0 ? 1e-4 * tr / S.NumberOfRows : 1e-4); - for (int i = 0; i < U.NumberOfRows; i++) U[i, i] += big; - return U; - } + throw new InvalidOperationException("Matrix ridge escalation exhausted finite candidates before positive definiteness was achieved."); + } } } diff --git a/Test_Numerics/Mathematics/Linear Algebra/Test_CholeskyDecomposition.cs b/Test_Numerics/Mathematics/Linear Algebra/Test_CholeskyDecomposition.cs index b2f0d142..2a33fbee 100644 --- a/Test_Numerics/Mathematics/Linear Algebra/Test_CholeskyDecomposition.cs +++ b/Test_Numerics/Mathematics/Linear Algebra/Test_CholeskyDecomposition.cs @@ -539,6 +539,77 @@ public void Test_NonPositiveOrNonFiniteDiagonalFallsBackToTheAbsoluteTest() } } + /// + /// The internal non-throwing entry point retains known factors and the public failure diagnostics. + /// + [TestMethod] + public void Test_TryFactorize_KnownFactorAndRejectedPivot() + { + var matrix = new Matrix(new[,] { { 16d, 4d, 8d }, { 4d, 5d, -4d }, { 8d, -4d, 22d } }); + Assert.IsTrue(CholeskyDecomposition.TryFactorize(matrix, CholeskyDecomposition.DefaultRelativeTolerance(3), + out Matrix lower, out int row, out double pivot)); + var expected = new[,] { { 4d, 0d, 0d }, { 1d, 2d, 0d }, { 2d, -3d, 3d } }; + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + Assert.AreEqual(expected[i, j], lower[i, j], 0d); + } + Assert.AreEqual(-1, row); + Assert.IsTrue(double.IsNaN(pivot)); + Assert.AreEqual(8d, matrix[0, 2], 0d); + + matrix = new Matrix(new[,] { { 1d, 2d }, { 2d, 1d } }); + Assert.IsFalse(CholeskyDecomposition.TryFactorize(matrix, CholeskyDecomposition.DefaultRelativeTolerance(2), + out _, out row, out pivot)); + Assert.AreEqual(1, row); + Assert.AreEqual(-3d, pivot, 0d); + Assert.AreEqual("Cholesky Decomposition failed. The input matrix is not positive-definite.", + AssertThrowsAny(() => new CholeskyDecomposition(matrix)).Message); + } + + /// + /// Adjacent correlations straddling the existing default pivot threshold retain opposite decisions. + /// + [TestMethod] + public void Test_TryFactorize_ExistingPivotThresholdIsUnchanged() + { + double tolerance = CholeskyDecomposition.DefaultRelativeTolerance(2); + double rho = 0.99999999999999978d; + var below = new Matrix(new[,] { { 1d, rho }, { rho, 1d } }); + Assert.IsFalse(CholeskyDecomposition.TryFactorize(below, tolerance, out _, out int row, out double pivot)); + Assert.AreEqual(1, row); + Assert.AreEqual(4.440892098500626E-16d, pivot, 0d); + string message = "Cholesky Decomposition failed. The input matrix is not positive-definite. The pivot at row 1 is " + + "4.440892E-016 times its diagonal entry, at or below the relative tolerance 4.440892E-016, so the matrix is numerically rank-deficient."; + Assert.AreEqual(message, AssertThrowsAny(() => new CholeskyDecomposition(below)).Message); + Assert.IsTrue(CholeskyDecomposition.TryFactorize(below, 0d, out _, out _, out _)); + + rho = ThreeUlpCorrelation; + var above = new Matrix(new[,] { { 1d, rho }, { rho, 1d } }); + Assert.IsTrue(CholeskyDecomposition.TryFactorize(above, tolerance, out Matrix lower, out _, out _)); + Assert.AreEqual(Math.Sqrt(6.661338147750939E-16d), lower[1, 1], 0d); + var mixedScale = new Matrix(new[,] { { 1E12d, 1d }, { 1d, 1E-11d } }); + Assert.IsTrue(CholeskyDecomposition.TryFactorize(mixedScale, tolerance, out lower, out _, out _)); + Assert.AreEqual(1E6d, lower[0, 0], 0d); + Assert.AreEqual(1E-6d, lower[1, 0], 0d); + Assert.AreEqual(3E-6d, lower[1, 1], 1E-21d); + } + + /// + /// Invalid shapes and tolerances still throw rather than masquerading as rejected pivots. + /// + [TestMethod] + public void Test_TryFactorize_InvalidArgumentsStillThrow() + { + Assert.ThrowsExactly(() => CholeskyDecomposition.TryFactorize( + new Matrix(2, 3), 0d, out _, out _, out _)); + Assert.ThrowsExactly(() => CholeskyDecomposition.TryFactorize( + new Matrix(2), double.NaN, out _, out _, out _)); + Assert.ThrowsExactly(() => new CholeskyDecomposition(new Matrix(2, 3))); + var exception = Assert.ThrowsExactly(() => new CholeskyDecomposition(null, double.NaN)); + Assert.AreEqual("relativeTolerance", exception.ParamName); + } + } } diff --git a/Test_Numerics/Mathematics/Linear Algebra/Test_MatrixRegularization.cs b/Test_Numerics/Mathematics/Linear Algebra/Test_MatrixRegularization.cs index d2a24ad9..6b3cbdc0 100644 --- a/Test_Numerics/Mathematics/Linear Algebra/Test_MatrixRegularization.cs +++ b/Test_Numerics/Mathematics/Linear Algebra/Test_MatrixRegularization.cs @@ -19,7 +19,7 @@ namespace Mathematics.LinearAlgebra /// symmetrizes its input and first tests /// that un-ridged candidate. It returns the candidate unchanged when Cholesky accepts it. Only a rejected /// candidate enters the trace-scaled ridge loop, beginning at 1E-10 * trace / p and multiplying the - /// ridge by ten after each rejection, up to eight attempts. These tests pin both the no-ridge and fallback + /// ridge by ten after each rejection until a finite candidate is accepted. These tests pin both the no-ridge and fallback /// paths so that a conditioning-policy change cannot silently alter downstream fitted results. /// /// @@ -136,23 +136,171 @@ public void Test_MakeSymmetricPositiveDefinite_SymmetrizesFirst() } /// - /// Verifies the indefinite path, where the ridge must escalate, is reached identically. + /// Continues the established ridge ladder after the eighth rejected candidate, returning an SPD matrix. /// /// - /// The input has an eigenvalue of -1, which no ridge in the loop's range can lift, so the method - /// falls through to the last-resort ridge of 1E-4 * trace / p. Every rejection along the way - /// comes from a strictly negative pivot, which both the absolute and the relative test reject - /// identically, so this path cannot move. + /// The eigenvalues of [[1,2],[2,1]] are -1 and 3. A ridge of 1 leaves a zero eigenvalue; + /// the first accepted decade is 10, giving eigenvalues 9 and 13 and determinant 117. + /// The former unchecked 1E-4 ridge left the matrix indefinite. /// [TestMethod] - public void Test_MakeSymmetricPositiveDefinite_IndefiniteFallsThroughToTheLastResortRidge() + public void Test_MakeSymmetricPositiveDefinite_IndefiniteContinuesTheCheckedRidgeLadder() { - var M = new Matrix(new[,] { { 1d, 2d }, { 2d, 1d } }); - var regularized = MatrixRegularization.MakeSymmetricPositiveDefinite(M); - double expectedRidge = 1E-4d * 2d / 2d; - Assert.AreEqual(1d + expectedRidge, regularized[0, 0], 0d); - Assert.AreEqual(1d + expectedRidge, regularized[1, 1], 0d); - Assert.AreEqual(2d, regularized[0, 1], 0d); + var original = new Matrix(new[,] { { 1d, 2d }, { 2d, 1d } }); + var regularized = MatrixRegularization.MakeSymmetricPositiveDefinite(original); + AssertMatricesEqual(new Matrix(new[,] { { 11d, 2d }, { 2d, 11d } }), regularized, 0d); + Assert.AreEqual(117d, new CholeskyDecomposition(regularized).Determinant(), 1E-12d); + AssertMatricesEqual(new Matrix(new[,] { { 1d, 2d }, { 2d, 1d } }), original, 0d); + } + + /// + /// Each of the original eight ridge candidates retains its exact value and selection boundary. + /// + /// The zero-based index of the first ridge larger than the negative eigenvalue. + /// The diagonal fixture has known eigenvalues, so its required shift is analytical. + [TestMethod] + [DataRow(0)] + [DataRow(1)] + [DataRow(2)] + [DataRow(3)] + [DataRow(4)] + [DataRow(5)] + [DataRow(6)] + [DataRow(7)] + public void Test_MakeSymmetricPositiveDefinite_OriginalEightCandidatesAreUnchanged(int decade) + { + double negativeEigenvalue = -5E-11d * Math.Pow(10d, decade); + var matrix = new Matrix(new[,] { { negativeEigenvalue, 0d }, { 0d, 2d } }); + double expectedRidge = (1E-10d * (negativeEigenvalue + 2d) / 2d) * Math.Pow(10d, decade); + var actual = MatrixRegularization.MakeSymmetricPositiveDefinite(matrix); + AssertMatricesEqual(new Matrix(new[,] { { negativeEigenvalue + expectedRidge, 0d }, { 0d, 2d + expectedRidge } }), actual, 0d); + Assert.IsTrue(actual[0, 0] > 0d && actual[1, 1] > 0d); + } + + /// + /// The captured B17C indefinite moment matrix requires the ninth trace-scaled ridge. + /// + /// + /// Independent NumPy eigvalsh results give a smallest eigenvalue of -9.092331071132015E-6 + /// before repair and 4.71784861949334E-5 after adding the ninth ridge. A positive determinant + /// alone is insufficient, so every leading principal minor is also checked (Sylvester's criterion). + /// + [TestMethod] + public void Test_MakeSymmetricPositiveDefinite_CapturedB17CWeightHasPositivePrincipalMinors() + { + var original = new Matrix(new[,] + { + { 0.01634642985063478d, -0.0005398365277961114d, 0.0007743723735985425d }, + { -0.0005398365277961114d, 0.0004912013373976332d, -0.00011353771970719928d }, + { 0.0007743723735985425d, -0.00011353771970719928d, 4.361399178720463e-05d } + }); + double baseRidge = 1E-10d * (original[0, 0] + original[1, 1] + original[2, 2]) / 3d; + double expectedRidge = (baseRidge * 1E7d) * 10d; + var actual = MatrixRegularization.MakeSymmetricPositiveDefinite(original); + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + Assert.AreEqual(original[i, j] + (i == j ? expectedRidge : 0d), actual[i, j], 0d); + } + double leadingTwo = actual[0, 0] * actual[1, 1] - actual[0, 1] * actual[1, 0]; + double determinant = actual[0, 0] * (actual[1, 1] * actual[2, 2] - actual[1, 2] * actual[2, 1]) + - actual[0, 1] * (actual[1, 0] * actual[2, 2] - actual[1, 2] * actual[2, 0]) + + actual[0, 2] * (actual[1, 0] * actual[2, 1] - actual[1, 1] * actual[2, 0]); + Assert.IsTrue(actual[0, 0] > 0d && leadingTwo > 0d && determinant > 0d); + Assert.IsTrue(new CholeskyDecomposition(actual).IsPositiveDefinite); + } + + /// + /// Zero and negative traces retain the existing absolute base ridge and receive checked escalation. + /// + [TestMethod] + public void Test_MakeSymmetricPositiveDefinite_NonPositiveTraceUsesExistingBaseRidge() + { + AssertMatricesEqual(new Matrix(new[,] { { 1E-10d, 0d }, { 0d, 1E-10d } }), + MatrixRegularization.MakeSymmetricPositiveDefinite(new Matrix(2)), 0d); + var actual = MatrixRegularization.MakeSymmetricPositiveDefinite(new Matrix(new[,] { { -1d, 0d }, { 0d, -2d } })); + AssertMatricesEqual(new Matrix(new[,] { { 9d, 0d }, { 0d, 8d } }), actual, 0d); + } + + /// + /// Invalid matrix arguments are distinguished from expected rejected positive-definiteness probes. + /// + [TestMethod] + public void Test_MakeSymmetricPositiveDefinite_InvalidArgumentsAreRejected() + { + Assert.ThrowsExactly(() => MatrixRegularization.MakeSymmetricPositiveDefinite(null)); + Assert.ThrowsExactly(() => MatrixRegularization.MakeSymmetricPositiveDefinite(new Matrix(2, 3))); + foreach (double value in new[] { double.NaN, double.PositiveInfinity, double.NegativeInfinity }) + { + Assert.ThrowsExactly(() => MatrixRegularization.MakeSymmetricPositiveDefinite( + new Matrix(new[,] { { value, 0d }, { 0d, 1d } }))); + Assert.ThrowsExactly(() => MatrixRegularization.MakeSymmetricPositiveDefinite( + new Matrix(new[,] { { 1d, value }, { value, 1d } }))); + } + } + + /// + /// Unrepresentable symmetrization, trace scale, and exhausted finite ridge candidates fail explicitly. + /// + [TestMethod] + public void Test_MakeSymmetricPositiveDefinite_UnrepresentableRepairThrows() + { + // Symmetrization overflows before a finite candidate can be formed. + Assert.ThrowsExactly(() => MatrixRegularization.MakeSymmetricPositiveDefinite( + new Matrix(new[,] { { double.MaxValue, 0d }, { 0d, 1d } }))); + // The finite zero-trace input requires a shift exceeding 8E307. The next decade overflows the positive diagonal. + Assert.ThrowsExactly(() => MatrixRegularization.MakeSymmetricPositiveDefinite( + new Matrix(new[,] { { -8E307d, 0d }, { 0d, 8E307d } }))); + // All finite decade shifts remain indefinite; the next ridge itself overflows. + Assert.ThrowsExactly(() => MatrixRegularization.MakeSymmetricPositiveDefinite( + new Matrix(new[,] { { -8E307d, 8E307d }, { 8E307d, -8E307d } }))); + // A finite indefinite matrix can still overflow when its diagonal entries are summed. + var overflowingTrace = new Matrix(new[,] + { + { 8E307d, 0d, 0d, 0d }, { 0d, 8E307d, 0d, 0d }, + { 0d, 0d, 8E307d, 0d }, { 0d, 0d, 0d, -8E307d } + }); + Assert.ThrowsExactly(() => MatrixRegularization.MakeSymmetricPositiveDefinite(overflowingTrace)); + // A trace-scaled starting ridge underflows to zero; do not silently select a different scale. + Assert.ThrowsExactly(() => MatrixRegularization.MakeSymmetricPositiveDefinite( + new Matrix(new[,] { { 0d, 0d }, { 0d, 1E-320d } }))); + } + + /// + /// Expected rejected pivots must not construct or throw exceptions during ridge selection. + /// + /// + /// First-chance observation detects exceptions even when the regularizer catches them internally. + /// The indefinite fixture is the captured B17C realization 213 moment covariance (seed 12345). + /// + [TestMethod] + public void Test_MakeSymmetricPositiveDefinite_ExpectedRejectionsDoNotThrow() + { + int rejectedPivotExceptions = 0; + int threadId = System.Threading.Thread.CurrentThread.ManagedThreadId; + EventHandler handler = (sender, args) => + { + if (System.Threading.Thread.CurrentThread.ManagedThreadId == threadId && + args.Exception.Message.StartsWith("Cholesky Decomposition failed.", StringComparison.Ordinal)) + rejectedPivotExceptions++; + }; + AppDomain.CurrentDomain.FirstChanceException += handler; + try + { + MatrixRegularization.MakeSymmetricPositiveDefinite( + new Matrix(new[,] { { 2d, 0.5d, 2d }, { 0.5d, 1d, 0.5d }, { 2d, 0.5d, 2d } })); + MatrixRegularization.MakeSymmetricPositiveDefinite(new Matrix(new[,] + { + { 0.01634642985063478d, -0.0005398365277961114d, 0.0007743723735985425d }, + { -0.0005398365277961114d, 0.0004912013373976332d, -0.00011353771970719928d }, + { 0.0007743723735985425d, -0.00011353771970719928d, 4.361399178720463e-05d } + })); + } + finally + { + AppDomain.CurrentDomain.FirstChanceException -= handler; + } + Assert.AreEqual(0, rejectedPivotExceptions); } /// From c949397023556f8f70582128deb9b7596bb15532 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Thu, 17 Sep 2026 14:10:21 -0600 Subject: [PATCH 219/222] Handle objective rounding in BFGS convergence --- .../Mathematics/Optimization/Local/BFGS.cs | 49 +++++++++++- .../Optimization/Local/Test_BFGSRegression.cs | 78 +++++++++++++++++++ 2 files changed, 124 insertions(+), 3 deletions(-) diff --git a/Numerics/Mathematics/Optimization/Local/BFGS.cs b/Numerics/Mathematics/Optimization/Local/BFGS.cs index 6a883e91..255c05c7 100644 --- a/Numerics/Mathematics/Optimization/Local/BFGS.cs +++ b/Numerics/Mathematics/Optimization/Local/BFGS.cs @@ -153,7 +153,7 @@ protected override void Optimize() { // A stale metric can exhaust the search even with a negative slope. As in // classical BFGS implementations (for example R's vmmin), restart the metric - // once at this point. Both searches must satisfy the same Wolfe conditions. + // once at this point. Both searches use the same acceptance conditions. inverseHessian = Matrix.Identity(n); for (int i = 0; i < n; i++) direction[i] = -projected[i]; accepted = LineSearch(x, f, g, direction, stpmax, out nextX, out nextF, out nextG, ref cancel); @@ -292,7 +292,8 @@ private static void UpdateInverseHessian(ref Matrix h, double[] s, double[] y) /// The evaluation-budget cancellation flag. /// Whether a step was accepted. /// A bound can truncate a descending ray before Wolfe curvature is attainable; subsequent - /// convergence still requires the projected gradient tolerance. Interior searches use c1=1e-4 and c2=0.9. + /// convergence still requires the projected gradient tolerance. Interior searches use c1=1e-4 and c2=0.9. + /// A supplied gradient may independently certify termination when objective rounding obscures decrease. private bool LineSearch(double[] x0, double f0, double[] g0, double[] p, double stpmax, out double[] x, out double f, out double[] g, ref bool cancel) { @@ -317,8 +318,16 @@ private bool LineSearch(double[] x0, double f0, double[] g0, double[] p, double double value = EvaluateObjective(trial, ref cancel); if (cancel) return false; if (!Tools.IsFinite(value) || value > f0 + 1e-4 * alpha * slope0) + { + if (TryRoundoffConvergence(trial, value, f0, out var stationaryGradient, ref cancel)) + { + x = trial; f = value; g = stationaryGradient; + return true; + } + if (cancel) return false; return Zoom(x0, f0, g0, p, slope0, previous, fPrevious, slopePrevious, alpha, value, double.NaN, out x, out f, out g, ref cancel); + } var gradient = EvaluateGradient(trial, ref cancel); if (cancel) return false; @@ -346,6 +355,34 @@ private bool LineSearch(double[] x0, double f0, double[] g0, double[] p, double return false; } + /// Checks stationarity independently when rounding can obscure objective decrease. + /// The feasible trial point. + /// Its scaled objective value. + /// The objective at the start of the line search. + /// The validated trial gradient when stationarity is confirmed. + /// The evaluation-budget cancellation flag. + /// Whether the existing projected-gradient convergence condition is satisfied. + /// + /// A small objective difference alone never establishes convergence. Only a supplied gradient + /// can independently confirm a rounding-ambiguous trial; finite differences reuse the noisy + /// function values. The eight-machine-epsilon relative window applies only to this terminal + /// check, has no unit-scale floor, and does not change Wolfe conditions for continuing steps + /// or the requested gradient tolerance. Non-finite values and resolvable increases are rejected. + /// + private bool TryRoundoffConvergence(double[] trial, double value, double initialValue, + out double[] gradient, ref bool cancel) + { + gradient = null!; + double roundoff = 8d * Tools.DoubleMachineEpsilon * Math.Abs(initialValue); + if (Gradient == null || !Tools.IsFinite(value) || Math.Abs(value - initialValue) > roundoff) + return false; + var candidateGradient = EvaluateGradient(trial, ref cancel); + if (cancel || ProjectedGradient(trial, candidateGradient, new double[NumberOfParameters]) > AbsoluteTolerance) + return false; + gradient = candidateGradient; + return true; + } + /// Constructs a point on a feasible ray, correcting only boundary roundoff. /// The ray origin. /// The feasible direction. @@ -375,7 +412,7 @@ private double[] TrialPoint(double[] x0, double[] p, double alpha) /// The returned point's objective. /// The returned point's gradient. /// The evaluation-budget cancellation flag. - /// Whether a Wolfe step was found. + /// Whether a Wolfe step or an independently stationary roundoff-limited point was found. /// Uses the bracket logic of Nocedal and Wright, Numerical Optimization, algorithm 3.6; /// compare SciPy 1.16.2 optimize/_linesearch.py. Interpolation is safeguarded away from both endpoints. private bool Zoom(double[] x0, double f0, double[] g0, double[] p, double slope0, @@ -395,6 +432,12 @@ private bool Zoom(double[] x0, double f0, double[] g0, double[] p, double slope0 if (cancel) return false; if (!Tools.IsFinite(value) || value > f0 + 1e-4 * alpha * slope0) { + if (TryRoundoffConvergence(trial, value, f0, out var stationaryGradient, ref cancel)) + { + x = trial; f = value; g = stationaryGradient; + return true; + } + if (cancel) return false; high = alpha; fHigh = value; slopeHigh = double.NaN; } else diff --git a/Test_Numerics/Mathematics/Optimization/Local/Test_BFGSRegression.cs b/Test_Numerics/Mathematics/Optimization/Local/Test_BFGSRegression.cs index 73a57bf1..2d325763 100644 --- a/Test_Numerics/Mathematics/Optimization/Local/Test_BFGSRegression.cs +++ b/Test_Numerics/Mathematics/Optimization/Local/Test_BFGSRegression.cs @@ -190,6 +190,84 @@ public void SteepQuadratic_ResolvesSmallWolfeStep() Assert.IsLessThanOrEqualTo(solver.AbsoluteTolerance, Math.Abs(1e12 * solver.BestParameterSet.Values[0])); } + /// Roundoff in a sum of squares must not reject its independently stationary minimum. + /// An additive objective constant, including a negative minimum. + /// Whether to maximize the negative quadratic. + /// The exact objective is offset + 1 + x squared. Its unique minimum is x=0. + [TestMethod] + [DataRow(0d, false)] + [DataRow(-1.25d, false)] + [DataRow(0d, true)] + public void RoundedUpMinimum_WithSuppliedGradient_Converges(double offset, bool maximize) + { + double sign = maximize ? -1d : 1d; + Func objective = p => sign * (offset + 0.5 * + ((p[0] - 1) * (p[0] - 1) + (p[0] + 1) * (p[0] + 1))); + Assert.IsLessThan(sign * objective(new[] { 0d }), + sign * objective(new[] { 6.8000000000000005e-9 })); + var solver = new BFGS(objective, 1, new[] { 6.8000000000000005e-9 }, + new[] { -10d }, new[] { 10d }, p => new[] { sign * 2 * p[0] }) + { ComputeHessian = false, ReportFailure = false }; + if (maximize) solver.Maximize(); else solver.Minimize(); + Assert.AreEqual(OptimizationStatus.Success, solver.Status); + Assert.AreEqual(0d, solver.BestParameterSet.Values[0], 1e-12); + Assert.IsLessThanOrEqualTo(solver.AbsoluteTolerance, Math.Abs(2 * solver.BestParameterSet.Values[0])); + // Optimizer stores minimization-scaled fitness for both optimization directions. + Assert.AreEqual(sign * objective(solver.BestParameterSet.Values), solver.BestParameterSet.Fitness); + Assert.AreEqual(1, solver.Iterations); + } + + /// A rounding-sized increase must not establish success when the supplied gradient is nonzero. + [TestMethod] + public void RoundoffRise_WithoutStationarity_StillFails() + { + var solver = new BFGS(p => p[0] == 0 ? 1d : 1d + 4 * Numerics.Tools.DoubleMachineEpsilon, + 1, new[] { 0d }, new[] { -10d }, new[] { 10d }, _ => new[] { 1e-7 }) + { ComputeHessian = false, ReportFailure = false }; + solver.Minimize(); + Assert.AreEqual(OptimizationStatus.LineSearchFailed, solver.Status); + Assert.AreEqual(0, solver.Iterations); + } + + /// A stationary trial with a resolvable objective increase must still be rejected. + /// The initial unit step reaches the local maximum at zero; backtracking reaches x=5/6. + [TestMethod] + public void StationaryUphillTrial_OutsideRoundoff_Backtracks() + { + var solver = new BFGS(p => 1 + 2 * p[0] * p[0] * p[0] - 2.5 * p[0] * p[0], + 1, new[] { 1d }, new[] { -2d }, new[] { 2d }, p => new[] { 6 * p[0] * p[0] - 5 * p[0] }) + { ComputeHessian = false, ReportFailure = false }; + solver.Minimize(); + Assert.AreEqual(OptimizationStatus.Success, solver.Status); + Assert.AreEqual(5d / 6d, solver.BestParameterSet.Values[0], 1e-8); + Assert.IsLessThan(0.5, solver.BestParameterSet.Fitness); + } + + /// An invalid derivative at a rounding-ambiguous trial must remain an explicit failure. + [TestMethod] + public void RoundoffTrial_InvalidSuppliedGradient_ReportsFailure() + { + var solver = new BFGS(p => 0.5 * ((p[0] - 1) * (p[0] - 1) + (p[0] + 1) * (p[0] + 1)), + 1, new[] { 6.8000000000000005e-9 }, new[] { -10d }, new[] { 10d }, + p => new[] { Math.Abs(p[0]) < 1e-12 ? double.NaN : 2 * p[0] }) + { ComputeHessian = false, ReportFailure = false }; + solver.Minimize(); + Assert.AreEqual(OptimizationStatus.Failure, solver.Status); + } + + /// Extra gradient checks on ambiguous trials must not overwrite evaluation-budget termination. + [TestMethod] + public void RoundoffTrial_PreservesEvaluationBudget() + { + var solver = new BFGS(p => p[0] == 0 ? 1d : 1d + 4 * Numerics.Tools.DoubleMachineEpsilon, + 1, new[] { 0d }, new[] { -10d }, new[] { 10d }, _ => new[] { 1e-7 }) + { ComputeHessian = false, ReportFailure = false, MaxFunctionEvaluations = 10 }; + solver.Minimize(); + Assert.AreEqual(OptimizationStatus.MaximumFunctionEvaluationsReached, solver.Status); + Assert.AreEqual(10, solver.FunctionEvaluations); + Assert.AreEqual(0, solver.Iterations); + } + /// Creates a Rosenbrock problem with its analytical derivative. /// The configured optimizer. private static BFGS RosenbrockSolver() => new BFGS( From 0c3b49521f0377f52b7f526f6c2f6345a84fabe8 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 18 Sep 2026 09:09:49 -0600 Subject: [PATCH 220/222] Prepare v2.2.0 release --- CITATION.cff | 2 +- Numerics/Numerics.csproj | 2 +- codemeta.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index 1b707be1..26e72894 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -3,7 +3,7 @@ message: "If you use this software, please cite our article in the Journal of Op type: software title: "Numerics: A .NET Library for Numerical Computing, Statistical Analysis, and Risk Assessment" version: "2.2.0" -date-released: "2026-08-28" +date-released: "2026-09-18" license: 0BSD repository-code: "https://github.com/USACE-RMC/Numerics" url: "https://github.com/USACE-RMC/Numerics" diff --git a/Numerics/Numerics.csproj b/Numerics/Numerics.csproj index a02c18ec..ac3766db 100644 --- a/Numerics/Numerics.csproj +++ b/Numerics/Numerics.csproj @@ -29,7 +29,7 @@ 2.2.0 - Version 2.2.0 adds weighted statistics, given-data global sensitivity estimators, a two-dimensional adaptive Gauss-Kronrod integrator, seeded Sobol scrambling with scrambled driving points on the Vegas integrator, conditional copula functions with an Independence copula, serialization, and factories, composable univariate functions, empirical-distribution convolution, sided transform-aware paired-data extrapolation, singular-covariance multivariate normal support, expanded shortest-path routing with custom weights and detours, lazy exclusive-probability enumeration, distribution XML serialization for the empirical, kernel-density, and competing-risks families, and adaptive NUTS with per-chain diagnostics and gradient reuse in NUTS and HMC. Reported results can differ from 2.1.4: R-hat and effective sample size follow the rank-normalized split definitions, NUTS adapts its mass matrix by default, regression trees stop at pure nodes, LogNormal and Log-Pearson Type III accept a negative log-space mean, Gamma.Incomplete runs its continued fraction to convergence (results change across its upper branch, most at large shape), bounded optimizers difference inside the feasible region, and seeded streams and reductions are deterministic across machines. The release also corrects validation, tie-correction, optimization, machine learning, and time-series edge cases and expands regression coverage. + Version 2.2.0 adds weighted statistics and global sensitivity estimators; adaptive two-dimensional Gauss-Kronrod integration; scrambled Sobol/Vegas sampling; conditional copulas and an Independence copula; composable, serializable functions; empirical convolution; sided extrapolation; singular-covariance multivariate normal support; and expanded shortest-path routing. It improves distribution tails, censored and interval likelihoods, uncertainty calculations, mixture and competing-risk evaluation, adaptive NUTS and MCMC diagnostics, BFGS convergence, matrix regularization, and regression coverage. Upgrade considerations: R-hat/ESS definitions, default NUTS mass adaptation, corrected numerical methods and fitting constraints can change reported results. See the v2.2.0 GitHub release notes for details. 2.2.0.0 diff --git a/codemeta.json b/codemeta.json index a435eb60..1a8bad79 100644 --- a/codemeta.json +++ b/codemeta.json @@ -6,7 +6,7 @@ "description": "A free and open-source .NET library providing numerical methods, probability distributions, statistical analysis, and Bayesian inference tools for quantitative risk assessment in water resources engineering.", "version": "2.2.0", "dateCreated": "2023-09-28", - "dateModified": "2026-08-28", + "dateModified": "2026-09-18", "license": "https://spdx.org/licenses/0BSD", "codeRepository": "https://github.com/USACE-RMC/Numerics", "issueTracker": "https://github.com/USACE-RMC/Numerics/issues", From 2bd9abcb8725ae1ee05e9eeca3cad3baada40100 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 18 Sep 2026 10:00:00 -0600 Subject: [PATCH 221/222] Exclude live BOM tests from PR integration --- .github/workflows/Integration.yml | 32 ++++++++++++++++--- .../Time Series/Test_TimeSeriesDownload.cs | 23 +++++++------ 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/.github/workflows/Integration.yml b/.github/workflows/Integration.yml index 11e8dc11..6c47f6ce 100644 --- a/.github/workflows/Integration.yml +++ b/.github/workflows/Integration.yml @@ -6,7 +6,31 @@ on: jobs: CI: - uses: HydrologicEngineeringCenter/dotnet-workflows/.github/workflows/integration.yml@main - with: - dotnet-version: '10.0.x' - run-tests: true + # Preserve the check name used by the previous reusable workflow. + name: CI / CI + runs-on: windows-latest + timeout-minutes: 90 + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup .NET SDKs + uses: actions/setup-dotnet@v4 + with: + dotnet-version: | + 8.0.x + 9.0.x + 10.0.x + + - name: Build + run: dotnet build -c Release + + - name: Test + env: + VSTEST_CONNECTION_TIMEOUT: '600' + # BOM's live WDP backend intermittently returns HTTP 500 DatasourceError. + # Keep offline BOM validation and all other tests in the PR gate. + run: dotnet test -c Release --no-build --filter "TestCategory!=BOMIntegration" diff --git a/Test_Numerics/Data/Time Series/Test_TimeSeriesDownload.cs b/Test_Numerics/Data/Time Series/Test_TimeSeriesDownload.cs index c66b49e1..fdba41ef 100644 --- a/Test_Numerics/Data/Time Series/Test_TimeSeriesDownload.cs +++ b/Test_Numerics/Data/Time Series/Test_TimeSeriesDownload.cs @@ -1750,10 +1750,13 @@ await TimeSeriesDownload.FromGHCN(GHCN_1, #region BOM (Australia) Tests + // Live BOM checks remain available locally but are excluded from PR integration. + // Run them with: dotnet test -c Release --filter "TestCategory=BOMIntegration" + /// /// Validates a full-period-of-record download for the BOM Cotter River station (discharge). /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("BOMIntegration")] public async Task BOM_FullPor_CotterRiver_Discharge() { if (!await BomAvailable()) return; @@ -1764,7 +1767,7 @@ public async Task BOM_FullPor_CotterRiver_Discharge() /// /// Validates a full-period-of-record download for the BOM Goodradigbee River station (discharge). /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("BOMIntegration")] public async Task BOM_FullPor_Goodradigbee_Discharge() { if (!await BomAvailable()) return; @@ -1775,7 +1778,7 @@ public async Task BOM_FullPor_Goodradigbee_Discharge() /// /// Validates a full-period-of-record download for the BOM Murray River station (stage). /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("BOMIntegration")] public async Task BOM_FullPor_MurrayRiver_Stage() { if (!await BomAvailable()) return; @@ -1786,7 +1789,7 @@ public async Task BOM_FullPor_MurrayRiver_Stage() /// /// Tests discharge unit conversions (cms ↔ cfs) for BOM data. /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("BOMIntegration")] public async Task BOM_UnitConversion_Discharge_CmsCfs() { if (!await BomAvailable()) return; @@ -1812,7 +1815,7 @@ public async Task BOM_UnitConversion_Discharge_CmsCfs() /// /// Tests stage unit conversions (m ↔ ft) for BOM data. /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("BOMIntegration")] public async Task BOM_UnitConversion_Stage_MFt() { if (!await BomAvailable()) return; @@ -1859,7 +1862,7 @@ await TimeSeriesDownload.FromABOM(BOM_1, /// /// Tests BOM with a windowed date range to verify date filtering works correctly. /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("BOMIntegration")] public async Task BOM_WindowedDownload_Works() { if (!await BomAvailable()) return; @@ -1884,7 +1887,7 @@ public async Task BOM_WindowedDownload_Works() /// /// Validates instantaneous discharge download from BOM. /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("BOMIntegration")] public async Task BOM_InstantaneousDischarge_Works() { if (!await BomAvailable()) return; @@ -1898,7 +1901,7 @@ public async Task BOM_InstantaneousDischarge_Works() /// /// Validates instantaneous stage download from BOM. /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("BOMIntegration")] public async Task BOM_InstantaneousStage_Works() { if (!await BomAvailable()) return; @@ -1912,7 +1915,7 @@ public async Task BOM_InstantaneousStage_Works() /// /// Validates daily precipitation download from BOM. /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("BOMIntegration")] public async Task BOM_DailyPrecipitation_Works() { if (!await BomAvailable()) return; @@ -1926,7 +1929,7 @@ public async Task BOM_DailyPrecipitation_Works() /// /// Tests precipitation unit conversions (mm ↔ inches) for BOM data. /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("BOMIntegration")] public async Task BOM_UnitConversion_Precip_MmIn() { if (!await BomAvailable()) return; From f9835599747332402b61faaec4885fd3289cbe55 Mon Sep 17 00:00:00 2001 From: HadenSmith Date: Fri, 18 Sep 2026 11:50:56 -0600 Subject: [PATCH 222/222] Stabilize PR tests and isolate live service checks --- .github/workflows/Integration.yml | 6 +- .../Time Series/Test_TimeSeriesDownload.cs | 71 ++++++++++--------- .../Utilities/Test_SafeProgressReporter.cs | 71 +++++++++++++++++-- 3 files changed, 107 insertions(+), 41 deletions(-) diff --git a/.github/workflows/Integration.yml b/.github/workflows/Integration.yml index 6c47f6ce..1bdfba86 100644 --- a/.github/workflows/Integration.yml +++ b/.github/workflows/Integration.yml @@ -31,6 +31,6 @@ jobs: - name: Test env: VSTEST_CONNECTION_TIMEOUT: '600' - # BOM's live WDP backend intermittently returns HTTP 500 DatasourceError. - # Keep offline BOM validation and all other tests in the PR gate. - run: dotnet test -c Release --no-build --filter "TestCategory!=BOMIntegration" + # Live provider outages and full-record download timeouts must not block PRs. + # Keep deterministic download, parsing, validation, and numerical tests in the gate. + run: dotnet test -c Release --no-build --filter "TestCategory!=LiveServiceIntegration" diff --git a/Test_Numerics/Data/Time Series/Test_TimeSeriesDownload.cs b/Test_Numerics/Data/Time Series/Test_TimeSeriesDownload.cs index fdba41ef..8c3c7c2e 100644 --- a/Test_Numerics/Data/Time Series/Test_TimeSeriesDownload.cs +++ b/Test_Numerics/Data/Time Series/Test_TimeSeriesDownload.cs @@ -16,10 +16,17 @@ namespace Data.TimeSeriesAnalysis /// /// Provides integration and validation tests for the class, /// including downloads from the Canadian Hydrometric Monitoring Network (CHMN), - /// the United States Geological Survey (USGS), and the Global Historical Climatology Network (GHCN). + /// the United States Geological Survey (USGS), the Global Historical Climatology Network (GHCN), + /// and the Australian Bureau of Meteorology (BOM). /// /// /// + /// Live provider checks carry the LiveServiceIntegration category and are excluded from + /// PR integration. Run them explicitly with + /// dotnet test -c Release --filter "TestCategory=LiveServiceIntegration". + /// Offline request, parsing, and input-validation tests remain in the PR gate. + /// + /// /// Authors: /// /// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil @@ -887,7 +894,7 @@ public void RejectIPv6BindEndPoint_ReturnsNullForIPv4() /// /// Validates a full-period-of-record download for the CHMN Cold River station (flow). /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration")] public async Task CHMN_FullPor_ColdRiver_Flow() { if (!await ChmnAvailable()) return; @@ -898,7 +905,7 @@ public async Task CHMN_FullPor_ColdRiver_Flow() /// /// Validates a full-period-of-record download for the CHMN Lillooet River station (flow). /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration")] public async Task CHMN_FullPor_Lillooet_Flow() { if (!await ChmnAvailable()) return; @@ -909,7 +916,7 @@ public async Task CHMN_FullPor_Lillooet_Flow() /// /// Validates a full-period-of-record download for the CHMN Capilano River station (flow). /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration")] public async Task CHMN_FullPor_Capilano_Flow() { if (!await ChmnAvailable()) return; @@ -920,7 +927,7 @@ public async Task CHMN_FullPor_Capilano_Flow() /// /// Tests flow unit conversions (cms ↔ cfs) for CHMN data. /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration")] public async Task CHMN_UnitConversion_Flow_CmsCfs() { if (!await ChmnAvailable()) return; @@ -946,7 +953,7 @@ public async Task CHMN_UnitConversion_Flow_CmsCfs() /// /// Tests stage unit conversions (m ↔ ft) for CHMN data. /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration")] public async Task CHMN_UnitConversion_Stage_MFt() { if (!await ChmnAvailable()) return; @@ -993,7 +1000,7 @@ await TimeSeriesDownload.FromCHMN(CHMN_1, /// /// Tests CHMN instantaneous discharge download (real-time 5-minute data). /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration")] public async Task CHMN_InstantaneousDischarge_Works() { if (!await ChmnAvailable()) return; @@ -1008,7 +1015,7 @@ public async Task CHMN_InstantaneousDischarge_Works() /// /// Tests CHMN instantaneous stage download (real-time 5-minute data). /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration")] public async Task CHMN_InstantaneousStage_Works() { if (!await ChmnAvailable()) return; @@ -1023,7 +1030,7 @@ public async Task CHMN_InstantaneousStage_Works() /// /// Tests CHMN peak discharge download (annual instantaneous maximums). /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration")] public async Task CHMN_PeakDischarge_Works() { if (!await ChmnAvailable()) return; @@ -1038,7 +1045,7 @@ public async Task CHMN_PeakDischarge_Works() /// /// Tests CHMN peak stage download (annual instantaneous maximums). /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration")] public async Task CHMN_PeakStage_Works() { if (!await ChmnAvailable()) return; @@ -1371,7 +1378,7 @@ private static async Task VerifyUsgsInstantaneousDownloadIsIrregular( /// /// Tests full-period-of-record USGS daily discharge download. /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration")] public async Task USGS_FullPor_DailyDischarge() { if (!await UsgsAvailable()) return; @@ -1383,7 +1390,7 @@ public async Task USGS_FullPor_DailyDischarge() /// /// Tests USGS daily stage download for correctness and continuity. /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration")] public async Task USGS_FullPor_DailyStage() { if (!await UsgsAvailable()) return; @@ -1395,7 +1402,7 @@ public async Task USGS_FullPor_DailyStage() /// /// Tests USGS peak discharge data retrieval for non-daily data. /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration")] public async Task USGS_PeakDischarge_Works() { if (!await UsgsAvailable()) return; @@ -1431,7 +1438,7 @@ await TimeSeriesDownload.FromUSGS(USGS_1, /// /// Tests USGS field measurement discharge data retrieval from the OGC API. /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration")] public async Task USGS_MeasuredDischarge_Works() { if (!await UsgsAvailable()) return; @@ -1454,7 +1461,7 @@ public async Task USGS_MeasuredDischarge_Works() /// /// Tests USGS field measurement stage (gage height) data retrieval from the OGC API. /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration")] public async Task USGS_MeasuredStage_Works() { if (!await UsgsAvailable()) return; @@ -1477,7 +1484,7 @@ public async Task USGS_MeasuredStage_Works() /// /// Tests USGS peak stage data retrieval. /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration")] public async Task USGS_PeakStage_Works() { if (!await UsgsAvailable()) return; @@ -1493,7 +1500,7 @@ public async Task USGS_PeakStage_Works() /// /// Tests USGS instantaneous discharge download. /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration")] public async Task USGS_InstantaneousDischarge_Works() { if (!await UsgsAvailable()) return; @@ -1508,7 +1515,7 @@ public async Task USGS_InstantaneousDischarge_Works() /// /// Tests USGS instantaneous stage download. /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration")] public async Task USGS_InstantaneousStage_Works() { if (!await UsgsAvailable()) return; @@ -1652,7 +1659,7 @@ await TimeSeriesDownload.FromGHCN( /// The ceiling guards against connection-establishment regressions: walking dead IPv6 /// addresses before IPv4 once made this download take minutes instead of seconds. /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration")] [Timeout(60000, CooperativeCancellation = true)] public async Task GHCN_FullPor_Precipitation() { @@ -1668,7 +1675,7 @@ public async Task GHCN_FullPor_Precipitation() /// The ceiling guards against connection-establishment regressions: walking dead IPv6 /// addresses before IPv4 once made this download take minutes instead of seconds. /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration")] [Timeout(60000, CooperativeCancellation = true)] public async Task GHCN_FullPor_Snow() { @@ -1681,7 +1688,7 @@ public async Task GHCN_FullPor_Snow() /// /// Tests precipitation unit conversion between millimeters and inches. /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration")] public async Task GHCN_UnitConversion_Mm_In() { if (!await GhcnAvailable()) return; @@ -1705,7 +1712,7 @@ public async Task GHCN_UnitConversion_Mm_In() /// /// Tests precipitation unit conversion between millimeters and centimeters. /// - [TestMethod, TestCategory("Integration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration")] public async Task GHCN_UnitConversion_Mm_Cm() { if (!await GhcnAvailable()) return; @@ -1756,7 +1763,7 @@ await TimeSeriesDownload.FromGHCN(GHCN_1, /// /// Validates a full-period-of-record download for the BOM Cotter River station (discharge). /// - [TestMethod, TestCategory("Integration"), TestCategory("BOMIntegration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration"), TestCategory("BOMIntegration")] public async Task BOM_FullPor_CotterRiver_Discharge() { if (!await BomAvailable()) return; @@ -1767,7 +1774,7 @@ public async Task BOM_FullPor_CotterRiver_Discharge() /// /// Validates a full-period-of-record download for the BOM Goodradigbee River station (discharge). /// - [TestMethod, TestCategory("Integration"), TestCategory("BOMIntegration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration"), TestCategory("BOMIntegration")] public async Task BOM_FullPor_Goodradigbee_Discharge() { if (!await BomAvailable()) return; @@ -1778,7 +1785,7 @@ public async Task BOM_FullPor_Goodradigbee_Discharge() /// /// Validates a full-period-of-record download for the BOM Murray River station (stage). /// - [TestMethod, TestCategory("Integration"), TestCategory("BOMIntegration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration"), TestCategory("BOMIntegration")] public async Task BOM_FullPor_MurrayRiver_Stage() { if (!await BomAvailable()) return; @@ -1789,7 +1796,7 @@ public async Task BOM_FullPor_MurrayRiver_Stage() /// /// Tests discharge unit conversions (cms ↔ cfs) for BOM data. /// - [TestMethod, TestCategory("Integration"), TestCategory("BOMIntegration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration"), TestCategory("BOMIntegration")] public async Task BOM_UnitConversion_Discharge_CmsCfs() { if (!await BomAvailable()) return; @@ -1815,7 +1822,7 @@ public async Task BOM_UnitConversion_Discharge_CmsCfs() /// /// Tests stage unit conversions (m ↔ ft) for BOM data. /// - [TestMethod, TestCategory("Integration"), TestCategory("BOMIntegration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration"), TestCategory("BOMIntegration")] public async Task BOM_UnitConversion_Stage_MFt() { if (!await BomAvailable()) return; @@ -1862,7 +1869,7 @@ await TimeSeriesDownload.FromABOM(BOM_1, /// /// Tests BOM with a windowed date range to verify date filtering works correctly. /// - [TestMethod, TestCategory("Integration"), TestCategory("BOMIntegration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration"), TestCategory("BOMIntegration")] public async Task BOM_WindowedDownload_Works() { if (!await BomAvailable()) return; @@ -1887,7 +1894,7 @@ public async Task BOM_WindowedDownload_Works() /// /// Validates instantaneous discharge download from BOM. /// - [TestMethod, TestCategory("Integration"), TestCategory("BOMIntegration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration"), TestCategory("BOMIntegration")] public async Task BOM_InstantaneousDischarge_Works() { if (!await BomAvailable()) return; @@ -1901,7 +1908,7 @@ public async Task BOM_InstantaneousDischarge_Works() /// /// Validates instantaneous stage download from BOM. /// - [TestMethod, TestCategory("Integration"), TestCategory("BOMIntegration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration"), TestCategory("BOMIntegration")] public async Task BOM_InstantaneousStage_Works() { if (!await BomAvailable()) return; @@ -1915,7 +1922,7 @@ public async Task BOM_InstantaneousStage_Works() /// /// Validates daily precipitation download from BOM. /// - [TestMethod, TestCategory("Integration"), TestCategory("BOMIntegration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration"), TestCategory("BOMIntegration")] public async Task BOM_DailyPrecipitation_Works() { if (!await BomAvailable()) return; @@ -1929,7 +1936,7 @@ public async Task BOM_DailyPrecipitation_Works() /// /// Tests precipitation unit conversions (mm ↔ inches) for BOM data. /// - [TestMethod, TestCategory("Integration"), TestCategory("BOMIntegration")] + [TestMethod, TestCategory("Integration"), TestCategory("LiveServiceIntegration"), TestCategory("BOMIntegration")] public async Task BOM_UnitConversion_Precip_MmIn() { if (!await BomAvailable()) return; diff --git a/Test_Numerics/Utilities/Test_SafeProgressReporter.cs b/Test_Numerics/Utilities/Test_SafeProgressReporter.cs index 56360cfd..c95759d4 100644 --- a/Test_Numerics/Utilities/Test_SafeProgressReporter.cs +++ b/Test_Numerics/Utilities/Test_SafeProgressReporter.cs @@ -1,6 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Numerics.Utilities; using System.Reflection; +using System.Runtime.ExceptionServices; using System.Threading; using System.Threading.Tasks; @@ -77,6 +78,7 @@ public void Test_ChildReporters_SnapshotDuringRegistration() /// cancellation source rather than the source that was current before registration. /// [TestMethod] + [DoNotParallelize] public void Test_CreateProgressModifier_ResetHandoffIsAtomic() { var parent = new SafeProgressReporter("parent"); @@ -87,29 +89,86 @@ public void Test_CreateProgressModifier_ResetHandoffIsAtomic() object registryLock = lockField.GetValue(parent)!; SafeProgressReporter child = null!; - var registrationThread = new Thread(() => child = parent.CreateProgressModifier(1f, "child")); - bool registrationBlocked; + using var registrationWaiting = new ManualResetEventSlim(); + var registrationContext = new WaitNotifyingSynchronizationContext(registrationWaiting); + ExceptionDispatchInfo registrationException = null!; + var registrationThread = new Thread(() => + { + var previousContext = SynchronizationContext.Current; + try + { + SynchronizationContext.SetSynchronizationContext(registrationContext); + child = parent.CreateProgressModifier(1f, "child"); + } + catch (Exception ex) + { + registrationException = ExceptionDispatchInfo.Capture(ex); + } + finally + { + SynchronizationContext.SetSynchronizationContext(previousContext); + } + }) { IsBackground = true }; + bool registrationStarted = false; + bool registrationBlocked = false; + bool registrationCompleted = false; Monitor.Enter(registryLock); try { registrationThread.Start(); - registrationBlocked = SpinWait.SpinUntil( - () => (registrationThread.ThreadState & ThreadState.WaitSleepJoin) != 0, - 5000); + registrationStarted = true; + // The worker performs no other waits after installing the context, so this + // notification comes from Monitor.Enter inside CreateProgressModifier. + registrationBlocked = registrationWaiting.Wait(5000); if (registrationBlocked) sourceField.SetValue(parent, new CancellationTokenSource()); } finally { Monitor.Exit(registryLock); + if (registrationStarted) + registrationCompleted = registrationThread.Join(5000); } + Assert.IsTrue(registrationCompleted, "Child registration did not complete."); + registrationException?.Throw(); Assert.IsTrue(registrationBlocked, "Child registration did not reach the registry lock."); - Assert.IsTrue(registrationThread.Join(5000), "Child registration did not complete."); parent.RequestCancel(); Assert.IsTrue(child.IsCancelRequested, "The child retained the cancellation source from before the reset handoff."); } + + /// + /// Signals when the registration thread enters a blocking wait, then preserves the + /// normal wait behavior so the test can replace the source while holding the registry lock. + /// + private sealed class WaitNotifyingSynchronizationContext : SynchronizationContext + { + private readonly ManualResetEventSlim _waiting; + + /// + /// Creates a context that notifies the test when a blocking wait begins. + /// + /// The event to signal when the worker begins waiting. + public WaitNotifyingSynchronizationContext(ManualResetEventSlim waiting) + { + _waiting = waiting; + SetWaitNotificationRequired(); + } + + /// + /// Signals the blocking wait and delegates to the default synchronization context. + /// + /// The native handles to wait on. + /// Whether all handles must be signaled. + /// The maximum wait duration in milliseconds. + /// The result of the default synchronization-context wait. + public override int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout) + { + _waiting.Set(); + return base.Wait(waitHandles, waitAll, millisecondsTimeout); + } + } } }